Java: Add/Subtracting Math.ulp() vs. Math.nextAfter()

2022-09-04 20:49:56

我试图更详细地理解Java中的浮点运算。如果我正确阅读了文档,则以下情况适用于任何给定的双x:

x - Math.ulp(x) == Math.nextAfter(x, Double.NEGATIVE_INFINITY);
x + Math.ulp(x) == Math.nextAfter(x, Double.POSITIVE_INFINITY);

问题:情况是否总是如此,还是存在一些结果会有所不同的特殊情况?


答案 1

此程序:

public class Test {
  public static void main(String[] args) {
    double x = 1;
    System.out.println(x - Math.ulp(x) == Math.nextAfter(x, Double.NEGATIVE_INFINITY));
    System.out.println(x + Math.ulp(x) == Math.nextAfter(x, Double.POSITIVE_INFINITY));
  }
}

输出:

false
true

连续双精度值之间的差异在 2 的每个正常整数幂(包括 1.0)下发生变化。其中一个测试必须失败,因为它假设差异恒定。Math.ulp(double) 被定义为返回“这个浮点值和下一个更大的双精度值之间的正距离”,因此当距离不同时,减法命题是假的。


答案 2

我认为要检查的直接情况是0,+无穷大,-无穷大和NaN:

static void check(double x) {
  double a, b;
  System.out.printf(
      "%9s %9s %23s %5s%n",
      x, a = x - Math.ulp(x), b = Math.nextAfter(x, Double.NEGATIVE_INFINITY), a == b);
  System.out.printf(
      "%9s %9s %23s %5s%n",
      x, a = x + Math.ulp(x), b = Math.nextAfter(x, Double.POSITIVE_INFINITY), a == b);
  System.out.println();
}

public static void main(String[] args) throws java.lang.Exception {
  check(0);
  check(Double.POSITIVE_INFINITY);
  check(Double.NEGATIVE_INFINITY);
  check(Double.NaN);
}

Ideone demo

输出:

      0.0 -4.9E-324               -4.9E-324  true
      0.0  4.9E-324                4.9E-324  true

 Infinity       NaN  1.7976931348623157E308 false
 Infinity  Infinity                Infinity  true

-Infinity -Infinity               -Infinity  true
-Infinity       NaN -1.7976931348623157E308 false

      NaN       NaN                     NaN false
      NaN       NaN                     NaN false

在这种情况下,表达式不相等并不奇怪(根据NaN的定义);但这些表达式对于 +无穷大和 -无穷大也不是真的(请参阅最后一列)。NaN

此答案并非旨在提供有问题的值的详尽列表,而是表明确实存在一些有问题的值。