2

I would like to know the mechanism why the compiler shows inaccurate value of float. Example

float a = 0.056;
printf("value = %f",a); // this prints "value = 0.056"

If you try to store 0.056 in binary floating point format, you get this (use this link for conversion)

0.00001110010101100000010000011000 which is equal to 0.0559999998658895

1. How the compiler shows 0.056 while it should show 0.055999999?

Lets take this example a little further

#include <stdio.h>
main()
{
float a, b;

a = 0.056;
b = 0.064; // difference is 0.08 

printf("a=%f, b=%f",a,b);

if( b - a == 0.08) // this fails
    printf("\n %f - %f == %f subtraction is correct",b,a,b-a); 
else
    printf("\n%f - %f != %f Subtraction has round-off error\n",b,a,b-a);
}

Note that else block gets execute here while we expect if block to be correct. Here is the output.

a=0.056000, b=0.064000
0.064000 - 0.056000 != 0.008000 Subtraction has round-off error

Again the values are shown the way we expect (with no round off error) but these values do have round off errors but false disguised values are shown. My second question is

2. Is there a way to show the actual value of the stored number rather than the disguised one that we entered?

Note: I have include C code in Visual Studio 2008 but it should be reproducible in any language.

Community
  • 1
  • 1
TheTechGuy
  • 16,560
  • 16
  • 115
  • 136
  • 15
    [google](http://www.google.com/search?q=what+every+computer+scientist+should+know+about+floating+point) "what every computer scientist should know about floating point" – pmg Jan 10 '12 at 16:38
  • I mean I know it, I want to know how the compiler displays the binary value inaccurate which fools the programmer too? – TheTechGuy Jan 10 '12 at 16:39
  • 5
    it's not about the compiler, it's about the implementation of `printf` – Zonko Jan 10 '12 at 16:43
  • @pmg - there are plenty of those kinds of questions on SO, but I don't think this is one of them. – Joe Jan 10 '12 at 16:46
  • @Joe - notice I provided that as a comment, not an answer :) – pmg Jan 10 '12 at 16:53
  • Sure! I have made that comment myself before. – Joe Jan 10 '12 at 16:53
  • Mmh... You seem to be getting `0xCAC083` as your mantissa, but I get `0xCAC084`, which makes up for the difference between 0.0559999998658895 and .05600000172853469848 – Ricardo Cárdenes Jan 10 '12 at 17:20

5 Answers5

11

The compiler doesn't show anything 😉 Your program shows 0.056 because %f only show the result up to 6 digits. Try %.16f if you want to see all the inaccuracies (Result: http://ideone.com/orrkk).

The manpage of printf shows many other options you can use with these specifiers.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 5
    and `printf` is rounding the values, not truncating. – Zonko Jan 10 '12 at 16:42
  • 2
    @Thecrocodilehunter: Sorry I was missing a `.`. `%16f` sets the width to 16, `%.16f` sets the precision to 16. See update. – kennytm Jan 10 '12 at 16:44
  • Now this `a=0.0560000017285347, b=0.0640000030398369` again since this is float not a double thats why you see discrepency after 7 digit. In essense it is still .056 and .064 – TheTechGuy Jan 10 '12 at 16:46
  • 1
    @Thecrocodilehunter: Not sure what you mean. You cannot store 0.056 or 0.064 exactly whether it's `float` or `double`. – kennytm Jan 10 '12 at 18:22
  • @KennyTM that is what I am after. If I can't store it accurate, why printf show it accurate. .16 in this case is totally wrong because float has accuracy of upto 8 digits. So I used .8f and I still get 0.056. Now do this `a = 0.056-0.00000001;` .8f now displays a=0.05599999 and you see it does not rounds it. I think the way it works, the compiler internally works on more than 32 bits. If the extra bits value rounds the number to the next bigger number, it round it otherwise it leaves it as it is. But this is my explanation at this point with no proof. – TheTechGuy Jan 10 '12 at 19:28
  • @Thecrocodilehunter: The "exact" value of that 0.056 is 0b111001010110000001000010 × 2⁻²⁸ (0.056000001728534698486328125), so rounding after 8 digits still gives you 0.05600000. That 0.00000001 is exactly 0b101010111100110001110111 × 2⁻⁵⁰ (9.99999993922529029077850282192230224609375e-8), the subtraction causes this to lose 22 bits of significance to be rounded to 0b11 × 2⁻²⁸ (1.11758708953857421875e-8), so the subtracted value will become (0b111001010110000001000010 - 0b11 = 0b111001010110000000111111) × 2⁻²⁸ (0.0559999905526638031005859375), so after rounding we get 0.05599999. – kennytm Jan 10 '12 at 19:48
  • 1
    @Thecrocodilehunter: The FPU/CPU (not the compiler) does not need to work on more than 32 bits. Remember floating points are computed in *base-2*. You can reason with decimal arithmetic. Please do read "what every computer scientist should know about floating point" as \@pmg has suggested. – kennytm Jan 10 '12 at 19:51
