1

I have a Olimex ESP32-PoE board which has a ESP-WROOM-32.

I just found out a strange behaviour for printing floating point numbers on the board.

Sketch

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  float a=34567891.234;
  float b=2.3456;
  a++;
  Serial.print(a+b, 4);
  Serial.println("");
  delay(1000);
}

the sketch is extremely simple where I want to add two floating point numbers, one a big and another small to print their addition result upto 4 places.

Output

on the serial console in the Arduino IDE

34567896.0000
34567896.0000
34567896.0000
34567896.0000
34567896.0000
34567896.0000
34567896.0000
34567896.0000
34567896.0000
34567896.0000
34567896.0000
34567896.0000
34567896.0000

Everytime I increment the float a using a++ i expect it to increment by one and print the result but the output above is completely off.

Without Increment

void loop() {
  // put your main code here, to run repeatedly:
  float a=34567891.234;
  float b=2.3456;
  Serial.print(a+b, 4);
  Serial.println("");
  delay(1000);

}

output

34567896.0000
34567896.0000
34567896.0000

Same output.

Is this Board specific or is the Serial.print not supporting float?

  • 1
    Try doubles. 34567891.234 needs 35 bits of precision to keep the decimals, but floats only have 23. – Dave X Oct 3 '18 at 16:50
  • There is a similar question, but that confuses me even more: stackoverflow.com/questions/8561393/… – Jot Oct 3 '18 at 17:01
  • The thing that confuses me, is that even if the compiler decides to round up both numbers (I remember I had this somewhere, that if you do not specify that the sum is also float, it acts as if they are integers), it would still be 34567895.0000, not 34567896.0000. So I am also at a loss here – Atizs Oct 4 '18 at 6:26
3

As already stated in comments and Dave X's answer, floats have a limited resolution. That resolution is given by the constant FLT_EPSILON, which is the difference between 1 and the smallest float larger than 1. On any system conformant to IEEE 754, FLT_EPSILON is 2−23, or about 1.19e-7. The implication is that, within the interval [1, 2], the real numbers that can be exactly represented by floats are the multiples of FLT_EPSILON. The resolution, however, degrades as you consider larger numbers. Within the interval [2, 4] it is twice as coarse. Within [4, 8], it's four times as coarse and so on:

interval     resolution
------------------------------------------
1    – 2     2^-23 ≈ 1.19e-7 (FLT_EPSILON)
2    – 4     2^-22
...
2^23 – 2^24  1
2^24 – 2^25  2
2^25 – 2^26  4
...

Your number a is just slightly larger than 225. Within this range, the numerical resolution (aka “ulp”: unit in the last place) is 4 (last line in the table above). Thus floats in this range can only represent exact multiples of 4. When you write

double a=34567891.234;

the compiler replaces this by the closest float, which happens to be 34567892. Same thing for b, but in this case the rounding error is tiny and inconsequential (about -1.1e-7). When you write

a++;

you are replacing a with the float that is closest to a+1, which happens to be a itself. So the instruction has no effect. When yo do

Serial.print(a+b, 4);

you are computing the float closest to 34567892+2.3456, which is 34567896.

A double instead has a resolution DBL_EPSILON which is 2−52, or about 2.22e-16. This gives you some precision margin, but be aware that AVR-based Arduinos do not really support doubles and treat the double keyword as a synonym for float. This may only be an issue if you ever have to move your code to a more traditional Arduino.

  • I didn't know about the AVR double-float synonym. thanks. – Dave X Oct 5 '18 at 8:09
3

Try doubles instead of floats.

Your initial value, 34567891.234 needs 35 bits of precision to keep track of the decimals, but floats only have 24 bits pf precision. So to have enough precision to keep track of the difference between 34567891.234 and 34567891.234+1, you need higher precision storage.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  double a=34567891.234;
  double b=2.3456;
  a++;
  Serial.print(a+b, 4);
  Serial.println("");
  delay(1000);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.