views:

57

answers:

3

Below is an excerpt from a program -

float val=214.20;
double val1=214.20;

printf("\n\n\nfloat :: %f,  %4.6f, %4.2f \n ",val,val,val);
printf("double:: %f,  %4.6f, %4.2f \n ",val1,val1,val1);

And the output is -

float :: 214.199997,  214.199997, 214.20<--- is the correct value as we wanted 
double:: 214.200000,  214.200000, 214.20

I understand that 214.20 has infinite binary representation. The first two elements of first line has approximation of the intended value, but the last value has absolutely same data. This caused a question in my mind -

How the scanf/fscanf/sprintf functions treat the precision formats ??

With no precision provided it printed an approximate value, but with %4.2f it gave correct result. Can some friend may explain me the algorithm used by scanf when handling precisions.

Thanks, Ripunjay

+2  A: 

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

DarkDust
Thanks for answering, though my question still is at same place. In the first place itself the float could NOT contain the exact value of 214.20, then how could it regain it ONLY with %4.2f precision, and NOT with %4.6f. What did it do to regain the value. I understand that approximation is done, but WHAT WAS THE BIT PATTERN USED THAT CAUSED INTERNALLY STORED 214.199997 TO BECOME 214.20 AND HOW DID WE ARRIVED AT THAT?
RIPUNJAY TRIPATHI
Not sure I get you, but that's just the [rounding](http://en.wikipedia.org/wiki/Rounding). It's pure chance that `%4.2f` gives you exactly the value that you wanted. It's still "wrong" since the binary representation is *not* equivalent to 214.20 but to 214.1999969482421875. Now, if you round "214.1999969482421875" to 6 digits, you get "214.199997" because the 7th digit is a 9, turning the 6 into a 7. But when you only round to 2 digits, you get "214.20" because the 3rd digit is a nine, so you need to increase the 9 on the 2nd digit. That "overflows" to 10, turning the ".19" into ".20".
DarkDust
Oh!! I believe you are trying to say that this operation is done in decimal representation, and that's how 214.199997 was formed as a round effect of double precision. Because had it been in binary representation we already confirmed that 214.20 is NOT representable in binary, with all rounding skills used.
RIPUNJAY TRIPATHI
Yes, that's what I wanted to say :-)
DarkDust
A: 

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.

Philip Starhill
If it was possible to do so, why did we store approximation in first place ? :-). Excuse me if I am causing pain, but that what I wanted to know, what does scanf does to represent a value that was given in six places after decimal to get converted into two points after decimal. Given that .20 is NOT representable in binary floating point
RIPUNJAY TRIPATHI
It looks like this follow-up question is answered in the comment dialog with DarkDust.
Philip Starhill
A: 

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.

R..