MATH-905
Avoid overflow on the whole range covered by the equivalent functions in the standard "Math" class. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1413600 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9e75d6faa9
commit
46ed0af1c2
|
@ -78,6 +78,8 @@ import java.io.PrintStream;
|
||||||
* @since 2.2
|
* @since 2.2
|
||||||
*/
|
*/
|
||||||
public class FastMath {
|
public class FastMath {
|
||||||
|
/** StrictMath.log(Double.MAX_VALUE): {@value} */
|
||||||
|
private static final double LOG_MAX_VALUE = StrictMath.log(Double.MAX_VALUE);
|
||||||
|
|
||||||
/** Archimede's constant PI, ratio of circle circumference to diameter. */
|
/** Archimede's constant PI, ratio of circle circumference to diameter. */
|
||||||
public static final double PI = 105414357.0 / 33554432.0 + 1.984187159361080883e-9;
|
public static final double PI = 105414357.0 / 33554432.0 + 1.984187159361080883e-9;
|
||||||
|
@ -389,15 +391,25 @@ public class FastMath {
|
||||||
// for numbers with magnitude 20 or so,
|
// for numbers with magnitude 20 or so,
|
||||||
// exp(-z) can be ignored in comparison with exp(z)
|
// exp(-z) can be ignored in comparison with exp(z)
|
||||||
|
|
||||||
if (x > 20.0) {
|
if (x > 20) {
|
||||||
return exp(x)/2.0;
|
if (x >= LOG_MAX_VALUE) {
|
||||||
|
// Avoid overflow (MATH-905).
|
||||||
|
final double t = exp(0.5 * x);
|
||||||
|
return (0.5 * t) * t;
|
||||||
|
} else {
|
||||||
|
return 0.5 * exp(x);
|
||||||
|
}
|
||||||
|
} else if (x < -20) {
|
||||||
|
if (x <= -LOG_MAX_VALUE) {
|
||||||
|
// Avoid overflow (MATH-905).
|
||||||
|
final double t = exp(-0.5 * x);
|
||||||
|
return (0.5 * t) * t;
|
||||||
|
} else {
|
||||||
|
return 0.5 * exp(-x);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x < -20) {
|
final double hiPrec[] = new double[2];
|
||||||
return exp(-x)/2.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double hiPrec[] = new double[2];
|
|
||||||
if (x < 0.0) {
|
if (x < 0.0) {
|
||||||
x = -x;
|
x = -x;
|
||||||
}
|
}
|
||||||
|
@ -449,12 +461,22 @@ public class FastMath {
|
||||||
// for values of z larger than about 20,
|
// for values of z larger than about 20,
|
||||||
// exp(-z) can be ignored in comparison with exp(z)
|
// exp(-z) can be ignored in comparison with exp(z)
|
||||||
|
|
||||||
if (x > 20.0) {
|
if (x > 20) {
|
||||||
return exp(x)/2.0;
|
if (x >= LOG_MAX_VALUE) {
|
||||||
|
// Avoid overflow (MATH-905).
|
||||||
|
final double t = exp(0.5 * x);
|
||||||
|
return (0.5 * t) * t;
|
||||||
|
} else {
|
||||||
|
return 0.5 * exp(x);
|
||||||
|
}
|
||||||
|
} else if (x < -20) {
|
||||||
|
if (x <= -LOG_MAX_VALUE) {
|
||||||
|
// Avoid overflow (MATH-905).
|
||||||
|
final double t = exp(-0.5 * x);
|
||||||
|
return (-0.5 * t) * t;
|
||||||
|
} else {
|
||||||
|
return -0.5 * exp(-x);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x < -20) {
|
|
||||||
return -exp(-x)/2.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
|
|
|
@ -157,6 +157,50 @@ public class FastMathTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMath905LargePositive() {
|
||||||
|
final double start = StrictMath.log(Double.MAX_VALUE);
|
||||||
|
final double endT = StrictMath.sqrt(2) * StrictMath.sqrt(Double.MAX_VALUE);
|
||||||
|
final double end = 2 * StrictMath.log(endT);
|
||||||
|
|
||||||
|
double maxErr = 0;
|
||||||
|
for (double x = start; x < end; x += 1e-3) {
|
||||||
|
final double tst = FastMath.cosh(x);
|
||||||
|
final double ref = Math.cosh(x);
|
||||||
|
maxErr = FastMath.max(maxErr, FastMath.abs(ref - tst) / FastMath.ulp(ref));
|
||||||
|
}
|
||||||
|
Assert.assertEquals(0, maxErr, 3);
|
||||||
|
|
||||||
|
for (double x = start; x < end; x += 1e-3) {
|
||||||
|
final double tst = FastMath.sinh(x);
|
||||||
|
final double ref = Math.sinh(x);
|
||||||
|
maxErr = FastMath.max(maxErr, FastMath.abs(ref - tst) / FastMath.ulp(ref));
|
||||||
|
}
|
||||||
|
Assert.assertEquals(0, maxErr, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMath905LargeNegative() {
|
||||||
|
final double start = -StrictMath.log(Double.MAX_VALUE);
|
||||||
|
final double endT = StrictMath.sqrt(2) * StrictMath.sqrt(Double.MAX_VALUE);
|
||||||
|
final double end = -2 * StrictMath.log(endT);
|
||||||
|
|
||||||
|
double maxErr = 0;
|
||||||
|
for (double x = start; x > end; x -= 1e-3) {
|
||||||
|
final double tst = FastMath.cosh(x);
|
||||||
|
final double ref = Math.cosh(x);
|
||||||
|
maxErr = FastMath.max(maxErr, FastMath.abs(ref - tst) / FastMath.ulp(ref));
|
||||||
|
}
|
||||||
|
Assert.assertEquals(0, maxErr, 3);
|
||||||
|
|
||||||
|
for (double x = start; x > end; x -= 1e-3) {
|
||||||
|
final double tst = FastMath.sinh(x);
|
||||||
|
final double ref = Math.sinh(x);
|
||||||
|
maxErr = FastMath.max(maxErr, FastMath.abs(ref - tst) / FastMath.ulp(ref));
|
||||||
|
}
|
||||||
|
Assert.assertEquals(0, maxErr, 3);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHyperbolicInverses() {
|
public void testHyperbolicInverses() {
|
||||||
double maxErr = 0;
|
double maxErr = 0;
|
||||||
|
|
Loading…
Reference in New Issue