/* * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * Shared static test methods for numerical tests. Sharing these * helper test methods avoids repeated functions in the various test * programs. The test methods return 1 for a test failure and 0 for * success. The order of arguments to the test methods is generally * the test name, followed by the test arguments, the computed result, * and finally the expected result. */ package test.java.lang.Math; import org.testng.annotations.Test; import org.testng.Assert; public class Tests { private Tests() { } ; // do not instantiate public static String toHexString(float f) { if (!Float.isNaN(f)) { return Float.toHexString(f); } else { return "NaN(0x" + Integer.toHexString(Float.floatToRawIntBits(f)) + ")"; } } public static String toHexString(double d) { if (!Double.isNaN(d)) { return Double.toHexString(d); } else { return "NaN(0x" + Long.toHexString(Double.doubleToRawLongBits(d)) + ")"; } } /** * Return the floating-point value next larger in magnitude. */ public static double nextOut(double d) { if (d > 0.0) { return Math.nextUp(d); } else { return -Math.nextUp(-d); } } /** * Returns unbiased exponent of a {@code float}; for subnormal values, the number is treated as * if it were normalized. That is for all finite, non-zero, positive numbers * x, scalb(x, -ilogb(x)) is * always in the range [1, 2). *

* Special cases: *

* * @param d double number which exponent is to be extracted * @return unbiased exponent of the argument. */ public static int ilogb(double d) { int exponent = Math.getExponent(d); switch (exponent) { case Double.MAX_EXPONENT + 1: // NaN or infinity if (Double.isNaN(d)) { return (1 << 30); // 2^30 } else // infinite value { return (1 << 28); // 2^28 } case Double.MIN_EXPONENT - 1: // zero or subnormal if (d == 0.0) { return -(1 << 28); // -(2^28) } else { long transducer = Double.doubleToRawLongBits(d); /* * To avoid causing slow arithmetic on subnormals, * the scaling to determine when d's significand * is normalized is done in integer arithmetic. * (there must be at least one "1" bit in the * significand since zero has been screened out. */ // isolate significand bits transducer &= DoubleConsts.SIGNIF_BIT_MASK; Assert.assertNotEquals(transducer, 0L); // This loop is simple and functional. We might be // able to do something more clever that was faster; // e.g. number of leading zero detection on // (transducer << (# exponent and sign bits). while (transducer < (1L << (DoubleConsts.SIGNIFICAND_WIDTH - 1))) { transducer *= 2; exponent--; } exponent++; Assert.assertTrue(exponent >= Double.MIN_EXPONENT - (DoubleConsts.SIGNIFICAND_WIDTH - 1) && exponent < Double.MIN_EXPONENT); return exponent; } default: Assert.assertTrue(exponent >= Double.MIN_EXPONENT && exponent <= Double.MAX_EXPONENT); return exponent; } } /** * Returns unbiased exponent of a {@code float}; for subnormal values, the number is treated as * if it were normalized. That is for all finite, non-zero, positive numbers * x, scalb(x, -ilogb(x)) is * always in the range [1, 2). *

* Special cases: *

* * @param f floating-point number whose exponent is to be extracted * @return unbiased exponent of the argument. */ public static int ilogb(float f) { int exponent = Math.getExponent(f); switch (exponent) { case Float.MAX_EXPONENT + 1: // NaN or infinity if (Float.isNaN(f)) { return (1 << 30); // 2^30 } else // infinite value { return (1 << 28); // 2^28 } case Float.MIN_EXPONENT - 1: // zero or subnormal if (f == 0.0f) { return -(1 << 28); // -(2^28) } else { int transducer = Float.floatToRawIntBits(f); /* * To avoid causing slow arithmetic on subnormals, * the scaling to determine when f's significand * is normalized is done in integer arithmetic. * (there must be at least one "1" bit in the * significand since zero has been screened out. */ // isolate significand bits transducer &= FloatConsts.SIGNIF_BIT_MASK; Assert.assertNotEquals(transducer, 0L); // This loop is simple and functional. We might be // able to do something more clever that was faster; // e.g. number of leading zero detection on // (transducer << (# exponent and sign bits). while (transducer < (1 << (FloatConsts.SIGNIFICAND_WIDTH - 1))) { transducer *= 2; exponent--; } exponent++; Assert.assertTrue(exponent >= Float.MIN_EXPONENT - (FloatConsts.SIGNIFICAND_WIDTH - 1) && exponent < Float.MIN_EXPONENT); return exponent; } default: Assert.assertTrue(exponent >= Float.MIN_EXPONENT && exponent <= Float.MAX_EXPONENT); return exponent; } } /** * Returns {@code true} if the unordered relation holds between the two arguments. When two * floating-point values are unordered, one value is neither less than, equal to, nor greater * than the other. For the unordered relation to be true, at least one argument must be a * {@code NaN}. * * @param arg1 the first argument * @param arg2 the second argument * @return {@code true} if at least one argument is a NaN, {@code false} otherwise. */ public static boolean isUnordered(float arg1, float arg2) { return Float.isNaN(arg1) || Float.isNaN(arg2); } /** * Returns {@code true} if the unordered relation holds between the two arguments. When two * floating-point values are unordered, one value is neither less than, equal to, nor greater * than the other. For the unordered relation to be true, at least one argument must be a * {@code NaN}. * * @param arg1 the first argument * @param arg2 the second argument * @return {@code true} if at least one argument is a NaN, {@code false} otherwise. */ public static boolean isUnordered(double arg1, double arg2) { return Double.isNaN(arg1) || Double.isNaN(arg2); } public static void test(String testName, float input, boolean result, boolean expected) { Assert.assertEquals(result, expected, "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\texpected " + expected + "\n" + "\tgot " + result + ")."); } public static void test(String testName, double input, boolean result, boolean expected) { Assert.assertEquals(result, expected, "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\texpected " + expected + "\n" + "\tgot " + result + ")."); } public static void test(String testName, float input1, float input2, boolean result, boolean expected) { Assert.assertEquals(result, expected, "Failure for " + testName + ":\n" + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + input2 + "\t(" + toHexString(input2) + ")\n" + "\texpected " + expected + "\n" + "\tgot " + result + ")."); } public static void test(String testName, double input1, double input2, boolean result, boolean expected) { Assert.assertEquals(result, expected, "Failure for " + testName + ":\n" + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + input2 + "\t(" + toHexString(input2) + ")\n" + "\texpected " + expected + "\n" + "\tgot " + result + ")."); } public static void test(String testName, float input, int result, int expected) { Assert.assertEquals(result, expected, "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\texpected " + expected + "\n" + "\tgot " + result + ")."); } public static void test(String testName, double input, int result, int expected) { Assert.assertEquals(result, expected, "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\texpected " + expected + "\n" + "\tgot " + result + ")."); } public static void test(String testName, float input, float result, float expected) { Assert.assertEquals(result, expected, "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ")."); } public static void test(String testName, double input, double result, double expected) { Assert.assertEquals(Double.compare(expected, result), 0, "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ")."); } public static void test(String testName, float input1, double input2, float result, float expected) { Assert.assertEquals(Float.compare(expected, result), 0, "Failure for " + testName + ":\n" + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + input2 + "\t(" + toHexString(input2) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ")."); } public static void test(String testName, double input1, double input2, double result, double expected) { Assert.assertEquals(Double.compare(expected, result), 0, "Failure for " + testName + ":\n" + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + input2 + "\t(" + toHexString(input2) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ")."); } public static void test(String testName, float input1, int input2, float result, float expected) { Assert.assertEquals(Float.compare(expected, result), 0, "Failure for " + testName + ":\n" + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + input2 + "\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ")."); } public static void test(String testName, double input1, int input2, double result, double expected) { Assert.assertEquals(Double.compare(expected, result), 0, "Failure for " + testName + ":\n" + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + input2 + "\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ")."); } public static void test(String testName, float input1, float input2, float input3, float result, float expected) { Assert.assertEquals(Float.compare(expected, result), 0, "Failure for " + testName + ":\n" + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + input2 + "\t(" + toHexString(input2) + ") and" + input3 + "\t(" + toHexString(input3) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ")."); } public static void test(String testName, double input1, double input2, double input3, double result, double expected) { Assert.assertEquals(Double.compare(expected, result), 0, "Failure for " + testName + ":\n" + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + input2 + "\t(" + toHexString(input2) + ") and" + input3 + "\t(" + toHexString(input3) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ")."); } static int testUlpCore(double result, double expected, double ulps) { // We assume we won't be unlucky and have an inexact expected // be nextDown(2^i) when 2^i would be the correctly rounded // answer. This would cause the ulp size to be half as large // as it should be, doubling the measured error). if (Double.compare(expected, result) == 0) { return 0; // result and expected are equivalent } else { if (ulps == 0.0) { // Equivalent results required but not found return 1; } else { double difference = expected - result; if (isUnordered(expected, result) || Double.isNaN(difference) || // fail if greater than or unordered !(Math.abs(difference / Math.ulp(expected)) <= Math.abs(ulps))) { return 1; } else { return 0; } } } } // One input argument. public static void testUlpDiff(String testName, double input, double result, double expected, double ulps) { int code = testUlpCore(result, expected, ulps); Assert.assertEquals(code, 0, "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ");\n" + "\tdifference greater than ulp tolerance " + ulps); } // Two input arguments. public static void testUlpDiff(String testName, double input1, double input2, double result, double expected, double ulps) { int code = testUlpCore(result, expected, ulps); Assert.assertEquals(code, 0, "Failure for " + testName + ":\n" + "\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and " + input2 + "\t(" + toHexString(input2) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ");\n" + "\tdifference greater than ulp tolerance " + ulps); } // For a successful test, the result must be within the ulp bound of // expected AND the result must have absolute value less than or // equal to absBound. public static void testUlpDiffWithAbsBound(String testName, double input, double result, double expected, double ulps, double absBound) { int code = 0; // return code value if (!(StrictMath.abs(result) <= StrictMath.abs(absBound)) && !Double.isNaN(expected)) { code = 1; } else { code = testUlpCore(result, expected, ulps); } Assert.assertEquals(code, 0, "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ");\n" + "\tdifference greater than ulp tolerance " + ulps + " or the result has larger magnitude than " + absBound); } // For a successful test, the result must be within the ulp bound of // expected AND the result must have absolute value greater than // or equal to the lowerBound. public static void testUlpDiffWithLowerBound(String testName, double input, double result, double expected, double ulps, double lowerBound) { int code = 0; // return code value if (!(result >= lowerBound) && !Double.isNaN(expected)) { code = 1; } else { code = testUlpCore(result, expected, ulps); } Assert.assertEquals(code, 0, "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")" + "\n\texpected " + expected + "\t(" + toHexString(expected) + ")" + "\n\tgot " + result + "\t(" + toHexString(result) + ");" + "\ndifference greater than ulp tolerance " + ulps + " or result not greater than or equal to the bound " + lowerBound); } public static void testTolerance(String testName, double input, double result, double expected, double tolerance) { if (Double.compare(expected, result) != 0) { double difference = expected - result; Assert.assertFalse(isUnordered(expected, result) || Double.isNaN(difference) || // fail if greater than or unordered !(Math.abs((difference) / expected) <= StrictMath.pow(10, -tolerance)), "Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\texpected " + expected + "\t(" + toHexString(expected) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ");\n" + "\tdifference greater than tolerance 10^-" + tolerance); } } // For a successful test, the result must be within the upper and // lower bounds. public static void testBounds(String testName, double input, double result, double bound1, double bound2) { if (!((result >= bound1 && result <= bound2) || (result <= bound1 && result >= bound2))) { double lowerBound = Math.min(bound1, bound2); double upperBound = Math.max(bound1, bound2); Assert.fail("Failure for " + testName + ":\n" + "\tFor input " + input + "\t(" + toHexString(input) + ")\n" + "\tgot " + result + "\t(" + toHexString(result) + ");\n" + "\toutside of range\n" + "\t[" + lowerBound + "\t(" + toHexString(lowerBound) + "), " + upperBound + "\t(" + toHexString(upperBound) + ")]"); } } }