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

2022-09-01 05:12:59

我在 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相比)。


答案 1

但是Java规范中是否有导致差异的变化?

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

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

我们得到:

3ff281ddb6b6e675
3ff281ddb6b6e676

这确实相差一个ULP。

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


*至少,据我所知不是那么远!

答案 2

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

我挖掘了这些代码部分:

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

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