JDK-8229259 : Math.hypot result not always within 1 ulp of exact result
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Priority: P3
  • Status: Closed
  • Resolution: Won't Fix
  • Submitted: 2019-08-07
  • Updated: 2023-10-25
  • Resolved: 2023-10-25
Related Reports
Relates :  
Relates :  
Description
(As reported by Hans Boehm (boehm@acm.org))

Math.hypot says:

"""The computed result must be within 1 ulp of the exact result."""

This means that if the exact result is between adjacent floating point numbers f1 and f2, then the result must be either f1 or f2. I can exhibit a pair of doubles for which this is not the case under OpenJDK 8 through 14.. It's only off by a little.

public class BasicHypotBug {
    public static void main(String[] args) {
        double arg1 = 2.6718173667255144E-307;
        double arg2 = 1.1432573432387167E-308;  // Slightly denormal
        // Actual result:
        // 2.6742622187558112649563688387773761534655742E-307
        double resultUpperBound = 2.6742622187558113E-307;
        // Actual value of resultUpperBound:
        // 2.6742622187558112997755783398169632667381058E-307
        double result = Math.hypot(arg1, arg2);
        if (result > resultUpperBound) {
            System.out.println(
                "Result too large: " + result + " > "  + resultUpperBound);
        }
    }
}

=>
Result too large: 2.6742622187558117E-307 > 2.6742622187558113E-307

Comments
Closing as will not fix. JDK-8316688 widened the allowable bound to account for a more accurate estimate of the error of FDLIBM hypot. If there is a larger effort to revisit the math library implementation, new algorithms for Math.hypot could be considered.
25-10-2023

Also see the recent paper: "Accuracy of Mathematical Functions in Single, Double, Double Extended, and Quadruple Precision" by Brian Gladman, Vincenzo Innocente and Paul Zimmermann https://members.loria.fr/PZimmermann/papers/accuracy.pdf That paper reports an FDLIBM-derivied hypot as having a max error of 1.21 ulps.
25-10-2023

(The paper by Böhm cited above has a reference to this issue at the end of section 7.3.) According to the post by [~bpb], the fdlibm 5.3 library has the same issue, contrary to what is claimed in the C implementation (https://netlib.org/fdlibm/e_hypot.c). Since the definitions in j.l.StrictMath are tied to that library in terms of results, they cannot be changed easily. However, the "1 ulp" restriction in the JDK is only mandated by j.l.Math, not by j.l.StrictMath. Thus, there is some latitude in changing the implementation in j.l.Math to achieve the "1 ulp" bound.
05-12-2022

See also Hans-J. Boehm, "Towards an API for the Real Numbers," PLDI June 2020. https://dl.acm.org/doi/pdf/10.1145/3385412.3386037
03-12-2022

It looks like the native fdlibm function was still used in JDK 8u and this gives an incorrect result bpb:tmp{56}$ /Library/Java/JavaVirtualMachines/jdk1.8.0_281.jdk/Contents/Home/bin/javac BasicHypotBug.java bpb:tmp{57}$ /Library/Java/JavaVirtualMachines/jdk1.8.0_281.jdk/Contents/Home/bin/java BasicHypotBug Result too large: 2.6742622187558117E-307 > 2.6742622187558113E-307 which suggests that the fdlibm routine is incorrect in its claim about 1 ulp accuracy.
18-05-2022

According to the original reporter Hans Boehm, the specific case illustrated above is the only one he encountered, and it was by pure chance while testing against slowly computed reference results in the context of another work. Math.hypot() simply invokes StrictMath.hypot(), which, in turn, invokes a Java translation of the corresponding fdlibm routine. Either the fdlibm routine is incorrect in its claim about 1 ulp accuracy, or the translation is not 100% correct.
18-05-2022