升级到Java 1.8.0_20后,我们的测试系统报告了错误,但代码没有改变。我发现,使用完全相同的输入参数调用Math.pow()
会在重新调用的调用时产生不同的结果。在Java 1.8.0_11中,它的行为与预期的一样,并且总是返回相同的值,但是对于Java 1.8.0_20及更高版本,它有时会返回稍微不同的值。
这与问题Math.pow yields different result depending on java version类似,但不同,因为pow()的结果在一个VM中不同。
在Java 1.8.0_20及更高版本
下运行时,以下JUint测试失败import static org.junit.Assert.assertEquals;
import java.util.function.BiFunction;
import org.junit.BeforeClass;
import org.junit.Test;
public class PowerTest {
private static final int N = 1000000;
private static final double base = 5350.456329377186;
private static final double exp = 2.0;
private static double eval(final BiFunction<Double, Double, Double> f) {
return f.apply(base, exp);
}
private void loop(final BiFunction<Double, Double, Double> f) {
final double x = eval(f);
for (int i = 0; i < N; i++) {
final double p = eval(f);
assertEquals("i=" + i, x, p, 0);
}
}
@BeforeClass
public static void info() {
System.out.println("Java " + System.getProperty("java.version"));
}
@Test
public void mathPow() {
loop(Math::pow);
}
@Test
public void strictMathPow() {
loop(StrictMath::pow);
}
}
在Java 1.8.0_11下测试不会失败,或者如果使用-Xint
启用了热点。 pow()的严格数学版本产生一致的结果。我怀疑热点JIT做了一些优化,切换到pow()的不同实现,这会产生某些输入值的不同结果。数学函数应该是确定性的,并且应该产生一致且可重复的结果。
这是一个错误还是一个功能?
答案 0 :(得分:8)
我找到了以下错误报告:
JDK-7021568 : Double.parseDouble() returns architecture dependent results
它非常相似,因为它报告了一个浮点运算,在JITed代码和解释代码中返回的结果略有不同。
该问题被标记为错误并已修复。在此基础上,我认为你所看到的也是一个bug,应该报告给Oracle。
答案 1 :(得分:1)
这是一个错误。我向ORACLE(http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8063086)报告了它,它被接受并在Java 9中修复。