0

There's a great discussion on SO about why floating-point should not be used for currency operations and a lovely example is given here from Bloch's Effective Java.

I was playing around with this and noticed something weird. If the numbers are doubles:

System.out.println(1.03d - .42d); 
//prints out 0.6100000000000001.

But, if the numbers are floats:

System.out.println(1.03f - .42f); 
//prints out 0.61.

Why does this not fail in the same way for floats also? Both types are susceptible to the same problem, but what causes the difference in behaviour?

Community
  • 1
  • 1
Alex Florescu
  • 5,096
  • 1
  • 28
  • 49
  • Similar to http://stackoverflow.com/questions/177506/why-do-i-see-a-double-variable-initialized-to-some-value-like-21-4-as-21-3999996 . And although this is referring to C#, the answers at http://stackoverflow.com/q/618535/509840 are superb. – rajah9 Apr 10 '14 at 13:02
  • Why should it? Floats are less precise than doubles. – user207421 Apr 10 '14 at 13:04
  • every single value will behave differently between floats and doubles because of their binary representation, one example of properly displayed float is not an example that every float will be fine – Kamil Mikolajczyk Apr 10 '14 at 13:04
  • @EJP right, they are less precise, but I'm not sure I understand why that matters here. – Alex Florescu Apr 10 '14 at 13:08
  • @KamilMikolajczyk Of course not all floats would be fine. But the question is why is this one fine at all? – Alex Florescu Apr 10 '14 at 13:09
  • @AlexFlorescu you would have to see their binary representations, do the binary substraction and see where's the difference, it probably comes from rounding – Kamil Mikolajczyk Apr 10 '14 at 13:11

1 Answers1

5

There are eight interesting values involved here (four for each type). Their exact values are:

Double values

1.03d:  1.0300000000000000266453525910037569701671600341796875
0.42d:  0.419999999999999984456877655247808434069156646728515625
Result: 0.6100000000000000976996261670137755572795867919921875
0.61d:  0.60999999999999998667732370449812151491641998291015625

Float values

1.03f:  1.0299999713897705078125
0.42f:  0.4199999868869781494140625
Result: 0.61000001430511474609375
0.61f:  0.61000001430511474609375

Note how the closest double to 1.03 is slightly more than 1.03, and the closest double to 0.42 is slightly less than 0.42... so the result of the subtraction differs from the precise (decimal) subtraction by the sum of those two errors.

The closest float to 1.03 and the closest float to 0.42 are both less than the original values, so the errors mitigate against each other to some extent. That's why the double result "feels" more inaccurate than the float result. The float result happens to be as close to 0.61 as you can represent as a float, so the string representation is just "0.61". As there is a closer double to 0.61 than the subtraction result, the string representation has to differentiate between the two.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194