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

Dam*_*num 37 java math floating-point pow

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

System.out.println(Math.pow(1.5476348320352065, (0.3333333333333333)));
Run Code Online (Sandbox Code Playgroud)

其结果是:1.1567055833133086

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

其结果是:1.1567055833133089

我知道double并不是无限精确,但java规范中是否有变化会导致差异?

PS:因为我们使用遗留系统,所以Big Decimal不是一个选项.

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

Oli*_*rth 41

但java规范是否有变化导致差异?

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

System.out.printf("%016x\n", Double.doubleToLongBits(1.1567055833133086));
System.out.printf("%016x\n", Double.doubleToLongBits(1.1567055833133089));
Run Code Online (Sandbox Code Playgroud)

我们得到:

3ff281ddb6b6e675
3ff281ddb6b6e676
Run Code Online (Sandbox Code Playgroud)

确实相差一个ULP.

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


*至少,据我所知!

  • 为了记录,确切的结果是`1.156705583313308737 ...`,Java 1.7.0_60给出的答案更准确. (13认同)
  • @biziclop:为此,我们需要浏览特定于平台的本机方法的源代码,我现在不喜欢这样做;) (5认同)

Mar*_*o13 8

规范没有变化,但热点优化器中有一些可能(!)与此相关的变化.

我挖出了这些代码部分:

(这些并不是引入这些更改的版本,我只是因为您提供的版本信息而选择它们).

这些变化(以及代码所做的事情)远远超出了我在合理时间内可以分析的变化,但也许有人认为这个参考有趣或有用.


小智 5

如果您想在JVM之间使用可重复的浮点值,可以使用strictfp关键字,请参阅以下问题我何时应该在java中使用"strictfp"关键字?

  • @Damnum`buttrictfp`对`Math`中的方法没有任何作用.尝试[`StrictMath.pow()`](http://docs.oracle.com/javase/7/docs/api/java/lang/StrictMath.html#pow(double,%20double))并查看它是否产生一致的结果. (8认同)
  • @tmyklebu这给我们带来了几乎是哲学上的问题,为什么IEEE 754没有,对于那些无法以足够低的成本计算到0.5ULP的函数来强制执行它,使用精度/代码大小/速度交易标准化某些特定算法 - 在标准化时是最先进的技术.对此的答案似乎是,他们不想让生活更加艰难,更新,更准确的算法,这将不得不与标准作斗争.回想起来,做得很好的IEEE 754(和Sun将fdlibm降级为`StrictMath`). (2认同)

Dam*_*num 5

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

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