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 2022/02/16 13:47:12 UTC

[commons-numbers] 02/04: Ensure Precision equals is symmetric

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 28337327ecb68421cd7e240fc7515bbb2c266070
Author: aherbert <ah...@apache.org>
AuthorDate: Wed Feb 16 12:32:30 2022 +0000

    Ensure Precision equals is symmetric
    
    Update the tests to use an interface that asserts the return value is
    the same when arguments are reversed.
---
 .../apache/commons/numbers/core/PrecisionTest.java | 138 +++++++++++++++------
 1 file changed, 99 insertions(+), 39 deletions(-)

diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/PrecisionTest.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/PrecisionTest.java
index 08a8820..81f88fc 100644
--- a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/PrecisionTest.java
+++ b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/PrecisionTest.java
@@ -29,57 +29,109 @@ import org.junit.jupiter.api.Test;
  */
 class PrecisionTest {
 
-    // Interfaces to allow testing equals variants with the same conditions.
+    // Interfaces to allow testing variants with the same conditions.
+    // All methods that should be tested using these interfaces as the
+    // symmetry is asserted.
+
+    @FunctionalInterface
+    private interface Equals {
+        default boolean equals(double a, double b) {
+            final boolean r = equalsImp(a, b);
+            Assertions.assertEquals(r, equalsImp(b, a),
+                () -> String.format("equals(%s, %s) != equals(%s, %s)", a, b, b, a));
+            return r;
+        }
+        boolean equalsImp(double a, double b);
+    }
 
     @FunctionalInterface
     private interface EqualsWithDelta {
-        boolean equals(double a, double b, double delta);
+        default boolean equals(double a, double b, double delta) {
+            final boolean r = equalsImp(a, b, delta);
+            Assertions.assertEquals(r, equalsImp(b, a, delta),
+                () -> String.format("equals(%s, %s, %s) != equals(%s, %s, %s)", a, b, delta, b, a, delta));
+            return r;
+        }
+        boolean equalsImp(double a, double b, double delta);
     }
 
     @FunctionalInterface
     private interface EqualsWithUlps {
-        boolean equals(double a, double b, int ulps);
+        default boolean equals(double a, double b, int ulps) {
+            final boolean r = equalsImp(a, b, ulps);
+            Assertions.assertEquals(r, equalsImp(b, a, ulps),
+                () -> String.format("equals(%s, %s, %d) != equals(%s, %s, %d)", a, b, ulps, b, a, ulps));
+            return r;
+        }
+        boolean equalsImp(double a, double b, int ulps);
+    }
+
+    @FunctionalInterface
+    private interface FloatEquals {
+        default boolean equals(float a, float b) {
+            final boolean r = equalsImp(a, b);
+            Assertions.assertEquals(r, equalsImp(b, a),
+                () -> String.format("equals(%s, %s) != equals(%s, %s)", a, b, b, a));
+            return r;
+        }
+        boolean equalsImp(float a, float b);
     }
 
     @FunctionalInterface
     private interface FloatEqualsWithDelta {
-        boolean equals(float a, float b, float delta);
+        default boolean equals(float a, float b, float delta) {
+            final boolean r = equalsImp(a, b, delta);
+            Assertions.assertEquals(r, equalsImp(b, a, delta),
+                () -> String.format("equals(%s, %s, %s) != equals(%s, %s, %s)", a, b, delta, b, a, delta));
+            return r;
+        }
+        boolean equalsImp(float a, float b, float delta);
     }
 
     @FunctionalInterface
     private interface FloatEqualsWithUlps {
-        boolean equals(float a, float b, int ulps);
+        default boolean equals(float a, float b, int ulps) {
+            final boolean r = equalsImp(a, b, ulps);
+            Assertions.assertEquals(r, equalsImp(b, a, ulps),
+                () -> String.format("equals(%s, %s, %d) != equals(%s, %s, %d)", a, b, ulps, b, a, ulps));
+            return r;
+        }
+        boolean equalsImp(float a, float b, int ulps);
     }
 
     @Test
     void testEqualsWithRelativeTolerance() {
-        Assertions.assertTrue(Precision.equalsWithRelativeTolerance(0d, 0d, 0d));
-        Assertions.assertTrue(Precision.equalsWithRelativeTolerance(0d, 1 / Double.NEGATIVE_INFINITY, 0d));
+        final EqualsWithDelta fun = Precision::equalsWithRelativeTolerance;
+
+        Assertions.assertTrue(fun.equals(0d, 0d, 0d));
+        Assertions.assertTrue(fun.equals(0d, 1 / Double.NEGATIVE_INFINITY, 0d));
 
         final double eps = 1e-14;
-        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(1.987654687654968, 1.987654687654988, eps));
-        Assertions.assertTrue(Precision.equalsWithRelativeTolerance(1.987654687654968, 1.987654687654987, eps));
-        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(1.987654687654968, 1.987654687654948, eps));
-        Assertions.assertTrue(Precision.equalsWithRelativeTolerance(1.987654687654968, 1.987654687654949, eps));
+        Assertions.assertFalse(fun.equals(1.987654687654968, 1.987654687654988, eps));
+        Assertions.assertTrue(fun.equals(1.987654687654968, 1.987654687654987, eps));
+        Assertions.assertFalse(fun.equals(1.987654687654968, 1.987654687654948, eps));
+        Assertions.assertTrue(fun.equals(1.987654687654968, 1.987654687654949, eps));
 
