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 <[email protected]> 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); } }
