FULL PRODUCT VERSION :
java version "1.6.0_24"
Java(TM) SE Runtime Environment (build 1.6.0_24-b07)
Java HotSpot(TM) Client VM (build 19.1-b02, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
EXTRA RELEVANT SYSTEM CONFIGURATION :
This also occurs under OpenJDK on Linux:
java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.4) (6b20-1.9.4-0ubuntu1)
OpenJDK Client VM (build 19.0-b09, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
The result of the conversion of the decimal string representing the value 2^-1047 + 2^-1075 depends on the architecture on which Java runs. For example, using x87 instructions, the converted result is 0x0.0000008p-1022; using SSE instructions, the converted result is 0x0.0000008000001p-1022.
I believe the fix is to add the 'strictfp' keyword to the FloatingDecimal class, either at the class or doubleValue() method level (I tried both -- both work). When that is done, the result is consistently 0x0.0000008000001p-1022 (which is incorrect by one ULP, but that I'll report as a separate bug).
(See my article http://www.exploringbinary.com/nondeterministic-floating-point-conversions-in-java/ for more details, including examples on 64-bit systems and on the Power architecture.)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached testcase diffconvert.java twice:
1) java -XX:UseSSE=0 diffconvert
(no output)
2) java -XX:UseSSE=2 diffconvert
Iteration 113 converts as 0x0.0000008p-1022
Iteration 114 converts as 0x0.0000008000001p-1022
In this case, the switchover from default x87 FPU instructions to JIT produced SSE instructions causes the result to change. (The iteration in which it switches varies from run to run).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The same converted value no matter which floating-point instructions are used to compute it.
ACTUAL -
Two different converted values for the same decimal string.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
class diffconvert {
public static void main(String[] args) {
double conversion, conversion_prev = 0;
//2^-1047 + 2^-1075
String decimal = "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316";
for(int i = 1; i <= 10000; i++) {
conversion = Double.parseDouble(decimal);
if (i != 1 && conversion != conversion_prev) {
System.out.printf("Iteration %d converts as %a%n",
i-1,conversion_prev);
System.out.printf("Iteration %d converts as %a%n",
i,conversion);
}
conversion_prev = conversion;
}
}
}
---------- END SOURCE ----------