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

我试图在Java中确定double 机器epsilon ,使用它的定义是最小的可表示的doublex ,使得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))); 

结果是我的预期:

 eps = 2.220446049250313E-16 eps == 2^-52? true 

到现在为止还挺好。 但是,如果我检查给定的eps确实是最小的 x ,使得1.0 + x != 1.0 ,那么似乎有一个较小的,即根据Math.nextAfter()前一个 double值:

 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)); 

产量:

 epsPred = 2.2204460492503128E-16 epsPred < eps? true 1.0 + epsPred == 1.0? false 

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

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

编辑:感谢评论者,我终于明白了。 我实际上使用了错误的定义! eps = Math.ulp(1.0)计算距离最小可表示的双倍> 1.0的距离,但是 – 这就是重点 – eps 不是 1.0 + x != 1.0的最小x ,而是大约两倍的值:添加1.0 + Math.nextAfter(eps/2) 向上舍入为1.0 + eps

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

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

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

你的“定义” 错误了近2倍 。

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

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

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

ulp方法的文档说:

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

所以,如果你想要1.0 + eps != 1.0的最小eps值,你的eps通常应该小于 Math.ulp(1.0) ,因为至少对于任何大于Math.ulp(1.0) / 2 ,结果将被四舍五入。

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

Interesting Posts