JDK-7130085 : Port fdlibm hypot to Java
  • Type: Sub-task
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 6u29
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2012-01-14
  • Updated: 2015-11-09
  • Resolved: 2015-09-23
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9 b84Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
Java HotSpot(TM) 64-Bit Server VM (build 14.0-b16, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
Math.hypot is excessively slow.  Running it in a simple timing loop gives a run time of about 800 nanoseconds on an i7 2.67 GHz.  The pure Java method provided below runs in 40 nanoseconds.


REPRODUCIBILITY :
This bug can be reproduced always.

CUSTOMER SUBMITTED WORKAROUND :
	/**
	 * <b>hypot</b>
	 * @param x
	 * @param y
	 * @return sqrt(x*x +y*y) without intermediate overflow or underflow.
	 * @Note Math.hypot is unnecessarily slow.  This returns the identical result to
	 * Math.hypot with reasonable run times (~40 nsec vs. 800 nsec).
	 * <p>The logic for computing z is copied from "Freely Distributable Math Library"
	 * fdlibm's e_hypot.c. This minimizes rounding error to provide 1 ulb accuracy.
	 */
	public static double hypot(double x, double y) {
		
		if (Double.isInfinite(x) || Double.isInfinite(y)) return Double.POSITIVE_INFINITY;
		if (Double.isNaN(x) || Double.isNaN(y)) return Double.NaN;

		x = Math.abs(x);
		y = Math.abs(y);
		
		if (x < y) {
			double d = x;
			x = y;
			y = d;
		}
		
		int xi = Math.getExponent(x);
		int yi = Math.getExponent(y);
		
		if (xi > yi + 27) return x;
		
		int bias = 0;
		if (xi > 510 || xi < -511) {
			bias = xi;
			x = Math.scalb(x, -bias);
			y = Math.scalb(y, -bias);
		}
		
		// translated from "Freely Distributable Math Library" e_hypot.c to minimize rounding errors
		double z = 0;
		if (x > 2*y) {
			double x1 = Double.longBitsToDouble(Double.doubleToLongBits(x) & 0xffffffff00000000L);
			double x2 = x - x1;
			z = Math.sqrt(x1*x1 + (y*y + x2*(x+x1)));
		} else {
			double t = 2 * x;
			double t1 = Double.longBitsToDouble(Double.doubleToLongBits(t) & 0xffffffff00000000L);
			double t2 = t - t1;
			double y1 = Double.longBitsToDouble(Double.doubleToLongBits(y) & 0xffffffff00000000L);
			double y2 = y - y1;
			double x_y = x - y;
			z = Math.sqrt(t1*y1 + (x_y*x_y + (t1*y2 + t2*y))); // Note: 2*x*y <= x*x + y*y
		}
		
		if (bias == 0) {
			return z;
		} else {
			return Math.scalb(z, bias);
		}
	}

Comments
7130085-patch-sequence.zip holds the iterations of changes leading up to the final patch for this fix.
28-09-2015

Review thread: http://mail.openjdk.java.net/pipermail/core-libs-dev/2015-September/035332.html
22-09-2015

Micro-benchmark testing on a MacBookPro5,3 confirms a ~38X performance improvement over the existing implementation.
17-01-2014

EVALUATION Port of the FDLIBM code to Java would avoid the JNI overhead.
17-01-2012