Java 8 和 Java 13 中的 Math.pow() 返回不同的结果

And*_*elf 2 java math jvm

以下代码在 Java 8 和 Java 11 上返回不同的结果。

class Playground {
    public static void main(String[ ] args) {
        long x = 29218;
        long q = 4761432;
        double ret = Math.pow(1.0 + (double) x / q, 0.0005);
        System.out.println("val = " + String.format("%.24f", ret));
    }
}
Run Code Online (Sandbox Code Playgroud)

爪哇 8:

val = 1.000003058823805400000000
Run Code Online (Sandbox Code Playgroud)

Java 11:(结果与 Python、Rust 相同)

val = 1.000003058823805100000000
Run Code Online (Sandbox Code Playgroud)

这些问题是:

  • 任何描述这种行为的文档?
  • 如何实现 Java 8 Math.pow()在 Python 中?
  • 如何确保与使用 Java 8 库的旧程序严格一致?

Sal*_*lba 11

如果我们只使用 Double.toString() 来打印答案,那么两种不同的结果将是

1.0000030588238054
1.0000030588238051
Run Code Online (Sandbox Code Playgroud)

末尾的额外数字只是 String.format() 的一个因素。我们看到这些数字仅相差最后一位有效的十进制数字。将两个数字转换为十六进制给出

1.000033518c576
1.000033518c575
Run Code Online (Sandbox Code Playgroud)

所以二进制表示在最后一个位置(ulp)只相差一个单位。

阅读Math.pow的规范,我们发现

“计算结果必须在准确结果的 1 ulp 以内。”

真实值接近 1.000003058823805246468 ( WolframAlpha ) 介于两个答案之间,因此两者都在规范内。

所发生的一切只是该库在算法上略有变化,也许是为了使其更快。

  • 如果你再快一点的话我就可以跳过我的工作了。但无论如何,用两种方式解释同一件事并不一定是坏事。接受我的敬意和+1,因为我是更快的回答者...... (2认同)

Hol*_*ger 7

注意

System.out.println("val = " + String.format("%.24f", ret));
Run Code Online (Sandbox Code Playgroud)

printf样式格式、字符串连接和println. 您可以printf首先使用:

System.out.printf("val = %.24f%n", ret);
Run Code Online (Sandbox Code Playgroud)

但是,请求 24 位十进制数字是没有意义的,因为double精度甚至无法远程提供那么多数字。当你使用

System.out.println("val = " + ret);
Run Code Online (Sandbox Code Playgroud)

相反,它将默认为实际可用的数字,从而产生

val = 1.0000030588238054
Run Code Online (Sandbox Code Playgroud)

对于 Java 8 和

val = 1.0000030588238051
Run Code Online (Sandbox Code Playgroud)

对于 Java 11。

所以区别只在最后一位。或者更准确地说

double d1 = 1.0000030588238051, d2 = 1.0000030588238054;
System.out.println((d2 - d1) == Math.ulp(d1));
Run Code Online (Sandbox Code Playgroud)

打印true,因此这两个值之间的距离是double. double它们之间没有其他价值。的规范pow

计算结果必须在精确结果的 1 ulp 以内。

由于上面的代码显示,两个结果的距离都是 1 ulp,因此当确切结果介于两个结果之间时,两个结果都是正确的。Wolfram Alpha说,确切的结果始于

1.000003058823805246…
Run Code Online (Sandbox Code Playgroud)

所以它介于这两个结果之间。因此,根据规范,这两个结果都是正确的。

为了更容易的比较:

1.0000030588238054         JDK  8
1.000003058823805246…      Wolfram Alpha
1.0000030588238051         JDK 11
Run Code Online (Sandbox Code Playgroud)

  • 那么,“你的问题”就不是你发布的问题。您要求提供“描述此类行为的文档”,您得到了。 (3认同)