Speed of math with fractions. With or without floats.

Hi all,

So for a project i need de divide the ADC output with a constant.

  • The ADC output range is 0 to 475
  • The output variable needs to be 0-100

At the Arduino reference they advise against using floats as this will slow down the Arduino. The thing is. In the example below the 4,75 is a float if i'm not mistaking. When i measure the time it takes the output is always 4uS.

int awnser = 0;
int ADCval = 238;
unsigned long time1 = 0;
unsigned long time2 = 0;

void setup() {
 Serial.begin(115200);              // Start serial comminucation
}

void loop() {
time1 = micros();
awnser = ADCval / 4.75 ; 
time2 = micros();
Serial.print ("the awnser is: "); 
Serial.println(awnser);
Serial.print ("time passed: "); 
Serial.println(time2 - time1);
delay (2000); 
}

Even if i run the math 50 times, the time taken remains 4uS, (less than the resolution of the micros function. Therefore i see no problem in using the float. Am i doing something wrong here? Or is the speed of the math that fast?

Anyway, i can prevent using float doing the following:

unsigned int Factor = 475;
awnser = ADCval * 100 / Factor;

I have to use a unsigned integer to prevent overflow it the ADC*100 value. This works fine as if seems to force the math into using unsigned integers instead of integers.

The thing is, I have no way to tell if there is any difference in the time it takes compared to using floats because both seem to work very fast.

Am i doing something wrong here, or is there really no issue using floats?

Floating point math is much slower than integer, especially if your CPU doesn't support it in hardware. But the Arduino is running at 16MHz, which is quick enough that you're unlikely to notice the impact of a bit of floating point math in a simple case like this.

If your code is working fast enough for your purposes, don't worry about it.

wildbill:
Floating point math is much slower than integer, especially if your CPU doesn't support it in hardware. But the Arduino is running at 16MHz, which is quick enough that you're unlikely to notice the impact of a bit of floating point math in a simple case like this.

If your code is working fast enough for your purposes, don't worry about it.

Thanks! you'll understand as a engineer i would love to know how much difference there is in speed. Not exactly, but in general. It seems that both ways are done MUCH faster than i can even measure.

Surely there must be a reason why they advise against using floats. I can read its supposed to be slower but if i cant measure the difference, why mention it at all?

I just want to understand :slight_smile:

Even if i run the math 50 times, the time taken remains 4uS,

The compiler already optimised the CONSTANT arithmetic.

It is much slower. Try writing something that does tens of thousands of calculations and time it with micros and compare with a similar integer calc. Be careful that it accumulates the values as it goes along or the compiler may be able to optimize the calculation away entirely.

Compared to modern CPUs, the common Arduino chip is slow as molasses, so people will justifiably warn you that floating point operations are expensive.

However, the Arduino is eight times faster than the microprocessors I used to code business applications on in my first job and we were using floats even then. So yes, float is expensive to do, especially if you're doing a lot of it, ray tracing perhaps, but it's still doable for simpler applications. But if you're reading the ADC, which is itself quite slow, the cost of doing a floating point operation as well probably doesn't matter.

TheMemberFormerlyKnownAsAWOL:
The compiler already optimised the CONSTANT arithmetic.

Does that mean the compiler only runs the line once because the outcome is always the same?

wildbill:
It is much slower. Try writing something that does tens of thousands of calculations and time it with micros and compare with a similar integer calc. Be careful that it accumulates the values as it goes along or the compiler may be able to optimize the calculation away entirely.

Compared to modern CPUs, the common Arduino chip is slow as molasses, so people will justifiably warn you that floating point operations are expensive.

However, the Arduino is eight times faster than the microprocessors I used to code business applications on in my first job and we were using floats even then. So yes, float is expensive to do, especially if you're doing a lot of it, ray tracing perhaps, but it's still doable for simpler applications. But if you're reading the ADC, which is itself quite slow, the cost of doing a floating point operation as well probably doesn't matter.

So i guess avoiding floats in fractions is a general idea, but in the case of a typical ardruino program its no problem most of the time. I can think of little situations where i need to do a lot of math and can not spare a few uS.

Thanks you all for clarifying. I really thought i was doing something wrong.

superkris:
Does that mean the compiler only runs the line once because the outcome is always the same?

It means the compiler calculated the expression once because it is a constant.

Does that mean the compiler only runs the line once because the outcome is always the same?

No, the compiler calculates the value at COMPILE TIME. At run-time, that line is a simple assignment of a constant value to the variable. There is NO calculation at run-time.

Change this line:

int ADCval = 238;

to this:

volatile int ADCval = 238;

and will actually do the calculation on every pass.

Regards,
Ray L.

Something else to consider is that the code to process floating point arithmetic will increase the size of your code more than if you used integer arithmetic.

Incidentally, you don't need to use an unsigned int for the calculation. 100 / 475 will reduce to 4 / 19, and with a maximum value of 475 for ADCval, the numbers stay well within the range of a signed int

RayLivingston:
No, the compiler calculates the value at COMPILE TIME. At run-time, that line is a simple assignment of a constant value to the variable. There is NO calculation at run-time.

Change this line:

int ADCval = 238;

to this:

volatile int ADCval = 238;

and will actually do the calculation on every pass.

Regards,
Ray L.

Oh wow. i did not know that. i'm still a little bit of a noob when it comes to writing code. I did not realize that the compiler replaces the math with a constant (still a little new with this term).

Now i understand that the Arduino was never doing the math during my test at all. Knowing this ity makes sense that i wasn't able to measure any calculation time at all.

I will try it again later this evening with the addition of volatile. Also something that is new to me. Had a very interesting read!

david_2018:
Something else to consider is that the code to process floating point arithmetic will increase the size of your code more than if you used integer arithmetic.

Incidentally, you don't need to use an unsigned int for the calculation. 100 / 475 will reduce to 4 / 19, and with a maximum value of 475 for ADCval, the numbers stay well within the range of a signed int

I'm not sure i full understand what your saying...

The math without floats is 475 * 100 / 475. Following the rules of math it first calculates 475 * 100 = 47500, so the integer overflows at that point.

Can you please clarify?

Again, thank you all!

superkris:
The math without floats is 475 * 100 / 475. Following the rules of math it first calculates 475 * 100 = 47500, so the integer overflows at that point.

It does not overflow an unsigned int. But apparently you missed reply #8. You can multiply by only 4.

It's been quite a while since I looked, but the Arduino microprocessors can only do add, subtract and bit shifts. Any other calculation has to be done with software.

Paul

Paul_KD7HB:
It's been quite a while since I looked, but the Arduino microprocessors can only do add, subtract and bit shifts. Any other calculation has to be done with software.

Paul

No, many Arduinos, even AVR ones, have hardware multipliers.

Regards,
Ray L.

Yes, but no hardware divide... so the most efficient scaling is done by multiplying the fractional numerator by the input number, then dividing only by powers of two, so you can perform the division by bit shifting.

So i just run the first sketch again but now with ADCval as a volatile integer. The time i get back now is:

Approx 40uS for running math once.
Approx 44uS for running math 10 times.

When i run it without the float (2nd code, but with volatile integer) the measurements are :

Approx 16uS for running math once.
Approx 20uS for running math 10 times.

Keep in mind the time resolution is 4uS so these values are not very accurate

So clearly the float is taking longer, but if it makes a difference for you code, depends on te code itself.

I do think its interesting the is so little difference in doing the math once, or 10 times!

aarg:
It does not overflow an unsigned int. But apparently you missed reply #8. You can multiply by only 4.

Ah, i think i understand now...

ADCval * 100 / 475

is the same as

ADCval * 4 / 19..

You are right. Im not great at math i gues...

However, are there any disadvantages to using unsigned integers? For me the human math to calculate the "475" is much simpeler, and unsigned intergers use the same amount of memory if im not mistaking.

superkris:
I do think its interesting the is so little difference in doing the math once, or 10 times!

Declare awnser as volatile, that makes a major difference, because the answer has to be saved to memory after each calculation. If awnser is not volatile, the compiler still seems to be eliminating the repeated calculations, since it only needs the results of the final calculation.

Also, pay attention to how much program memory the sketch used with floating point vs integer arithmetic.

david_2018:
Declare awnser as volatile, that makes a major difference, because the answer has to be saved to memory after each calculation. If awnser is not volatile, the compiler still seems to be eliminating the repeated calculations, since it only needs the results of the final calculation.

Also, pay attention to how much program memory the sketch used with floating point vs integer arithmetic.

It does make a (big) difference in processing time, can you help me understand this better?

My understanding of how the compiler works is very limited. Is the compiler that "smart" that i realized my first math can be ignored because the values are being overwritten before the answer is actualy being used?

If so i understand the whole volatile thing a bit better now i think... The compiler tries to optimize the code and concludes some lines can be removed without effecting the outcome. It does this until the function is at its end, am i right? Usually the global variable i write to is not being used by any other function, because the arduino only runs 1 function at the same time. However, the exception for this are interrupts (which i also know very little about) the compiler isnt looking at what any interrupts may use the global variable for. If you using interrupts that need to acces the global variable, its better to make it a volatile?

superkris:
However, are there any disadvantages to using unsigned integers? For me the human math to calculate the "475" is much simpeler, and unsigned intergers use the same amount of memory if im not mistaking.

The unsigned integer has the disadvantage of not being able to represent negative numbers. If you wanted to use the 4/19 formula, but make it clearer in code, you could use:

#define FIXEDSCALE 25;
const int VOLTS_SCALED = 475;
const int UNITS_SCALED = 100;
...
int foo = bar * (UNITS_SCALED / FIXEDSCALE) / (VOLTS_SCALED / FIXEDSCALE);

or simply:

int foo = bar * (100 / 25) / (475 / 25);

However, the exception for this are interrupts (which i also know very little about) the compiler isnt looking at what any interrupts may use the global variable for. If you using interrupts that need to acces the global variable, its better to make it a volatile?

It's not better, it's mandatory. But it's far from the only precaution you need to take with interrupts.

superkris:
My understanding of how the compiler works is very limited. Is the compiler that "smart" that i realized my first math can be ignored because the values are being overwritten before the answer is actualy being used?

Optimizing compilers are much smarter than you think they are, that's for sure... Typical compilers have
many techniques for optimization developed over 7 decades - what do you imagine computer scientists
have been doing all these years?

Start here if you want to get a feel for this: Category:Compiler optimizations - Wikipedia

So to follow up on everything that was discussed in this thread, you can see my findings below:

The code used

volatile int answer = 0;            // Make volatile so the code runs every time
volatile int ADCval = 475;          // Make volatile so the code runs every time
unsigned long time1 = 0;            // time stamp 1 (in uS)
unsigned long time2 = 0;            // time stamp 1 (in uS)

void setup() {
 Serial.begin(115200);              // Start serial comminucation
}

void loop() {
  time1 = micros();                 // Set timestamp 1
  answer = ADCval / 4.75 ;          // Acual math 
  time2 = micros();                 // Set timestamp 2 
  Serial.print ("the answer is: "); // just printing text
  Serial.println(answer);           // print answer value
  Serial.print ("time passed: ");   // just printing text
  Serial.println(time2 - time1);    // Calculate the time past and print it
  delay (2000);                     // Delay, befor running code again
}

So this is the code with using floats. It takes approx 40uS to run the math.
When i run the math line 10 times, it takes 380uS

No lets try without a float:

  unsigned int Factor = 475;        // forcing a unsigned integer to prevent it form overflowing
  answer = ADCval * 100 / Factor;   // The actual math

It takes approx 16uS to run the math.
To run the math line 10 times, it takes 140uS

Now lets try the user optimized math without using floats or unsigned integer

  answer = ADCval * 4 / 19 ;        // The actual math

Strangely enough this is taking 20uS. Keep in mind that this is just 1 stap in the resolution of micros. The actual diffrence may be much smaller than 4uS
Running the math line 10 times, its taking approx 148 so still a little longer than the non optimized math.

Another easy way to write this can be:

  time1 = micros();                         // Set timestamp 1
  answer = ADCval * (100/25) / (475/19);    // The actual math
  time2 = micros();                         // Set timestamp 2

This is also using approx 16uS to run once, and 148uS to run 10 times. This makes sense because the compiler already calculates the (100/25) and (475/19) so the arduino doesn't has to.

For the sketch with floats the compiler calculates the memory usage at 230 bytes. 2694 bytes of storage.
Without floats this is 230 bytes, and 2416 bytes of storage.

I can not see the local variables this way off course. This may be larger using the floats.

My final conclusion:
Yes, doing fractions with floats takes much longer. In the example shown here its approx 2,5x faster without using floats.

However, for simple math like this the arduino is likely to be fast enough in both ways.

I guess i still try to avoid using floats, but when its a simple fix, there is nothing wrong using this option.