Re: Double type precision in java



John Ersatznom wrote:

Now, there are, oddly, some -0.0s in the lists output. I'd guess it was
actually a subnormal -0.00000...0001 internally (and it's NOT always
outputting enough digits to exactly read it back in) except that many of
those are definitely products of power-of-2-denominator rationals -- in
fact, integers to the last one. Those shouldn't lose precision (unless
the numerator gets too big).

I don't think the representation of -0.0 is any kind of an issue here (-0.0 has
a perfectly good FP representation all of its own, so an "approximate" internal
representation is neither necessary nor permitted). To see a simpler version
of the same test that displays the same behaviour without any zeros:

public class Test
{
public static void
main(String[] args)
{
System.out.println(0.1D);
System.out.println(2.1D - 2.0D);
}
}

Which produces the output:
0.1
0.10000000000000009

(In this case the sum in the second println() is actually evaluated by the
compiler rather than at runtime, but that makes no difference to the
behaviour).


One way to think of why this happens:

The value 2.1 when represented as 64-bit floating point is "rounded" to the
nearest binary value, as it happens this is the value which (if printed at
perfect precision) would be
2.100000000000000088817841970012523233890533447265625
(That is just one a range of real numbers which all have the same
representation as a double, but it is the only one from that which is exactly
representable as a double).

Similarly the most precise representation of 2.0 is
2.
(;-)

But that of the result of the calculation (2.1 - 2.0) is:
0.100000000000000088817841970012523233890533447265625

For comparison, that of 0.1 is
0.1000000000000000055511151231257827021181583404541015625

Note the difference. This is part of the reason for the unexpected (by the OP)
results, and is also the reason why (2.1 - 2.0) != 0.1.

Also, when Java prints the double values, it chooses the real number with the
shortest string representation from the range of reals which all map the
same double value. In this case the nearest to:
0.100000000000000088817841970012523233890533447265625

is:
0.1

Whereas the nearest to:
0.1000000000000000055511151231257827021181583404541015625
is:
0.10000000000000009

Hence the output of the test program.

-- chris



.