Math.pow根据Java版本产生不同的结果

我在JDK版本1.7.0_60上运行以下代码:

System.out.println(Math.pow(1.5476348320352065, (0.3333333333333333))); 

其结果是:1.1567055833133086

我在JDK版本1.7.0上运行完全相同的代码。

其结果是:1.1567055833133089

我明白,double不是无限精确的,但是java规范中的变化是否会导致差异?

PS:因为我们使用遗留系统,大十进制不是一个选项。

编辑:我能够追踪更改的时间:它是在JDK版本1.7.0_40中引入的(与版本1.7.0_25相比)。

但有没有在java规格导致差异的变化?

Math.pow*根据Math.pow的Javadocs,最多允许一个ULP(最后一个单位)的差异。 如果我们看看你的两个价值观:

 System.out.printf("%016x\n", Double.doubleToLongBits(1.1567055833133086)); System.out.printf("%016x\n", Double.doubleToLongBits(1.1567055833133089)); 

我们得到:

 3ff281ddb6b6e675 3ff281ddb6b6e676 

这实际上相差一个ULP。

您所看到的可能是由于JDK / JVM用于实现这些操作的浮点指令序列略有不同。


*至less,我不知道!

规范没有改变,但热点优化器有一些变化可能与此相关。

我挖了这些代码部分:

  • 从更新25: vm / opto / library_call.cpp:inline_pow
  • 从更新40: vm / opto / library_call.cpp:inline_pow

(这些并不完全是引入这些更改的版本,因为您提供的版本信息,我只是select了它们)。

这些变化(以及代码在做什么)远远超出我可以在合理的时间内分析的结果,但也许有人认为这个参考很有趣或有用。

如果你想在JVM之间使用可重复的浮点值,你可以使用strictfp关键字,参见下面的问题什么时候应该在java中使用“strictfp”关键字?

为了在所有Java版本之间产生一致的结果,解决scheme是使用StrictMath.pow()而不是Math.pow()

有关可能导致差异的背景信息,请参阅此答案 。