Optimise x2y2m1 using assumptions on the range of x and y.
diff --git a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java
index c8cafa6..c4f2727 100644
--- a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java
+++ b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java
@@ -2301,13 +2301,14 @@
// Otherwise there can be serious cancellation and the relative error in the real part
// could be enormous.
+ final double xx = x * x;
final double yy = y * y;
// Modify to use high precision before the threshold set by Hull et al.
// This is to preserve the monotonic output of the computation at the switch.
// Set the threshold when x^2 + y^2 is above 0.5 thus subtracting 1 results in a number
// that can be expressed with a higher precision than any number in the range 0.5-1.0
// due to the variable exponent used below 0.5.
- if (x < 1 && x * x + yy > 0.5) {
+ if (x < 1 && xx + yy > 0.5) {
// Large relative error.
// This does not use o.a.c.numbers.LinearCombination.value(x, x, y, y, 1, -1).
// It is optimised knowing that:
@@ -2319,18 +2320,16 @@
// the summation using a double-length summation algorithm.
// Split x and y as one 26 bits number and one 27 bits number
- final double xHigh = splitHigh(x);
- final double xLow = x - xHigh;
- final double yHigh = splitHigh(y);
- final double yLow = y - yHigh;
+ final double xHigh = splitHigh(x);
+ final double xLow = x - xHigh;
+ final double yHigh = splitHigh(y);
+ final double yLow = y - yHigh;
// Accurate split multiplication x * x and y * y
- final double x2High = x * x;
- final double x2Low = squareRoundOff(xLow, xHigh, x2High);
- final double y2High = y * y;
- final double y2Low = squareRoundOff(yLow, yHigh, y2High);
+ final double x2Low = squareRoundOff(xLow, xHigh, xx);
+ final double y2Low = squareRoundOff(yLow, yHigh, yy);
- return sumx2y2m1(x2High, x2Low, y2High, y2Low);
+ return sumx2y2m1(xx, x2Low, yy, y2Low);
}
return (x - 1) * (x + 1) + yy;
}
@@ -2379,7 +2378,8 @@
/**
* Sum x^2 + y^2 - 1.
*
- * <p>Implement Dekker's add2 sum under the assumption that {@code |x| >= |y|}.
+ * <p>Implement Dekker's add2 sum under the assumption that {@code |x| >= |y|},
+ * {@code 0.5 < |x| < 1} and {@code x^2 + y^2 > 0.5}.
*
* @param x2High High part of x^2.
* @param x2Low Low part of x^2.
@@ -2392,14 +2392,15 @@
// See Dekker (1971) Numerische Mathematik 18, p.240.
double r = x2High + y2High;
// Assume |x| > |y|
- double s = x2High - r + y2High + y2Low + x2Low;
+ final double s = x2High - r + y2High + y2Low + x2Low;
final double z = r + s;
final double zz = r - z + s;
// Repeat with (z, zz) add (-1, 0) to create the upper part of the
// double length scalar product x^2 + y^2 - 1.
- r = z - 1;
- s = z - r - 1 + zz;
- return r + s;
+ // Note however that this method is called when 0.5<|x|<1, |y|<|x| and x^2+y^2>0.5 so
+ // z is in the range (0.5, 2). In all cases z-1 is exact with no round-off term so
+ // we can skip a second add2 split addition.
+ return z - 1 + zz;
}
/**