-        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(Precision.SAFE_MIN, 0.0, eps));
+        Assertions.assertFalse(fun.equals(Precision.SAFE_MIN, 0.0, eps));
 
-        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(1.0000000000001e-300, 1e-300, eps));
-        Assertions.assertTrue(Precision.equalsWithRelativeTolerance(1.00000000000001e-300, 1e-300, eps));
+        Assertions.assertFalse(fun.equals(1.0000000000001e-300, 1e-300, eps));
+        Assertions.assertTrue(fun.equals(1.00000000000001e-300, 1e-300, eps));
 
-        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(Double.NEGATIVE_INFINITY, 1.23, eps));
-        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(Double.POSITIVE_INFINITY, 1.23, eps));
+        Assertions.assertFalse(fun.equals(Double.NEGATIVE_INFINITY, 1.23, eps));
+        Assertions.assertFalse(fun.equals(Double.POSITIVE_INFINITY, 1.23, eps));
 
-        Assertions.assertTrue(Precision.equalsWithRelativeTolerance(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, eps));
-        Assertions.assertTrue(Precision.equalsWithRelativeTolerance(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, eps));
-        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, eps));
+        Assertions.assertTrue(fun.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, eps));
+        Assertions.assertTrue(fun.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, eps));
+        Assertions.assertFalse(fun.equals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, eps));
 
