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));
}
}