added fast cubic root computation
JIRA: MATH-375 git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/branches/MATH_2_X@996166 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f5bec5767b
commit
b5d04e247f
|
@ -154,6 +154,13 @@ public class FastMath {
|
||||||
*/
|
*/
|
||||||
private static final double EIGHTHES[] = {0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625};
|
private static final double EIGHTHES[] = {0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625};
|
||||||
|
|
||||||
|
/* Table of 2^((n+2)/3) */
|
||||||
|
private static final double CBRTTWO[] = { 0.6299605249474366,
|
||||||
|
0.7937005259840998,
|
||||||
|
1.0,
|
||||||
|
1.2599210498948732,
|
||||||
|
1.5874010519681994 };
|
||||||
|
|
||||||
// Initialize tables
|
// Initialize tables
|
||||||
static {
|
static {
|
||||||
int i;
|
int i;
|
||||||
|
@ -212,14 +219,6 @@ public class FastMath {
|
||||||
return Math.sqrt(a);
|
return Math.sqrt(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compute the cubic root of a number.
|
|
||||||
* @param a number on which evaluation is done
|
|
||||||
* @return cubic root of a
|
|
||||||
*/
|
|
||||||
public static double cbrt(final double a) {
|
|
||||||
return Math.cbrt(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Compute the hyperbolic cosine of a number.
|
/** Compute the hyperbolic cosine of a number.
|
||||||
* @param a number on which evaluation is done
|
* @param a number on which evaluation is done
|
||||||
* @return hyperbolic cosine of a
|
* @return hyperbolic cosine of a
|
||||||
|
@ -1020,11 +1019,6 @@ public class FastMath {
|
||||||
ya = aa + tmp - tmp;
|
ya = aa + tmp - tmp;
|
||||||
yb = aa - ya + ab;
|
yb = aa - ya + ab;
|
||||||
|
|
||||||
if (hiPrec != null) {
|
|
||||||
hiPrec[0] = ya;
|
|
||||||
hiPrec[1] = yb;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ya + yb;
|
return ya + yb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2886,6 +2880,90 @@ public class FastMath {
|
||||||
return atan(ra, rb, x<0);
|
return atan(ra, rb, x<0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Compute the cubic root of a number.
|
||||||
|
* @param a number on which evaluation is done
|
||||||
|
* @return cubic root of a
|
||||||
|
*/
|
||||||
|
public static double cbrt(double x) {
|
||||||
|
/* Convert input double to bits */
|
||||||
|
long inbits = Double.doubleToLongBits(x);
|
||||||
|
int exponent = (int) ((inbits >> 52) & 0x7ff) - 1023;
|
||||||
|
boolean subnormal = false;
|
||||||
|
|
||||||
|
if (exponent == -1023) {
|
||||||
|
if (x == 0) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subnormal, so normalize */
|
||||||
|
subnormal = true;
|
||||||
|
x *= 1.8014398509481984E16; // 2^54
|
||||||
|
inbits = Double.doubleToLongBits(x);
|
||||||
|
exponent = (int) ((inbits >> 52) & 0x7ff) - 1023;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exponent == 1024) {
|
||||||
|
// Nan or infinity. Don't care which.
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Divide the exponent by 3 */
|
||||||
|
int exp3 = exponent / 3;
|
||||||
|
|
||||||
|
/* p2 will be the nearest power of 2 to x with its exponent divided by 3 */
|
||||||
|
double p2 = Double.longBitsToDouble((inbits & 0x8000000000000000L) |
|
||||||
|
(long)(((exp3 + 1023) & 0x7ff)) << 52);
|
||||||
|
|
||||||
|
/* This will be a number between 1 and 2 */
|
||||||
|
final double mant = Double.longBitsToDouble((inbits & 0x000fffffffffffffL) | 0x3ff0000000000000L);
|
||||||
|
|
||||||
|
/* Estimate the cube root of mant by polynomial */
|
||||||
|
double est = -0.010714690733195933;
|
||||||
|
est = est * mant + 0.0875862700108075;
|
||||||
|
est = est * mant + -0.3058015757857271;
|
||||||
|
est = est * mant + 0.7249995199969751;
|
||||||
|
est = est * mant + 0.5039018405998233;
|
||||||
|
|
||||||
|
est *= CBRTTWO[exponent % 3 + 2];
|
||||||
|
|
||||||
|
// est should now be good to about 15 bits of precision. Do 2 rounds of
|
||||||
|
// Newton's method to get closer, this should get us full double precision
|
||||||
|
// Scale down x for the purpose of doing newtons method. This avoids over/under flows.
|
||||||
|
final double xs = x / (p2*p2*p2);
|
||||||
|
est += (xs - est*est*est) / (3*est*est);
|
||||||
|
est += (xs - est*est*est) / (3*est*est);
|
||||||
|
|
||||||
|
// Do one round of Newton's method in extended precision to get the last bit right.
|
||||||
|
double temp = est * 1073741824.0;
|
||||||
|
double ya = est + temp - temp;
|
||||||
|
double yb = est - ya;
|
||||||
|
|
||||||
|
double za = ya * ya;
|
||||||
|
double zb = ya * yb * 2.0 + yb * yb;
|
||||||
|
temp = za * 1073741824.0;
|
||||||
|
double temp2 = za + temp - temp;
|
||||||
|
zb += (za - temp2);
|
||||||
|
za = temp2;
|
||||||
|
|
||||||
|
zb = za * yb + ya * zb + zb * yb;
|
||||||
|
za = za * ya;
|
||||||
|
|
||||||
|
double na = xs - za;
|
||||||
|
double nb = -(na - xs + za);
|
||||||
|
nb -= zb;
|
||||||
|
|
||||||
|
est += (na+nb)/(3*est*est);
|
||||||
|
|
||||||
|
/* Scale by a power of two, so this is exact. */
|
||||||
|
est *= p2;
|
||||||
|
|
||||||
|
if (subnormal) {
|
||||||
|
est *= 3.814697265625E-6; // 2^-18
|
||||||
|
}
|
||||||
|
|
||||||
|
return est;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert degrees to radians, with error of less than 0.5 ULP
|
* Convert degrees to radians, with error of less than 0.5 ULP
|
||||||
* @param x angle in degrees
|
* @param x angle in degrees
|
||||||
|
|
|
@ -787,6 +787,28 @@ public class FastMathTest {
|
||||||
Assert.assertTrue("acos() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
|
Assert.assertTrue("acos() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCbrtAccuracy() {
|
||||||
|
double maxerrulp = 0.0;
|
||||||
|
|
||||||
|
for (int i=0; i<10000; i++) {
|
||||||
|
double x = ((generator.nextDouble() * 200.0) - 100.0) * generator.nextDouble();
|
||||||
|
|
||||||
|
double tst = FastMath.cbrt(x);
|
||||||
|
double ref = cbrt(field.newDfp(x)).toDouble();
|
||||||
|
double err = (tst - ref) / ref;
|
||||||
|
|
||||||
|
if (err != 0) {
|
||||||
|
double ulp = Math.abs(ref - Double.longBitsToDouble((Double.doubleToLongBits(ref) ^ 1)));
|
||||||
|
double errulp = field.newDfp(tst).subtract(cbrt(field.newDfp(x))).divide(field.newDfp(ulp)).toDouble();
|
||||||
|
//System.out.println(x+"\t"+tst+"\t"+ref+"\t"+err+"\t"+errulp);
|
||||||
|
maxerrulp = Math.max(maxerrulp, Math.abs(errulp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertTrue("cbrt() had errors in excess of " + MAX_ERROR_ULP + " ULP", maxerrulp < MAX_ERROR_ULP);
|
||||||
|
}
|
||||||
|
|
||||||
private Dfp cbrt(Dfp x) {
|
private Dfp cbrt(Dfp x) {
|
||||||
boolean negative=false;
|
boolean negative=false;
|
||||||
|
|
||||||
|
@ -938,6 +960,7 @@ public class FastMathTest {
|
||||||
time = System.currentTimeMillis() - time;
|
time = System.currentTimeMillis() - time;
|
||||||
System.out.println("FastMath.cos " + time + "\t" + x);
|
System.out.println("FastMath.cos " + time + "\t" + x);
|
||||||
|
|
||||||
|
x = 0;
|
||||||
time = System.currentTimeMillis();
|
time = System.currentTimeMillis();
|
||||||
for (int i = 0; i < numberOfRuns; i++)
|
for (int i = 0; i < numberOfRuns; i++)
|
||||||
x += StrictMath.acos(i / 10000000.0);
|
x += StrictMath.acos(i / 10000000.0);
|
||||||
|
@ -980,6 +1003,20 @@ public class FastMathTest {
|
||||||
System.out.println("FastMath.atan " + time + "\t" + x);
|
System.out.println("FastMath.atan " + time + "\t" + x);
|
||||||
|
|
||||||
x = 0;
|
x = 0;
|
||||||
|
time = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < numberOfRuns; i++)
|
||||||
|
x += StrictMath.cbrt(i / 1000000.0);
|
||||||
|
time = System.currentTimeMillis() - time;
|
||||||
|
System.out.print("StrictMath.cbrt " + time + "\t" + x + "\t");
|
||||||
|
|
||||||
|
x = 0;
|
||||||
|
time = System.currentTimeMillis();
|
||||||
|
for (int i = 0; i < numberOfRuns; i++)
|
||||||
|
x += FastMath.cbrt(i / 1000000.0);
|
||||||
|
time = System.currentTimeMillis() - time;
|
||||||
|
System.out.println("FastMath.cbrt " + time + "\t" + x);
|
||||||
|
|
||||||
|
x = 0;
|
||||||
time = System.currentTimeMillis();
|
time = System.currentTimeMillis();
|
||||||
for (int i = 0; i < numberOfRuns; i++)
|
for (int i = 0; i < numberOfRuns; i++)
|
||||||
x += StrictMath.expm1(-i / 100000.0);
|
x += StrictMath.expm1(-i / 100000.0);
|
||||||
|
|
Loading…
Reference in New Issue