【问题标题】:Java: Double machine epsilon is not the smallest x such that 1+x != 1?Java:双机ε不是最小的x,使得1 + x!= 1?
【发布时间】:2015-04-28 21:15:53
【问题描述】:

我正在尝试确定 Java 中的 double machine 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,似乎有一个更小的,也就是previous @ 987654334@值根据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));

产量:

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 不是最小的 x1.0 + x != 1.0,而是大约 两倍 该值:将 1.0 + Math.nextAfter(eps/2) 添加到 向上1.0 + eps

【问题讨论】:

  • 你试过strictfp吗?
  • 是的,strictfp 在这里没有帮助。

标签: java floating-point epsilon


【解决方案1】:

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

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

定义是机器epsilon是1到大于1的最小float/double之间的距离。

你的“定义”是wrong by a factor of nearly 2

此外,strictfp 的缺失仅允许更大的指数范围,并且不应对 epsilon 的经验测量产生任何影响,因为它是根据 1.0 及其后继计算得出的,其中每一个以及它们的差异可以用标准指数范围来表示。

【讨论】:

  • 1.0 + x != 1.0 &amp;&amp; (1.0 + x) - 1.0 == x 是一个足够的定义吗?还是“1.0减(大于1.0的最小double值)”?
  • @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)
  • 所以建议的定义 (1.0 + x != 1.0) 因为四舍五入而无效?是这样吗?
  • @BrianJ 是的。 1.0 + x 对于我们在这里关注的幅度的x 的值通常是不精确的。相比之下,虽然减法的定义隐含了这一事实,但0x1.00000000000010x1.0000000000000 之间的距离可以表示为double0x1.0000000000001 - 0x1.0000000000000,作为接近浮点数的减法,是精确的并且计算正是这个值。
  • @FranzD。维基百科页面声称 C 标准使用该定义。它不是。我在我的博文中实际上引用了 C 标准。请自己检查参考open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf(要查找的句子在博客文章中给出)。错误的定义会给出错误的结果,因为任何人都可以自己检查(你也有)。 glibc 中使用了正确的定义。你还需要什么?
【解决方案2】:

我不确定您的实验方法/理论是否合理。 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) 给出。

【讨论】:

  • 数学 ulp(x) 是为任何实数 x 定义的,因此对于大多数 x 值,定义是否具有 ≤ 或 ens-lyon.fr/LIP/Pub/Rapports/RR/RR2005/RR2005-09.pdf中有相当彻底的处理
  • @davmac:我也不确定我的方法是否合理,这就是我问的原因 :) 但我不明白:为什么不是 ulp(1.0),“1.0 之间的正距离和下一个更大的双倍”,1.0 + eps != 1.0 的最小值?该属性不应该有任何较小的值,因为 ulp(1.0) 不是next 较大双精度的距离。但是,正如@Pascal 所说,似乎没有ulp(1.0) 的一般定义,所以也许我的问题就在这里。还是没看懂……
  • @FranzD。 “定义”给出的值是“最小可表示的双精度值 x 使得 1.0 + x != 1.0”不是 2 的幂,因此它不是数学 ulp 的任何定义的 ulp 函数的结果,也不是对于名为 ulp 的 Java 方法。
  • @Pascal:嗯?谁说 ulp(x) 只返回 2 的幂?我只说输入 x = 1.0 是 2 的幂,所以 ulp 的结果可能是可疑的。
  • @FranzD。我说 ulp 返回 2 的幂。你不必相信我。你读过ens-lyon.fr/LIP/Pub/Rapports/RR/RR2005/RR2005-09.pdf 吗?
猜你喜欢
  • 2018-02-09
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-20
  • 2021-06-30
  • 1970-01-01
相关资源
最近更新 更多