Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do printf and scanf handle floating point precision formats?

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?

like image 973
ultimate cause Avatar asked Sep 24 '10 18:09

ultimate cause


People also ask

How do I specify precision in printf?

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.

How precise is a float in C?

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.


2 Answers

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

like image 169
DarkDust Avatar answered Sep 19 '22 19:09

DarkDust


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.

like image 38
R.. GitHub STOP HELPING ICE Avatar answered Sep 20 '22 19:09

R.. GitHub STOP HELPING ICE