Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm trying to understand floating point operations in Java in more detail. If I read the documentation correctly, the following holds for any given double x:

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

Question: Is this always the case or are there some exceptional cases in which results will differ?

like image 832
Frank Blaga Avatar asked Nov 14 '17 11:11

Frank Blaga


2 Answers

This program:

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

outputs:

false
true

The difference between consecutive doubles changes at each normal integer power of two, including 1.0. One of the tests must fail, because it assumes constant difference. Math.ulp(double) is defined to return "the positive distance between this floating-point value and the double value next larger in magnitude" so the subtract proposition is false when the distance is different.

like image 186
Patricia Shanahan Avatar answered Nov 19 '22 13:11

Patricia Shanahan


The immediate cases I'd think to check are 0, +infinity and -infinity and 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

Output:

      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

That the expressions aren't equal in the NaN case isn't surprising (by the definition of NaN); but these expressions also aren't true for +infinity and -infinity (see the last column).

This answer is not intended to provide an exhaustive list of problematic values, but rather to show that some problematic values do exist.

like image 4
Andy Turner Avatar answered Nov 19 '22 14:11

Andy Turner