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/10 14:59:35 UTC

[commons-numbers] 05/05: Improve CReferenceTest equality assertions and debug reporting.

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 e135bae831ace9d237a12af261abc4ff616bd41a
Author: aherbert <ah...@apache.org>
AuthorDate: Tue Dec 10 14:59:25 2019 +0000

    Improve CReferenceTest equality assertions and debug reporting.
    
    Changed a few max ULPs for some of the tests.
---
 .../commons/numbers/complex/CReferenceTest.java    | 100 ++++++++++++++++-----
 1 file changed, 78 insertions(+), 22 deletions(-)

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 d122235..caddc73 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
@@ -37,6 +37,11 @@ import java.util.function.UnaryOperator;
  *    ISO/IEC 9899 - Programming languages - C</a>
  */
 public class CReferenceTest {
+    /** Positive zero bits. */
+    private static final long POSITIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(+0.0);
+    /** Negative zero bits. */
+    private static final long NEGATIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(-0.0);
+
     /**
      * The maximum units of least precision (ULPs) allowed between values.
      * This is a global setting used to override individual test settings for ULPs as follows:
@@ -50,26 +55,42 @@ public class CReferenceTest {
      * setting takes precedence.
      * </ul>
      *
-     * <p>During testing if the difference between an expected and actual result is greater in
-     * magnitude than the current ULPS then this is considered an error. If the maximum ULPs is
-     * positive then an assertion error is raised. If negative then the error is printed to
-     * System out. This allows reporting of large deviations between the library and the
-     * reference data.
+     * <p>During testing the difference between an expected and actual result is specified by the
+     * count of numbers in the range between them lower end exclusive and upper end inclusive.
+     * Two equal numbers have a count of 0. The next adjacent value has a count of 1.
+     *
+     * <p>If the configured ULPs is positive then the test is
+     * asserted using: {@code delta <= maxUlps}.
+     *
+     * <p>If the configured ULPs is negative the test is asserted using:
+     * {@code delta <= (|maxUlps|-1)}. This allows setting a ULPs of -1 to indicate
+     * maximum ULPs = 0 but flagging the assertion for special processing.
+     *
+     * <p>If the maximum ULPs is positive then an assertion error is raised.
+     * If negative then the error is printed to System out. This allows reporting of large
+     * deviations between the library and the reference data.
      *
      * <p>In a standard use-case all tests will have a configured positive maximum ULPs to
      * pass the current test data. The global setting can be set to a negative value to allow
-     * reporting of errors larger in magnitude to the console.
+     * reporting of errors larger in magnitude to the console. Setting -1 will output all
+     * differences. Setting -2 will output only those with a value between the two numbers,
+     * i.e. the numbers are not the same to floating point roundoff.
      *
      * <p>Setting the global maximum ULPs to negative has the second effect of loading all
-     * data that has been flagged in data files. Otherwise this data is ignored by testing and
-     * printed to System out.
+     * data that has been flagged in data files using the {@code ;} character.
+     * Otherwise this data is ignored by testing and printed to System out.
      */
     private static long globalMaxUlps = 0;
 
+    /** Set this to true to report all deviations to System out when the maximum ULPs is negative. */
+    private static boolean reportAllDeviations = false;
+
     /**
      * Assert the two numbers are equal within the provided units of least precision.
      * The maximum count of numbers allowed between the two values is {@code maxUlps - 1}.
      *
+     * <p>Numbers must have the same sign. Thus -0.0 and 0.0 are never equal.
+     *
      * @param msg the failure message
      * @param expected the expected
      * @param actual the actual
@@ -78,18 +99,49 @@ public class CReferenceTest {
     private static void assertEquals(Supplier<String> msg, double expected, double actual, long maxUlps) {
         final long e = Double.doubleToLongBits(expected);
         final long a = Double.doubleToLongBits(actual);
-        final long delta = Math.abs(e - a);
-        if (delta > Math.abs(maxUlps)) {
-            // DEBUG:
-            if (maxUlps < 0) {
-                // CHECKSTYLE: stop Regex
-                System.out.printf("%s: expected <%s> != actual <%s> (ulps=%d)%n",
-                        msg.get(), expected, actual, delta);
-                // CHECKSTYLE: resume Regex
+
+        // Code adapted from Precision#equals(double, double, int) so we maintain the delta
+        // for the message.
+
+        long delta;
+        boolean equal;
+        if (e == a) {
+            // Binary equal
+            equal = true;
+            delta = 0;
+        } else if ((a ^ e) < 0L) {
+            // Opposite signs are never equal.
+            equal = false;
+            // The difference is the count of numbers between each and zero.
+            // This may overflow but we report it using an unsigned formatter.
+            if (a < e) {
+                delta = (e - POSITIVE_ZERO_DOUBLE_BITS) + (a - NEGATIVE_ZERO_DOUBLE_BITS) + 1;
             } else {
-                Assertions.fail(String.format("%s: expected <%s> != actual <%s> (ulps=%d)",
-                        msg.get(), expected, actual, delta));
+                delta = (a - POSITIVE_ZERO_DOUBLE_BITS) + (e - NEGATIVE_ZERO_DOUBLE_BITS) + 1;
             }
+        } else {
+            delta = Math.abs(e - a);
+            // Allow input of a negative maximum ULPs
+            equal = delta <= ((maxUlps < 0) ? (-maxUlps - 1) : maxUlps);
+        }
+
+        // DEBUG:
+        if (maxUlps < 0) {
+            // CHECKSTYLE: stop Regex
+            if (!equal) {
+                System.out.printf("%s: expected <%s> != actual <%s> (ulps=%s)%n",
+                        msg.get(), expected, actual, Long.toUnsignedString(delta));
+            } else if (reportAllDeviations) {
+                System.out.printf("%s: expected <%s> == actual <%s> (ulps=0)%n",
+                    msg.get(), expected, actual);
+            }
+            // CHECKSTYLE: resume Regex
+            return;
+        }
+
+        if (!equal) {
+            Assertions.fail(String.format("%s: expected <%s> != actual <%s> (ulps=%s)",
+                    msg.get(), expected, actual, Long.toUnsignedString(delta)));
         }
     }
 
@@ -100,6 +152,8 @@ public class CReferenceTest {
      * precision. The maximum count of numbers allowed between the two values is
      * {@code maxUlps - 1}.
      *
+     * <p>Numbers must have the same sign. Thus -0.0 and 0.0 are never equal.
+     *
      * @param c Input number.
      * @param name the operation name
      * @param operation the operation
@@ -121,6 +175,8 @@ public class CReferenceTest {
      * precision. The maximum count of numbers allowed between the two values is
      * {@code maxUlps - 1}.
      *
+     * <p>Numbers must have the same sign. Thus -0.0 and 0.0 are never equal.
+     *
      * @param c1 First number.
      * @param c2 Second number.
      * @param name the operation name
@@ -222,13 +278,13 @@ public class CReferenceTest {
     @Test
     public void testAsinh() {
         // Odd function: negative real cases defined by positive real cases
-        assertOperation("asinh", Complex::asinh, 2);
+        assertOperation("asinh", Complex::asinh, 1);
     }
 
     @Test
     public void testAtanh() {
         // Odd function: negative real cases defined by positive real cases
-        assertOperation("atanh", Complex::atanh, 26);
+        assertOperation("atanh", Complex::atanh, 17);
     }
 
     @Test
@@ -271,11 +327,11 @@ public class CReferenceTest {
 
     @Test
     public void testDivide() {
-        assertBiOperation("divide", Complex::divide, 0);
+        assertBiOperation("divide", Complex::divide, 7);
     }
 
     @Test
     public void testPowComplex() {
-        assertBiOperation("pow", Complex::pow, 17);
+        assertBiOperation("pow", Complex::pow, 9);
     }
 }