开发者

How do printf and scanf handle floating point precision formats?

开发者 https://www.devze.com 2023-01-17 17:19 出处:网络
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);

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 element开发者_开发问答s 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?


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.


scanf will round the input value to the nearest exactly representable floating point value. As DarkDust's answer illustrates, in single precision the closest exactly representable value is below the exact value, and in double precision the closest exactly representable value is above the exact value.

0

精彩评论

暂无评论...
验证码 换一张
取 消