以下代码在 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)
这些问题是:
Math.pow()在 Python 中?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 ) 介于两个答案之间,因此两者都在规范内。
所发生的一切只是该库在算法上略有变化,也许是为了使其更快。
注意
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)