Java:双机器epsilon不是最小的x,这样1 + x!= 1?

Fra*_* D. 19 java floating-point epsilon

我想,以确定double 机器精度在Java中,使用它是最小的可表示的定义double值,x这样1.0 + x != 1.0,就像在C/C++.根据维基百科,这台机器epsilon等于2^-52(52是double尾数位数 - 1).

我的实现使用了这个Math.ulp()函数:

double eps = Math.ulp(1.0);
System.out.println("eps = " + eps);
System.out.println("eps == 2^-52? " + (eps == Math.pow(2, -52)));
Run Code Online (Sandbox Code Playgroud)

结果是我的预期:

eps = 2.220446049250313E-16
eps == 2^-52? true
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.不过,如果我检查给定的eps确实是最小的 x这样1.0 + x != 1.0,似乎有一个较小的一个,又名先前 double根据价值Math.nextAfter():

double epsPred = Math.nextAfter(eps, Double.NEGATIVE_INFINITY);
System.out.println("epsPred = " + epsPred);
System.out.println("epsPred < eps? " + (epsPred < eps));
System.out.println("1.0 + epsPred == 1.0? " + (1.0 + epsPred == 1.0));
Run Code Online (Sandbox Code Playgroud)

产量:

epsPred = 2.2204460492503128E-16
epsPred < eps? true
1.0 + epsPred == 1.0? false
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,我们有一个小于机器epsilon,加上1,产生不是1,与定义相矛盾.

那么根据这个定义,机器epsilon普遍接受的值有什么问题呢?还是我错过了什么?我怀疑浮点数学的另一个深奥的方面,但我看不出我错在哪里...

编辑:感谢评论者,我终于明白了.我实际上使用了错误的定义!eps = Math.ulp(1.0)计算的最小可表示双>的距离1.0,但是-这一点-这eps不是最小的x1.0 + x != 1.0,而是约两倍该值:将1.0 + Math.nextAfter(eps/2)四舍五入最多1.0 + eps.

Pas*_*uoq 21

使用它的定义是最小的可表示的双值x,使得1.0 + x!= 1.0,就像在C/C++中一样

这从来就不是定义,不是在Java中而是在C中而不是在C++中.

定义是机器epsilon是一个与最大浮点数/双倍之间的距离大于一.

你的"定义" 错误了近2倍.

此外,缺少strictfp仅允许更大的指数范围并且不应对ε的经验测量产生任何影响,因为这是从其1.0及其后继计算的,其中每一个和其差异可以用标准指数范围表示.

  • @ Random832如果你想"计算"它,使用`Math.nextAfter(1.0,Double.POSITIVE_INFINITY) - 1.0`.第一个提议是对于许多数字都适用的属性,包括1.0(这是真的:1.0 + 1.0!= 1.0 &&(1.0 + 1.0) - 1.0 == 1.0) (2认同)

dav*_*mac 7

我不确定你的实验方法/理论是否合理.Math类的文档说明:

对于给定的浮点格式,特定实数值的ulp是包含该数值的两个浮点值之间的距离

ulp方法的文档说:

双值的ulp是该浮点值与接下来幅度较大的双值之间的正距离

所以,如果你想最小的eps值,这样1.0 + eps != 1.0,您的EPS确实一般应小于 Math.ulp(1.0),因为至少大于任何值Math.ulp(1.0) / 2,其结果将被调高.

我认为最小的这样的价值将由Math.nextAfter(eps/2, 1.0).