Java – Floating point calculation being too exact

floating pointjava

In Java, I've run this method:

private static void floatAddition() {
    float a = 0.1F, b = 0.2F, c = a+b;
    System.out.println(a + " + " + b + " = " + c);
}

and the result is 0.3 for floatAddition. I'm now trying to get behind why it is adding to a round result, and not to something slightly off due to floating points.

I've manually transformed 0.1 and 0.2 into binary 32-Bit system with IEEE 754 standard, this is what I got:

0.1 (decimal) = 0 01111011 10011001100110011001101 (binary)
0.2 (decimal) = 0 01111100 10011001100110011001101 (binary)

I now add these two together. Since the exponents are different, I have to bring them both to the bigger one (2^-3) so I'm unnormalizing 0.1.

0.1 (decimal) = 0.11001100110011001100111 * 2-3 (binary)
0.2 (decimal) = 1.10011001100110011001101 * 2-3 (binary)

This is the manual addition result:

0.1 (decimal) + 0.2 (decimal) = 10.01100110011001100110100 * 2-3 (binary)

In IEEE 754 standard, this would be

0 01111101 00110011001100110011010

Now for checking whether or not I'm correct, I've used this IEEE 754 Converter: http://www.h-schmidt.net/FloatConverter/IEEE754.html
According to this site, my result should've been 0.30000001192092896.

Why is my floating point calculation more exact than it should be?

Best Answer

Lets look at some quick code:

public class Floats {
    public static void main(String[] args) {
        float a = 0.1F;
        float b = 0.2F;
        float c = a+b;
        System.out.println(a + " + " + b + " = " + c);

        System.out.printf("0x%8x", Float.floatToIntBits(c));
    }
}

The output of this code is:

0.1 + 0.2 = 0.3
0x3e99999a

That last line is the hexadecimal representation of the value that is being rendered as 0.3.

So, lets look what C does.

#include <stdio.h>
int main (int argc, char* argv[]) {
    float a = 0.1F;
    float b = 0.2F;
    float c = a + b;
    printf("%f + %f = %f\n", a, b, c);
    printf("0x%8x\n", *(unsigned int*)&c);  
}

And this produces the output:

0.100000 + 0.200000 = 0.300000
0x3e99999a

You will note that the representation of the float value is equivalent.

I am certain that the code is properly following the specification. You may be confused at the float to string conversion, but that is something to be done with various formatting or other libraries to display what you want.

But the underlying floating point representation is exactly what it should be.

As an aside, the difference between the C and the Java can be seen by switching to printf where one is expected to be a bit more precise with the values:

public class Floats {
    public static void main(String[] args) {
        float a = 0.1F;
        float b = 0.2F;
        float c = a+b;
        System.out.println(a + " + " + b + " = " + c);
        System.out.printf("%f + %f = %f\n", a, b, c);

        System.out.printf("0x%8x", Float.floatToIntBits(c));
    }
}

prints out:

0.1 + 0.2 = 0.3
0.100000 + 0.200000 = 0.300000
0x3e99999a

Note the printf in Java matches the printf in C.

Digging into the JLS we find in section 5.1.11,

If T is float, then use new Float(x).

Digging through this, one gets to sun.misc.FloatingDecimal which when created with FloatingDecimal(float f) which goes through a fair bit of calculations to try to figure out what to display. Java8 changes this a little bit to do the BinaryToAsciiConverter, but the idea is still the same.

Related Topic