Author: erans
Date: Mon Nov 26 13:25:27 2012
New Revision: 1413600

URL: http://svn.apache.org/viewvc?rev=1413600&view=rev
Log:
MATH-905
Avoid overflow on the whole range covered by the equivalent functions in
the standard "Math" class.

Modified:
    
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/FastMath.java
    
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/FastMathTest.java

Modified: 
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/FastMath.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/FastMath.java?rev=1413600&r1=1413599&r2=1413600&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/FastMath.java
 (original)
+++ 
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/util/FastMath.java
 Mon Nov 26 13:25:27 2012
@@ -78,6 +78,8 @@ import java.io.PrintStream;
  * @since 2.2
  */
 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. */
     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,
       // exp(-z) can be ignored in comparison with exp(z)
 
-      if (x > 20.0) {
-          return exp(x)/2.0;
-      }
-
-      if (x < -20) {
-          return exp(-x)/2.0;
+      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);
+          }
+      } 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);
+          }
       }
 
-      double hiPrec[] = new double[2];
+      final double hiPrec[] = new double[2];
       if (x < 0.0) {
           x = -x;
       }
@@ -449,12 +461,22 @@ public class FastMath {
       // for values of z larger than about 20,
       // exp(-z) can be ignored in comparison with exp(z)
 
-      if (x > 20.0) {
-          return exp(x)/2.0;
-      }
-
-      if (x < -20) {
-          return -exp(-x)/2.0;
+      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);
+          }
+      } 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 == 0) {

Modified: 
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/FastMathTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/FastMathTest.java?rev=1413600&r1=1413599&r2=1413600&view=diff
==============================================================================
--- 
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/FastMathTest.java
 (original)
+++ 
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/util/FastMathTest.java
 Mon Nov 26 13:25:27 2012
@@ -158,6 +158,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
     public void testHyperbolicInverses() {
         double maxErr = 0;
         for (double x = -30; x < 30; x += 0.01) {


Reply via email to