5

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?

  • 1
    Are you sure those are the correct values? What happens when you do this in say... C? – user40980 Feb 20 '16 at 14:32
  • I don't know about Java, but C# allows using higher precision. In practice that means that 80 bit extended floating point values are used for calculations (using the x87 instruction set), which can then be reduced to 64 bit doubles or 32 bit floats when necessary or convenient. This can result in performance benefits, but it also makes reasoning about the code very hard. – CodesInChaos Feb 20 '16 at 14:38
  • 2
    My guess is that the float->string conversion (for the printing) is being "helpful" by rounding the value to what most people expect. – Bart van Ingen Schenau Feb 20 '16 at 14:41
  • This is my java snippet in an online IDE: tutorialspoint.com/… I have no clue of C, but this is what I got: tutorialspoint.com/… – Baustein Feb 20 '16 at 14:43
  • @BartvanIngenSchenau Do you know a way to disable the "being friendly" feature? – Baustein Feb 20 '16 at 14:44
6

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.

  • So my manual calculation is appearantly correct. Using e.g. System.out.format("%.20f%n", a+b); gives out my result. Thanks. – Baustein Feb 20 '16 at 14:53
  • @Baustein there's a fair amount of code in the Float to string converter that is actually an interesting read to figure out how it works. – user40980 Feb 20 '16 at 14:57
  • Nitpick: The C code violates strict aliasing. It would be better to use memcpy to copy the bytes of the float into the bytes of an integer. And I'd prefer using uint32_t here. – 5gon12eder Feb 20 '16 at 16:24
3

Probably because 0.3f = 0.30000001192092896.

0.3f is the single precision floating point number closest to the real number 0.3. The real number 0.3 cannot be represented exactly as a single precision floating point number (or as any binary floating point number), therefore 0.3f must be slightly different from the real number 0.3.

Single precision floating point has a 24 bit mantissa. For 0.3, the highest bit of the mantissa has a value of 0.25 = 2^-2, the lowest bit has a value of 2^-25, and you'd have to expect a rounding error up to

2^-26 = 0.000 000 014 901 161

Actually, 0.3f was rounded up by

0.000 000 011 920 928 96

which is entirely in the range that you would expect.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.