-        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(Double.NaN, 1.23, eps));
-        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(Double.NaN, Double.NaN, eps));
+        Assertions.assertFalse(fun.equals(Double.NaN, 1.23, eps));
+        Assertions.assertFalse(fun.equals(Double.NaN, Double.NaN, eps));
     }
 
     @Test
     void testEqualsIncludingNaN() {
+        final Equals fun = Precision::equalsIncludingNaN;
+
         double[] testArray = {
             Double.NaN,
             Double.POSITIVE_INFINITY,
@@ -89,11 +141,11 @@ class PrecisionTest {
         for (int i = 0; i < testArray.length; i++) {
             for (int j = 0; j < testArray.length; j++) {
                 if (i == j) {
-                    Assertions.assertTrue(Precision.equalsIncludingNaN(testArray[i], testArray[j]));
-                    Assertions.assertTrue(Precision.equalsIncludingNaN(testArray[j], testArray[i]));
+                    Assertions.assertTrue(fun.equals(testArray[i], testArray[j]));
+                    Assertions.assertTrue(fun.equals(testArray[j], testArray[i]));
                 } else {
-                    Assertions.assertFalse(Precision.equalsIncludingNaN(testArray[i], testArray[j]));
-                    Assertions.assertFalse(Precision.equalsIncludingNaN(testArray[j], testArray[i]));
+                    Assertions.assertFalse(fun.equals(testArray[i], testArray[j]));
+                    Assertions.assertFalse(fun.equals(testArray[j], testArray[i]));
                 }
             }
         }
@@ -196,6 +248,8 @@ class PrecisionTest {
 
     @Test
     void testFloatEqualsIncludingNaN() {
+        final FloatEquals fun = Precision::equalsIncludingNaN;
+
         float[] testArray = {
             Float.NaN,
             Float.POSITIVE_INFINITY,
@@ -205,11 +259,11 @@ class PrecisionTest {
         for (int i = 0; i < testArray.length; i++) {
             for (int j = 0; j < testArray.length; j++) {
                 if (i == j) {
-                    Assertions.assertTrue(Precision.equalsIncludingNaN(testArray[i], testArray[j]));
-                    Assertions.assertTrue(Precision.equalsIncludingNaN(testArray[j], testArray[i]));
+                    Assertions.assertTrue(fun.equals(testArray[i], testArray[j]));
+                    Assertions.assertTrue(fun.equals(testArray[j], testArray[i]));
                 } else {
-                    Assertions.assertFalse(Precision.equalsIncludingNaN(testArray[i], testArray[j]));
-                    Assertions.assertFalse(Precision.equalsIncludingNaN(testArray[j], testArray[i]));
+                    Assertions.assertFalse(fun.equals(testArray[i], testArray[j]));
+                    Assertions.assertFalse(fun.equals(testArray[j], testArray[i]));
                 }
             }
         }
@@ -509,6 +563,8 @@ class PrecisionTest {
 
     @Test
     void testMath475() {
+        final EqualsWithDelta fun = Precision::equals;
+
         final double a = 1.7976931348623182E16;
         final double b = Math.nextUp(a);
 
@@ -516,18 +572,20 @@ class PrecisionTest {
         // Because they are adjacent floating point numbers, "a" and "b" are
         // considered equal even though the allowed error is smaller than
         // their difference.
-        Assertions.assertTrue(Precision.equals(a, b, 0.5 * diff));
+        Assertions.assertTrue(fun.equals(a, b, 0.5 * diff));
 
         final double c = Math.nextUp(b);
         diff = Math.abs(a - c);
         // Because "a" and "c" are not adjacent, the tolerance is taken into
         // account for assessing equality.
-        Assertions.assertTrue(Precision.equals(a, c, diff));
-        Assertions.assertFalse(Precision.equals(a, c, Math.nextDown(1.0) * diff));
+        Assertions.assertTrue(fun.equals(a, c, diff));
+        Assertions.assertFalse(fun.equals(a, c, Math.nextDown(1.0) * diff));
     }
 
     @Test
     void testMath475Float() {
+        final FloatEqualsWithDelta fun = Precision::equals;
+
         final float a = 1.7976931348623182E16f;
         final float b = Math.nextUp(a);
 
@@ -535,14 +593,14 @@ class PrecisionTest {
         // Because they are adjacent floating point numbers, "a" and "b" are
         // considered equal even though the allowed error is smaller than
         // their difference.
-        Assertions.assertTrue(Precision.equals(a, b, 0.5f * diff));
+        Assertions.assertTrue(fun.equals(a, b, 0.5f * diff));
 
         final float c = Math.nextUp(b);
         diff = Math.abs(a - c);
         // Because "a" and "c" are not adjacent, the tolerance is taken into
         // account for assessing equality.
-        Assertions.assertTrue(Precision.equals(a, c, diff));
-        Assertions.assertFalse(Precision.equals(a, c, Math.nextDown(1.0f) * diff));
+        Assertions.assertTrue(fun.equals(a, c, diff));
+        Assertions.assertFalse(fun.equals(a, c, Math.nextDown(1.0f) * diff));
     }
 
     @Test
@@ -559,9 +617,11 @@ class PrecisionTest {
 
     @Test
     void testMath1127() {
-        Assertions.assertFalse(Precision.equals(2.0, -2.0, 1));
-        Assertions.assertTrue(Precision.equals(0.0, -0.0, 0));
-        Assertions.assertFalse(Precision.equals(2.0f, -2.0f, 1));
-        Assertions.assertTrue(Precision.equals(0.0f, -0.0f, 0));
+        final EqualsWithUlps fun = Precision::equals;
+        Assertions.assertFalse(fun.equals(2.0, -2.0, 1));
+        Assertions.assertTrue(fun.equals(0.0, -0.0, 0));
+        final FloatEqualsWithUlps fun2 = Precision::equals;
+        Assertions.assertFalse(fun2.equals(2.0f, -2.0f, 1));
+        Assertions.assertTrue(fun2.equals(0.0f, -0.0f, 0));
     }
 }