Description
The Graal optimized Math.exp seems to be producing incorrect results by a large margin (not a few ulp). Specifically exp(x) for x < -1023 produces ~3.99999.... Before Graal compilation, the result is correct.
I wrote a program to demonstrate this.
double compute(double x) {
return Math.exp(x);
}
void run(int n) {
double x = Double.parseDouble("-1024"); // Make sure the optimizer doesn't constant propagate.
double r = 0; // Collector to make sure the computed value is used.
for (int i = 0; i < n; i++) {
r += compute(x);
}
// Print the result of compute and collector (to prevent optimizations)
System.out.println(compute(x) + " (collector: " + r + ")");
}
(for a runnable version see MathOptimizationTest2.java)
This code produces the following output on GraalVM 0.31:
0.0 (collector: 0.0)
3.9999999999999996 (collector: 39999.99999999999)
3.9999999999999996 (collector: 39999.99999999999)
3.9999999999999996 (collector: 39999.99999999999)
3.9999999999999996 (collector: 39999.99999999999)
(Just to be clear the number should be exp(-1024) which is 0 at double-precision)
The same code running on with "-XX:-UseJVMCICompiler" to disable Graal for the code produces the expected output (this output is also produced by my Java 8 JRE):
0.0 (collector: 0.0)
0.0 (collector: 0.0)
0.0 (collector: 0.0)
0.0 (collector: 0.0)
0.0 (collector: 0.0)
The issue first appears at -1024 (-1023 works correctly giving 0.0). I dug a little and I noticed this:
For IEEE double if x > 709.782712893383973096 then exp(x) overflow if x < -745.133219101941108420 then exp(x) underflow
in org.graalvm.compiler.lir.amd64.AMD64MathIntrinsicUnaryOp. I don't know enough about Graal to tell if this code is actually used in my case and the limits are not the same, but it definitely seems related.