Consider the following snippet of code:
float val1 = 214.20;
double val2 = 214.20;
printf("float : %f, %4.6f, %4.2f \n", val1, val1, val1);
printf("double: %f, %4.6f, %4.2f \n", val2, val2, val2);
Which outputs:
float : 214.199997, 214.199997, 214.20 | <- the correct value I wanted
double: 214.200000, 214.200000, 214.20 |
I understand that 214.20
has an infinite binary representation. The first two elements of the first line have an approximation of the intended value, but the the last one seems to have no approximation at all, and this led me to the following question:
How do the scanf
, fscanf
, printf
, fprintf
(etc.) functions treat the precision formats?
With no precision provided, printf
printed out an approximated value, but with %4.2f
it gave the correct result. Can you explain me the algorithm used by these functions to handle precision?
Either the width or the precision (or both) can be specified as *, which indicates that the next int argument from the argument list should be used as the field width or precision. For example, printf("%. *f", 2, 76.54321) prints 76.54. The flags are a few optional characters which modify the conversion in some way.
float is a 32-bit IEEE 754 single precision Floating Point Number – 1 bit for the sign, 8 bits for the exponent, and 23* for the value. float has 7 decimal digits of precision.
The thing is, 214.20 cannot be expressed exactly with binary representation. Few decimal numbers can. So an approximation is stored. Now when you use printf, the binary representation is turned into a decimal representation, but it again cannot be expressed exactly and is only approximated.
As you noticed, you can give a precision to printf to tell it how to round the decimal approximation. And if you don't give it a precision then a precision of 6 is assumed (see the man page for details).
If you use %.40f
for the float and %.40lf
for the double in your example above, you will get these results:
214.1999969482421875000000000000000000000000
214.1999999999999886313162278383970260620117
They are different because with double, there are more bits to better approximate 214.20. But as you can see, they are still very odd when represented in decimal.
I recommend to read the Wikipedia article on floating point numbers for more insights about how floating point numbers work. An excellent read is also What Every Computer Scientist Should Know About Floating-Point Arithmetic
Since you asked about scanf
, one thing you should note is that POSIX requires printf
and a subsequent scanf
(or strtod
) to reconstruct the original value exactly as long as sufficiently significant digits (at least DECIMAL_DIG
, I believe) were printed. Plain C of course makes no such requirement; the C standard pretty much allows floating point operations to give whatever result the implementor likes as long as they document it. However, if your intent is to store floating point numbers in text files to be read back later, you might be better off using the C99 %a
specifier to print them in hex. This way they'll be exact and there's no confusion about whether the serialize/deserialize process loses precision.
Keep in mind thatr when I said "reconstruct the original value", I mean the actual value which was held in the variable/expression passed to printf
, not the original decimal you wrote in the source file which got rounded to the best binary representation by the compiler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With