I know that java has double precision pitfalls, but why sometimes, the approximation result is ok, but sometimes isn't.
code like this:
for ( float value = 0.0f; value < 1.0f; value += 0.1f )
System.out.println( value );
result like this:
0.0
0.1
0.2
0.3
...
0.70000005
0.8000001
0.9000001
As you state, not all numbers can be represented exactly in IEEE754. In conjunction with the rules that Java uses for printing those numbers, that affects what you'll see.
For background, I'll briefly cover the IEEE754 inaccuracies. In this particular case,
0.1
cannot be represented exactly so you'll often find that the actual number used is something like0.100000001490116119384765625
.See here for the analysis of why this is so. The reason you're getting the "inaccurate" values is because that error (
0.000000001490116119384765625
) gradually adds up.The reason why
0.1
or0.2
(or similar numbers) don't always show that error has to do with the printing code in Java, rather than the actual value itself.Even though
0.1
is actually a little higher than what you expect, the code that prints it out doesn't give you all the digits. You'd find, if you set the format string to deliver 50 digits after the decimal, that you'd then see the true value.The rules for how Java decides to print out a float (without explicit formatting) are detailed here. The relevant bit for the digit count is:
By way of example, here's some code showing you how this works:
The output of this is:
The first column is the real value of the float, including inaccuracies from IEEE754 limitations.
The second column is the 32-bit integer representation of the floating point value (how it looks in memory rather than its actual integer value), useful for checking the values at the low level bit representation.
The final column is what you see when you just print out the number with no formatting.
Now looking at some more code, which will show you both how the inaccuracies of continuously adding an inexact value will give you the wrong number, and how the differences with surrounding values controls what is printed:
This code uses the continued addition of
0.1
to get up to0.6
then prints out the values for that and adjacent floats. The output of that is:The first thing to look at is that the final column has enough fractional digits in the middle lines of each block to distinguish it from the surrounding lines (as per the Java printing specifications mentioned previously).
For example, if you only had three places after the decimal, you would not be able to distinguish between
0.6
and0.6000001
(the adjacent bit patterns0x3f19999a
and0x3f19999b
). So, it prints as much as it needs.The second thing you'll notice is that our
0.7
value in the second block is not0.7
. Rather, it's0.70000005
despite the fact that there's an even closer bit pattern to that number (on the previous line).This has been caused by the gradual accumulation of errors caused by adding
0.1
. You can see from the final block that, if you just used0.7
directly rather than continuously adding0.1
, you'd get the right value.So, in your particular case, it's the latter issue causing you the problems. The fact that you're getting
0.70000005
printed out is not because Java hasn't got a close enough approximation (it has), it's because of the way you got to0.7
in the first place.If you modify that code above to contain:
you'll find it can print out all the numbers in that group correctly.