I came across a very puzzling example of non-deterministic floating point arithmetic in 64-bit Java (jdk1.6.0_26, Windows 7). Consider the following function:
boolean ok() {
double x = 0.1;
return 1.0 - x == 0.9;
}
Under normal circumstances, this function returns true. However, 64-bit Java sometimes returns false. In those circumstances, there is a small round-off error. The hexadecimal representation of 0.1
is:
0x1.999999999999ap-4
and of 0.9
is:
0x1.ccccccccccccdp-1
Normally, evaluating 1.0 - 0.1
yields this nearest representable value of 0.9
. Unfortunately, the 64-bit (but not 32-bit) JVM sometimes returns a slightly different value:
0x1.cccccccccccccp-1
Note that the last hexadecimal digit differs by one bit, C
instead of D
. This floating-point value is further away from 0.9
than the former value. That is not the issue, though. The issue is that the 64-bit JVM seems to flip back and forth between these two values non-deterministically.
I observed this behaviour in the context of a large desktop application. I scattered calls to ok()
throughout the codebase, printing out the value each time. What I saw was that the first few hundred calls returned true
, indicating that the correct rounding behaviour was occurring. After that, the calls would start returning false
. For the most part, it seemed that once the rounding errors started occurring, they would not revert back to the correct behaviour. What makes this puzzling is that I did discover circumstances where calls would once again start rounding properly. Except for this last point, I would have guessed that some kind of JIT problem was responsible for this bug. Now, I have no idea.
I spent hours trying to narrow down exactly where ok()
started failing by printing out its result in strategic locations. Ultimately I failed: the transition from proper to improper rounding seemed to occur in framework code that I could not easily instrument (i.e. Eclipse infrastructure). I was unable to create a free-standing unit test that exhibited the behaviour. I may redouble my efforts to find such a test, but until then this one will have to be filed under Mysterious Behaviour.
Incidentally, applying the strictfp
modifier to a class that performs this calculation has no effect.
A bit of Googling reveals at least one person who has encountered non-determinism in Java floating-point:
Nondeterministic Floating-Point Conversions in Java
The problem described there is somewhat different, but the root cause may be the same.