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.