Strange Float or Rounding error

void setup() {
  Serial.begin(9600);

  PrintFloat(1234.2);
  PrintFloat(1234.213);
  PrintFloat(1234.213567);
}

void loop() {
}

void PrintFloat(float i) {
  Serial.println(i, 5);
}

And this is the output:
1234.19995
1234.21301
1234.21362

Looks OK to me. Arduino floats have only about 7-8 digits of precision. And while in base 10, all of those numbers are nice clean terminating sequences, that might not be the case in binary.

What is the real problem?

floating point cannot represent a specific number exactly like an integer. It gets as close as it can, but there is some rounding error. floats have only 6-7 decimal digits of precision. Try a "double" instead of a float. It has.. you guessed it.. DOUBLE the precision. That will get you a little closer.

But then you'll find that a double is exactly the same as a float on an eight bit Arduino.

I need to extract the exact value of the decimal part of the data that is passed into the parameter 'i'.

Define "exact"

bDeters:
floating point cannot represent a specific number exactly like an integer. It gets as close as it can, but there is some rounding error. floats have only 6-7 decimal digits of precision. Try a "double" instead of a float. It has.. you guessed it.. DOUBLE the precision. That will get you a little closer.

There is so much wrong in this, that I suggest you study up on floats in the arduino a little bit more.

It most certainly can represent certain values exactly, though once you start manipulating them, you might run into issues.

what's your range? You could just use a 32-bit integer and insert the decimal accordingly. For example:
Range of unsigned long
0 to 4,294,967,295

"virtual divide" by 1,000,000 gives you:

0.000000 to 4,294.967295

I am developing a application that will speak a number. I have it working for 0 - 999,999 but I need to get the decimal part exact, not just close for this purpose. Otherwise it speaks different figures (OK, close but not exact!). I don’t want 4.2 spoken as “Four point one nine nine nine five one”.

Your response does not negate bDeters' question...

bDeters:
what's your range?

I'd settle for two decimal places. (0.01 - 999,999.99)

bDeters provided you with a reasonable solution. Store the value in an unsigned long. Pretend there is a decimal between the second and third base 10 digits. (Such a thing is commonly called "fixed point".)

I have modified to this given the two decimal places criteria and it will suffice:

void setup() {
  Serial.begin(9600);

  PrintFloat(1234.6);
  PrintFloat(1234.32);
  PrintFloat(1234.571);
}

void loop() {
}

void PrintFloat(double i) {
  Serial.println(i, 2);
}

Output:
1234.60
1234.32
1234.57

Watch out there, try to print 999999.99 and you're beyond the ~7 digit precision. That's what I meant by "what is your range"

For example:

PrintFloat(999999.99);
  PrintFloat(999999.98);
  PrintFloat(999999.97);
  PrintFloat(999999.96);
  PrintFloat(999999.95);
  PrintFloat(999999.94);
  PrintFloat(999999.93);
  PrintFloat(999999.92);
  PrintFloat(999999.91);
  PrintFloat(999999.90);

Outputs:

1000000.00
1000000.00
1000000.00
999999.93
999999.93
999999.93
999999.93
999999.93
999999.93
999999.87

This might help your understanding: Floating Point Precision and Binary 32 or, Arduino Don’t Know Math

This question is one I used to deal with at least once a month in a different forum years ago. It is a perpetual question, and is based on the premise that binary floating point numbers accurately represent decimal numbers. I first met this problem about 50 years ago, and for people encountering floating point for the first time, it always comes as a surprise.

This is how it is: Decimal numbers are base 10, and for all practical purposes have infinite precision in the domain of real numbers. The reason you can’t represent a decimal value accurately in floating point is the same reason you can’t store the result of 200+200 in a char or byte variable. Computers have a fixed precision, and anything beyond that is lost.

Floating point has two problems: (1) you cannot represent base 10 numbers accurately and (2) errors during computation will accumulate and give you slowly-growing errors. You can say, “Well, yes, on every iteration it can lose a bit of precision” but think of what happens to floating-point computations during multi-year missions to the outer planets. Floating-point precision is something that only floating-point experts should play with. The rest of us have to hope that we will not become victims. This means not doing repetitive computations until we (re)learn the idiosyncrasies of the floating-point computations on our platform.

Some years ago, IEEE developed a standard, IEEE-744, that covers every detail of floating-point computation. Nearly all modern computers must use IEEE-744-compliant FPUs or simulation software. There are some very rare and exotic exceptions to this, which become the nightmare of their programmers.

If you are running on a Windows platform, you can download my Floating Point Explorer from
http://www.flounder.com/floating_point_explorer.htm

It is open-source but the binary will run on most Windows machines. You can compile the C++ code under Visual Studio if you don’t trust my binary.

To get the effect you want, you need to first round the value to two decimal digits of precision, then convert it to a string. This will generally give you the right answer.

Note that if you have two floats, A and B, that supposedly arrive at the same number via two different computations and this means one thing, and if they get different numbers, this means another thing: you can NEVER ask about floating point equality!

float A;
float B;
A = ...some computation...;
B = ...another computation...;
if(A==B)
   {
     ...do something
   }

because it is extremely likely that A will NEVER equal B. This is because they might differ in the least significant bit of the mantissa. Or maybe even in the lowest N bits of the mantissa, because of the nature of finite-precision floating-point representations. Instead, you must write an “equality” test as

if(fabs(A - B) < MYEPSILON)

where MYEPSILON is a constant you define as the smallest error you are willing to accept, e.g.

const float MYEPSILON = 0.000001;

Also note that although the keyword ‘double’ suggests that it will have more precision than ‘float’, the C/C++ Standards do not require this; it is a decision that is made by the compiler writer. In the case of the Arduino, sizeof(double) == sizeof(float), and both are 4, that is, a 32-bit single-precision floating-point number. So saying ‘double’ in Arduino code does not give you more precision than ‘float’.

flounder:
Some years ago, IEEE developed a standard, IEEE-744, that covers every detail of floating-point computation.

IEEE-754...
Regards,
Ray L.

flounder:
Floating point has two problems: (1) you cannot represent base 10 numbers accurately and

It doesn't represent SOME base 10 numbers well. It does just fine with 1, 2, 3, 4, 5 etc. But 1/10 is an infinitely repeating decimal in base 2. Kind of like 1/3 is in base 10.

RayLivingston:
IEEE-754...
Regards,
Ray L.

Well, the discussion is about the accuracy of the representation of certain decimal numbers. :smiley:

In the case of the Arduino, sizeof(double) == sizeof(float), and both are 4, that is, a 32-bit single-precision floating-point number. So saying 'double' in Arduino code does not give you more precision than 'float'.

This is not true for all Arduinos - the Due has 64 bit doubles, for example.