I have been trying to convert a char array charray containing a number with 5 decimal places, ex; 1234.6789 and atof() does not return the exact value. It returns 1234.56787.

Anyone know why?
Is there an alternative? I could always eliminate the decimal point, make it a long and then divide by 100,000 but not sure that would be any better.

Simple routine;
char Msg[100] = {“null”}; //series of 5 decimal place numbers separated by commas or spaces

float GetFloatfromChArray(int Start, int End) { //get substring from char array and convert to float, 1234.56789
char chVal [10] = {“null”}; int n=0;
for(int i = Start; i < End; i++)
{ chVal[n++] = Msg*; } //get substring out of Msg*

return atof(chVal); // does not accurately convert 5 decimal place numbers! returns 1234.56787 instead of 1234.56789* }

Arduino floats, unless you’re using on the of the 32-bit boards, are 32-bit, which means they can represent no more than 6.5 significant digits, which is exactly what you’re seeing. On some processors you could use 64-bit doubles instead of floats, but on AVR processors, double and float are identical. And, floats of any kind, can only represent approximations of most numbers, not exact values. That is the nature of all binary representations of floating point numbers.

Because of the error, I am instead converting to long (by not including the decimal point) using atol() and using the numbers that way. Since I am only looking for differences from one run to the next, using longs will work. Would have preferred using float numbers but need accuracy to the 5th decimal place. An error of .00002 is significant in my project.

markvincenzo:
I have been trying to convert a char array charray containing a number with 5 decimal places, ex; 1234.6789 and atof() does not return the exact value. It returns 1234.56787.

If you enter 123.456789 from the InputBox of the Serial Monitor, then the atof() function (so applied on the received charcaters) always gives 123.45678 (5-digit accuracy).

You may try the following codes:

char myFloat[10] = "";
int i = 0; //index pointer for myFloat[] array
void setup()
{
Serial.begin(9600);
}
void loop()
{
byte n = Serial.available(); //checking how many charcaters are in BUFFer
if (n != 0 ) //checking if BUFFer really holds a charcater
{
char x = Serial.read();
if (x != '\n') //this is not Newline character
{
myFloat[i] = x; //save ASCII code of charcater in array
i++; //advanve array index
}
else
{
float y = atof(myFloat); //ASCII formated float number becomes numerical float
Serial.println(y, 6);
memset(myFloat, 0, 10); //myFloat[] array is reset to 0s.
i = 0; //index pointer is reset
}
}
}

It is best to keep the number as an integer, and keep track of the decimal places yourself, if necessary. Some decimal floating point numbers have no exact conversion to a binary floating point number, similar to how there is no exact decimal equivalent to 1/3.

I tried the code and for an input of 1234.56789, the output is 1234.56787.

for the following code;
Serial.print (atof("1234.56789"), 5); Serial.println();
The result is 1234.56787 not 1234.56789

for the following code;
char FloatTest[10] = {"1234.56789"};
Serial.print (atof(FloatTest), 5); Serial.println();
The result is 1234.56787 not 1234.56789

for the following code;
char FloatTest[10] = {"9876.98765"};
Serial.print (atof(FloatTest), 5); Serial.println();
Serial.print (atof("9876.98765"), 5); Serial.println();
The result is 9876.98730
The result is 9876.98730

Bottom line is that if you need precision conversion to 5 or more decimal places, atof() will not work.
I am using longs instead and conversion from charray[] to long works perfectly.

I appreciate the replies as never going to stop learning about the Arduino.

markvincenzo:
Bottom line is that if you need precision conversion to 5 or more decimal places, atof() will not work.

NOT "5 or more decimal places". 5 or more SIGNIFICANT DIGITS. 12345.0, 0.12345, 123.45 are all five significant digits. And, again, a 32-bit float is good for ~6.5 significant digits, not 5.

markvincenzo:
Not sure what you mean but convention is digits to the right of the decimal place are considered "decimal places". decimal place - Wiktionary

for the following, atof() is actually accurate to 8 significant digits but only 3 decimal places;
atof("12345.1234") = 12345.12304 12345.123046

Interesting and valuable information to know if looking for high precision conversion.

No, that is not correct. First, atof does not care how many digits are in the string it is parsing, nor how many digits there are to the left or the right of the decimal point. Second, it converts the string value to a double, not a float, and the number of digits that can be represented is determined by the format of a double on the target system. As stated earlier, on AVR Arduinos, float and double are BOTH 32-bit floating point values, and BOTH are limited to ~6.5 significant digits, regardless of whether those digits are all to the left of the decimal point, all to the right of the decimal point, or anywhere in-between. But, again, what matters it significant digits, NOT the position of the decimal point. On ARM, ESP and other 32-bit platforms, atof will return a 64-bt double, which can have up to ~16 significant digits.

It seems to matter how many digits;
1234.56789 = 1234.56787 difference of .00002
12341234.56789 = 12341235.00000 difference of .56789
123412341234.56789 = ovf
1.23456789 = 1.23456788 difference of .00000001

Very interesting and informative discussion. Lot to learn about the Arduino and the coding.

Put another way, if you have 24 bits of mantissa, the difference between any two consecutive values (xxx..xxx0 and xxx..xxx1) is 1/2^{24} ~= 0.00000006 (times whatever exponent is in use.)
So for your original example of 1234.6789 (probably a typo, BTW), there's an an exponent of ~10 (2^{10}), so a single bit difference in the mantissa is about 0.000061, which is pretty consistent with the values you see.

markvincenzo:
for the following code;
char FloatTest[10] = {"9876.98765"};
Serial.print (atof(FloatTest), 5); Serial.println();
Serial.print (atof("9876.98765"), 5); Serial.println();
The result is 9876.98730
The result is 9876.98730

Now, we know that the function atof() does not work robustly for all kinds of data; therefore, let us don't send our float numbers in ASCII format rather send them in binary32 format.

GolamMostafa:
Now, we know that the function atof() does not work robustly for all kinds of data; therefore, let us don't send our float numbers in ASCII format rather send them in binary32 format.

That is absolute nonsense! atof() works EXACTLY as it is supposed to, and it accurately converts the string to float as well as the float format allows. The limitation is the resolution of the float format available to it, NOT an imaginary problem in atof()!

RayLivingston:
That is absolute nonsense! atof() works EXACTLY as it is supposed to, and it accurately converts the string to float as well as the float format allows. The limitation is the resolution of the float format available to it, NOT an imaginary problem in atof()!

I had been always using atof() function with great pleasure; where, my accuracy was only 2-digit after the decimal point.

Only in this thread, I have come to see that the atof() function does not give 5-digit accuracy for all kinds of data.

According to IEEE-754/binary32 standards, the scheme would always keep 5-digit accuracy.

The atof() function fails to maintain wat the IEEE-754 claims.

So, what should be the choice for OP if he wishes to maintain 5-digit accuracy - atof() or binary32?