Reputation: 19500
While answering this question, I got these confusing results:
double d = 0.49999999999999990d; //output 0.4999999999999999 as expected
d = 0.49999999999999991d; //output 0.4999999999999999
d = 0.49999999999999992d; //output 0.49999999999999994
d = 0.49999999999999993d; //output 0.49999999999999994
d = 0.49999999999999994d; //output 0.49999999999999994 as expected
d = 0.49999999999999995d; //output 0.49999999999999994
d = 0.49999999999999996d; //output 0.49999999999999994
d = 0.49999999999999997d; //output 0.49999999999999994
d = 0.49999999999999998d; //output 0.5
Why is this behaviour showing?
NOTE: I got these outputs just by printing d; I mean I used:
System.out.println(d);
Upvotes: 1
Views: 459
Reputation: 719386
Floating point types cannot exactly represent all Real numbers. In fact, a double
is a 64-bit floating point type, and can therefore only represent 264 different values ... and there are an infinite number of Real numbers. (Indeed, there are an infinite number of Real numbers between 0.49999999999999990d
and 0.49999999999999999d
.)
You have picked some numbers that fall between consecutive values in the set-of-all double
values. In other words, you've exceeded the limits of precision for the double
type.
What can you do about it? Well one way to get more precision is to use the BigDecimal
class, which can (in theory) give you in the region of 2 BILLION decimal digits of precision. The downside is that your code will be more complicated ... and significantly slower, depending on how much precision you use.
The other approach is to recognize that you probably don't need that much precision.
Upvotes: 2
Reputation: 421180
System.out.println(d)
will go through Double.toString
which is a fairly complex method (as seen in its documentation) an will not always behave as you'd expect. It basically gives the shortest string which uniquely determines d
.
Perhaps the output of this program clarifies this:
double[] tests = {
0.49999999999999990d, //output 0.4999999999999999 as expected
0.49999999999999991d, //output 0.4999999999999999
0.49999999999999992d, //output 0.49999999999999994
0.49999999999999993d, //output 0.49999999999999994
0.49999999999999994d, //output 0.49999999999999994 as expected
0.49999999999999995d, //output 0.49999999999999994
0.49999999999999996d, //output 0.49999999999999994
0.49999999999999997d, //output 0.49999999999999994
0.49999999999999998d, //output 0.5
};
String[] literals = {
"0.49999999999999990d",
"0.49999999999999991d",
"0.49999999999999992d",
"0.49999999999999993d",
"0.49999999999999994d",
"0.49999999999999995d",
"0.49999999999999996d",
"0.49999999999999997d",
"0.49999999999999998d",
};
String f = "%-25s%-65s%-25s%n";
System.out.printf(f, "Literal", "Actually represents", "Printed as");
for (int i = 0; i < tests.length; i++)
System.out.printf(f, literals[i],
new BigDecimal(tests[i]).toString(),
Double.valueOf(tests[i]));
Output:
Literal Actually represents Printed as
0.49999999999999990d 0.49999999999999988897769753748434595763683319091796875 0.4999999999999999
0.49999999999999991d 0.49999999999999988897769753748434595763683319091796875 0.4999999999999999
0.49999999999999992d 0.499999999999999944488848768742172978818416595458984375 0.49999999999999994
0.49999999999999993d 0.499999999999999944488848768742172978818416595458984375 0.49999999999999994
0.49999999999999994d 0.499999999999999944488848768742172978818416595458984375 0.49999999999999994
0.49999999999999995d 0.499999999999999944488848768742172978818416595458984375 0.49999999999999994
0.49999999999999996d 0.499999999999999944488848768742172978818416595458984375 0.49999999999999994
0.49999999999999997d 0.499999999999999944488848768742172978818416595458984375 0.49999999999999994
0.49999999999999998d 0.5 0.5
As can be seen, the literal is sometimes far from the value it actually represents, which means that Double.toString
prints something that may look surprising.
Upvotes: 1
Reputation: 500853
Only certain numbers can be represented exactly as doubles
. There are three such numbers in the range under consideration:
0.49999999999999990
0.49999999999999994
0.5
Everything between these numbers gets rounded to the nearest of the three.
If you look at how these doubles are represented in hex, you'll see that the three numbers have consecutive mantissas (the part before the p
):
In [20]: float.hex(0.49999999999999990)
Out[20]: '0x1.ffffffffffffep-2'
In [21]: float.hex(0.49999999999999994)
Out[21]: '0x1.fffffffffffffp-2'
In [22]: float.hex(0.5)
Out[22]: '0x1.0000000000000p-1'
Representing numbers such as 0.49999999999999992
exactly would require more bits of mantissa than double
can offer.
Upvotes: 0