A double
is limited to 64-bit. Using the Pigeon Hole Principle, that means there are no more than 264 unique values that a double
could possibly represent.
That's a problem: doubles purport to represent any number between minus infinity and positive infinity. There are an infinite amount of numbers between 0 and 1, let alone minus infinity and +infinity, and 'infinity' is vastly larger than 264 is.
So, how does that work?
There are slightly fewer than 264 'blessed' numbers - the chosen numbers: These are exactly representable by a double
. non-blessed numbers simply aren't a thing a double
can represent. They aren't equally distributed; there are more blessed numbers near 0, and fewer as you get further away from it. Around 252 there are less than 1 blessed number per number (meaning, above 252, there are whole integers that are not blessed).
So how does double math work, when the result isn't blessed? Everything in double math is rounded to the nearest blessed number at every step.
This explains a few things:
double d = 0; // 0 is blessed.
for (int i = 0; i < 10; i++) {
d += 0.1; // uhoh! 0.1, simple as it seems, is NOT blessed!
}
double f = 1.0; // 1.0 is blessed.
System.out.println(f == d); // false ????
System.out.println(d); // 0.9999999 ??
Run this code and marvel: It really does print false
- evidently, ten times 0.1 isn't the same as 1.0. This is that rounding thing at work: ROUND(10 * ROUND(0.1))
isn't the same as ROUND(1.0)
.
In case you are wondering why something as utterly trivial as 0.1 isn't blessed: Computers count in binary. Just like 1/3 isn't blessed in our decimal system either (it's 0.3333 infinitely repeating - no finite piece of paper can properly represent it in decimal notation!), that's because '3' does not fit well into '10', '1/10th', which works fine in decimal (0.1 entirely represents it), is in binary problematic the same way 1/3 is a problem in decimal. Everything other than factors of 2 is a problem. For example, 7/64th is blessed. Because 64 is 26.
So why do you get the results you showed?
Simple: Because the BigDecimal
's .doubleValue()
method returns the nearest blessed number to what the BD represents. It has to - double
cannot represent non-blessed numbers, but BD can, so what to do if the BD number is non-blessed and you invoke .doubleValue()
? The nearest blessed number to 38.399999999999999 (which, obviously, isn't itself blessed) is X, and the nearest number to 38.39999999999999 is Y. X and Y aren't the same. System.out.println
is not appropriate for ever printing doubles - you really need to tell the system how to round it (use printf("%.6d", value)
instead for example), it rounds off very small errors, but that's it. That weird rounding printing stuff is written so that X is printed as 38.4
and Y is printed as 38.39999999999999
.
(X and Y are actual numbers, written out in decimal they look very complicated, and I don't think it's worthwhile doing the math to figure out what they are, precisely. You can convert these doubles back to BigDecimal and print those to see the exact value - that gets rid of the rounding that System.out.println
does, I think).