4

In most languages, the routines for printing float values actually print the shortest decimal number that is closer to the float value to be printed than to any other float value. This often (but not always) masks the rounding errors resulting from translation of decimal literals to float values.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • I like your answer but need some depth. – TheTechGuy Jan 10 '12 at 16:48
  • 1
    @The crocodile hunter: depth in what regard? – Michael Borgwardt Jan 10 '12 at 17:04
  • how printf works to remove this round off error. It is pretty much mathematical question. – TheTechGuy Jan 10 '12 at 17:58
  • @The crocodile hunter: It's a very, very complicated task. Here's the source code for OpenJDK: http://www.docjar.com/html/api/sun/misc/FloatingDecimal.java.html (the relevant part is the dtoa() method). That's so much code that there's a stackoverflow question just about that: http://stackoverflow.com/questions/3173056/why-does-dtoa-c-contain-so-much-code – Michael Borgwardt Jan 11 '12 at 08:37
2

I see a lot of talking about printf and how it is printing stuff "the wrong way" because it rounds things up, etc. printf is printing exactly what you expect, when you notice that the actual number stored in a is 0.05600000172853469848.

OP is assuming that the number stored in there is 0.0559999..., but a look to the actual number shows that's wrong:

#include <stdio.h>

int main() {
        float a = 0.056;
        printf("%A\n", a);
}

That will print 0X1.CAC084P-5, meaning our mantissa (0xCAC084) is 110010101100000010000100. That's 24 bits, though, not the 23 we can store in a 32 bits (IEEE-754 single precision) floating point, meaning that what's in there is actually 11001010110000001000010

Remember that the mantissa is normalized and assumed to start by 1 so, applying the exponent, etc, our number is:

0.0000111001010110000001000010

which translates to 0.05600000172853469848

OP assumed this, instead:

0.00001110010101100000010000011

which, certainly is more accurate, BUT that requires a bit more than what the mantissa can store, so we'd end up with this:

0.0000111001010110000001000001

or 0.05599999800324440002.

Of course, none of numbers are 0.056, but the error in the representation is higher for the latter one! So no surprise that we're getting what we're getting...

Ricardo Cárdenes
  • 9,004
  • 1
  • 21
  • 34
1

2) If you have a C99 library, try printing the double in hexadecimal

printf("%A\n", 56.0/100);
pmg
  • 106,608
  • 13
  • 126
  • 198
1

You are making the mistake of assuming that your float was ever accurate.

They are not designed to represent a precise value such as 0.0559999998658895. Libraries such as GMP exist for that.

Floats are designed to be fast and approximate.

In your example, 0.056 is displayed because the digits 0.0559999 are presumed to be accurate and the digits that follow 99865889... are considered mostly noise and only significant enough to round up 0.0559999 to 0.056.

printf doesn't know that you consider 0.056 to be "correct". It just knows that a float printed in human-readable format is only accurate to about 6 significant digits, and 0.0560000 represents the closest match using that many digits.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • @Thecrocodilehunter: Edited my response to answer that. – Drew Dormann Jan 10 '12 at 17:07
  • ok do this. `a = 0.056-0.000000001;' why does it now shows a=0.0559999980032444, why not round it to 0.056 ? – TheTechGuy Jan 10 '12 at 18:03
  • @Thecrocodilehunter: When I did what you asked me to do, the result I got was 0.056. – Drew Dormann Jan 10 '12 at 18:39
  • I put in one extra 0 in there. Plz try 0.056-0.00000001; I think I got it. float internally works on more than 32 bits. If the extra bit rounds the value it rounds it, otherwise it leave it as it is. – TheTechGuy Jan 10 '12 at 19:34
  • "_a float printed in human-readable format is only accurate to about 6 significant digits_" what? – curiousguy Jun 27 '12 at 13:56
  • @curiousguy: C allows a float's value to be printed in the code in a people-friendly format such as "0.056", even if that value can't be represented by a float. An approximation will be used in these circumstances. With the common 32-bit IEEE 754 format, 6 digits of accuracy can be expected from this approximation. – Drew Dormann Jun 27 '12 at 15:32
  • @DrewDormann IOW, the value will be converted to a decimal representation, truncated to 6 digit? – curiousguy Jun 27 '12 at 16:04
  • @curiousguy: There are additional rules that don't relate to this question. But you are correct that by default, printf won't show more than 6 significant digits of a `float`. And the reasoning is that additional digits would be mostly noise. – Drew Dormann Jun 27 '12 at 18:19
  • @Thecrocodilehunter: You are correct, although I don't think the C standard dictates that. Intel-compatible CPUs have always used 80 bits to represent floating point numbers internally, while IEEE-standard floats promise that each result is rounded to the nearest value, which implies that at least 1 extra bit is maintained. – Drew Dormann Jun 28 '12 at 00:24