You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2019/12/07 02:27:46 UTC

[commons-numbers] 04/04: Update tanh() to perform edge checks on twice the imaginary value.

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-numbers.git

commit 69a69afd8e4c5bbe4b3eef60acbe2d9637d5cfa7
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Sat Dec 7 02:25:41 2019 +0000

    Update tanh() to perform edge checks on twice the imaginary value.
    
    This matches the reference test result.
---
 .../apache/commons/numbers/complex/Complex.java    | 33 ++++++++++++++--------
 .../commons/numbers/complex/CReferenceTest.java    | 18 ++++++------
 2 files changed, 31 insertions(+), 20 deletions(-)

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 aa2bc2a..f8af35e 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
@@ -1785,27 +1785,38 @@ public final class Complex implements Serializable  {
      * @return the hyperbolic tangent of the complex number
      */
     private static Complex tanh(double real, double imaginary, ComplexConstructor constructor) {
-        // TODO: Should these checks be made on real2 and imaginary2?
-        // Compare to other library implementations.
-        //
         // Math.cos and Math.sin return NaN for infinity.
-        // Math.cosh returns positive infinity for infinity.
-        // Math.sinh returns the input infinity for infinity.
+        // Perform edge-condition checks on the twice the imaginary value.
+        // This handles very big imaginary numbers as infinite.
+
+        final double imaginary2 = 2 * imaginary;
 
         if (Double.isFinite(real)) {
-            if (Double.isFinite(imaginary)) {
-                final double real2 = 2 * real;
-                final double imaginary2 = 2 * imaginary;
+            if (Double.isFinite(imaginary2)) {
+                double real2 = 2 * real;
+
+                // Math.cosh returns positive infinity for infinity.
+                // cosh -> inf
                 final double d = Math.cosh(real2) + Math.cos(imaginary2);
 
-                return constructor.create(Math.sinh(real2) / d,
+                // Math.sinh returns the input infinity for infinity.
+                // sinh -> inf for positive x; else -inf
+                final double sinhRe2 = Math.sinh(real2);
+
+                // Avoid inf / inf
+                if (Double.isInfinite(sinhRe2) && Double.isInfinite(d)) {
+                    // Fall-through to the result if infinite
+                    return constructor.create(Math.copySign(1, real), Math.copySign(0, Math.sin(imaginary2)));
+                }
+                return constructor.create(sinhRe2 / d,
                                           Math.sin(imaginary2) / d);
             }
+            // imaginary is infinite or NaN
             return NAN;
         }
         if (Double.isInfinite(real)) {
-            if (Double.isFinite(imaginary)) {
-                return constructor.create(Math.copySign(1, real), Math.copySign(0, Math.sin(2 * imaginary)));
+            if (Double.isFinite(imaginary2)) {
+                return constructor.create(Math.copySign(1, real), Math.copySign(0, Math.sin(imaginary2)));
             }
             // imaginary is infinite or NaN
             return constructor.create(Math.copySign(1, real), Math.copySign(0, imaginary));
diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java
index 2c268f0..5ec32da 100644
--- a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java
+++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java
@@ -473,11 +473,11 @@ public class CReferenceTest {
 
     @Test
     public void testTanh() {
-//        assertComplex(-1e+308, 0.0, Complex::tanh, -1, -0.0);
-//        assertComplex(-1e+308, 0.5, Complex::tanh, -1, 0.0);
-//        assertComplex(-1e+308, 1, Complex::tanh, -1, 0.0);
-//        assertComplex(-1e+308, 2, Complex::tanh, -1, 0.0);
-//        assertComplex(-1e+308, 1e+308, Complex::tanh, nan, nan);
+        assertComplex(-1e+308, 0.0, Complex::tanh, -1, -0.0);
+        assertComplex(-1e+308, 0.5, Complex::tanh, -1, 0.0);
+        assertComplex(-1e+308, 1, Complex::tanh, -1, 0.0);
+        assertComplex(-1e+308, 2, Complex::tanh, -1, 0.0);
+        assertComplex(-1e+308, 1e+308, Complex::tanh, nan, nan);
         assertComplex(-2, 0.0, Complex::tanh, -0.9640275800758169, 0.0);
         assertComplex(-2, 0.5, Complex::tanh, -0.97994084996173814, 0.030215987322877575);
         assertComplex(-2, 1, Complex::tanh, -1.0147936161466335, 0.033812826079896691);
@@ -518,10 +518,10 @@ public class CReferenceTest {
         assertComplex(2, 1, Complex::tanh, 1.0147936161466335, 0.033812826079896691);
         assertComplex(2, 2, Complex::tanh, 1.0238355945704727, -0.028392952868232287);
         assertComplex(2, 1e+308, Complex::tanh, nan, nan);
-//        assertComplex(1e+308, 0.0, Complex::tanh, 1, -0.0);
-//        assertComplex(1e+308, 0.5, Complex::tanh, 1, 0.0);
-//        assertComplex(1e+308, 1, Complex::tanh, 1, 0.0);
-//        assertComplex(1e+308, 2, Complex::tanh, 1, 0.0);
+        assertComplex(1e+308, 0.0, Complex::tanh, 1, -0.0);
+        assertComplex(1e+308, 0.5, Complex::tanh, 1, 0.0);
+        assertComplex(1e+308, 1, Complex::tanh, 1, 0.0);
+        assertComplex(1e+308, 2, Complex::tanh, 1, 0.0);
         assertComplex(1e+308, 1e+308, Complex::tanh, nan, nan);
     }