7f02f07f75
Reviewed-by: rgiulietti
678 lines
30 KiB
Java
678 lines
30 KiB
Java
/*
|
|
* Copyright (c) 2003, 2024, 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.
|
|
*/
|
|
|
|
import java.util.function.DoubleBinaryOperator;
|
|
import java.util.function.DoubleUnaryOperator;
|
|
import java.util.function.DoubleToIntFunction;
|
|
import static java.lang.Double.longBitsToDouble;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
public class Tests {
|
|
private Tests(){}; // do not instantiate
|
|
|
|
// Used to create a NaN value at runtime; mark as volatile to foil
|
|
// compile-time constant folding.
|
|
static volatile double zero = 0.0;
|
|
|
|
private static final double PLATFORM_NAN = zero / zero;
|
|
|
|
public static final double[] NaNs = {
|
|
Double.NaN,
|
|
PLATFORM_NAN,
|
|
bitwiseNegate(PLATFORM_NAN),
|
|
// Exotic NaN bit patterns. Includes values that would
|
|
// *not* be considered a NaN if only the high-order
|
|
// 32-bits were examined.
|
|
longBitsToDouble(0x7FF0_0000_0000_0001L),
|
|
longBitsToDouble(0xFFF0_0000_0000_0001L),
|
|
longBitsToDouble(0x7FF8_5555_5555_5555L),
|
|
longBitsToDouble(0xFFF8_5555_5555_5555L),
|
|
longBitsToDouble(0x7FFF_FFFF_FFFF_FFFFL),
|
|
longBitsToDouble(0xFFFF_FFFF_FFFF_FFFFL),
|
|
longBitsToDouble(0x7FF0_0000_7FFF_FFFFL),
|
|
longBitsToDouble(0xFFF0_0000_7FFF_FFFFL),
|
|
longBitsToDouble(0x7FF0_Dead_Beef_0000L),
|
|
longBitsToDouble(0xFFF0_Dead_Beef_0000L),
|
|
longBitsToDouble(0x7FF0_Cafe_Babe_0000L),
|
|
longBitsToDouble(0xFFF0_Cafe_Babe_0000L),
|
|
};
|
|
|
|
public static double bitwiseNegate(double d) {
|
|
long SIGNBIT = 0x8000_0000_0000_0000L;
|
|
return longBitsToDouble(Double.doubleToRawLongBits(d) ^ SIGNBIT );
|
|
}
|
|
|
|
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 (d > 0.0) ? Math.nextUp(d) : Math.nextDown(d);
|
|
else
|
|
return Math.copySign(Double.MIN_VALUE, 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
|
|
* <i>x</i>, <code>scalb(<i>x</i>, -ilogb(<i>x</i>))</code> is
|
|
* always in the range [1, 2).
|
|
* <p>
|
|
* Special cases:
|
|
* <ul>
|
|
* <li> If the argument is NaN, then the result is 2<sup>30</sup>.
|
|
* <li> If the argument is infinite, then the result is 2<sup>28</sup>.
|
|
* <li> If the argument is zero, then the result is -(2<sup>28</sup>).
|
|
* </ul>
|
|
*
|
|
* @param d floating-point number whose 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(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( exponent >=
|
|
Double.MIN_EXPONENT - (DoubleConsts.SIGNIFICAND_WIDTH-1) &&
|
|
exponent < Double.MIN_EXPONENT);
|
|
return exponent;
|
|
}
|
|
|
|
default:
|
|
assert( 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
|
|
* <i>x</i>, <code>scalb(<i>x</i>, -ilogb(<i>x</i>))</code> is
|
|
* always in the range [1, 2).
|
|
* <p>
|
|
* Special cases:
|
|
* <ul>
|
|
* <li> If the argument is NaN, then the result is 2<sup>30</sup>.
|
|
* <li> If the argument is infinite, then the result is 2<sup>28</sup>.
|
|
* <li> If the argument is zero, then the result is -(2<sup>28</sup>).
|
|
* </ul>
|
|
*
|
|
* @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(transducer != 0);
|
|
|
|
// 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( exponent >=
|
|
Float.MIN_EXPONENT - (FloatConsts.SIGNIFICAND_WIDTH-1) &&
|
|
exponent < Float.MIN_EXPONENT);
|
|
return exponent;
|
|
}
|
|
|
|
default:
|
|
assert( 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 int test(String testName, float input,
|
|
boolean result, boolean expected) {
|
|
if (expected != result) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
|
|
"\texpected " + expected + "\n" +
|
|
"\tgot " + result + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static int test(String testName, double input,
|
|
boolean result, boolean expected) {
|
|
if (expected != result) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
|
|
"\texpected " + expected + "\n" +
|
|
"\tgot " + result + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static int test(String testName, float input1, float input2,
|
|
boolean result, boolean expected) {
|
|
if (expected != result) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and "
|
|
+ input2 + "\t(" + toHexString(input2) + ")\n" +
|
|
"\texpected " + expected + "\n" +
|
|
"\tgot " + result + ").");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public static int test(String testName, double input1, double input2,
|
|
boolean result, boolean expected) {
|
|
if (expected != result) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and "
|
|
+ input2 + "\t(" + toHexString(input2) + ")\n" +
|
|
"\texpected " + expected + "\n" +
|
|
"\tgot " + result + ").");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public static int test(String testName, float input,
|
|
int result, int expected) {
|
|
if (expected != result) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
|
|
"\texpected " + expected + "\n" +
|
|
"\tgot " + result + ").");
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public static int test(String testName,
|
|
double input,
|
|
DoubleToIntFunction func,
|
|
int expected) {
|
|
return test(testName, input, func.applyAsInt(input), expected);
|
|
}
|
|
|
|
public static int test(String testName, double input,
|
|
int result, int expected) {
|
|
if (expected != result) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
|
|
"\texpected " + expected + "\n" +
|
|
"\tgot " + result + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static int test(String testName, float input,
|
|
float result, float expected) {
|
|
if (Float.compare(expected, result) != 0 ) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
|
|
"\texpected " + expected + "\t(" + toHexString(expected) + ")\n" +
|
|
"\tgot " + result + "\t(" + toHexString(result) + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static int test(String testName,
|
|
double input,
|
|
DoubleUnaryOperator func,
|
|
double expected) {
|
|
return test(testName, input, func.applyAsDouble(input), expected);
|
|
}
|
|
|
|
public static int test(String testName, double input,
|
|
double result, double expected) {
|
|
if (Double.compare(expected, result ) != 0) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
|
|
"\texpected " + expected + "\t(" + toHexString(expected) + ")\n" +
|
|
"\tgot " + result + "\t(" + toHexString(result) + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static int test(String testName,
|
|
float input1, double input2,
|
|
float result, float expected) {
|
|
if (Float.compare(expected, result ) != 0) {
|
|
System.err.println("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) + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static int test(String testName,
|
|
double input1, double input2,
|
|
DoubleBinaryOperator func,
|
|
double expected) {
|
|
return test(testName, input1, input2, func.applyAsDouble(input1, input2), expected);
|
|
}
|
|
|
|
public static int test(String testName,
|
|
double input1, double input2,
|
|
double result, double expected) {
|
|
if (Double.compare(expected, result ) != 0) {
|
|
System.err.println("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) + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static int test(String testName,
|
|
float input1, int input2,
|
|
float result, float expected) {
|
|
if (Float.compare(expected, result ) != 0) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and "
|
|
+ input2 + "\n" +
|
|
"\texpected " + expected + "\t(" + toHexString(expected) + ")\n" +
|
|
"\tgot " + result + "\t(" + toHexString(result) + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static int test(String testName,
|
|
double input1, int input2,
|
|
double result, double expected) {
|
|
if (Double.compare(expected, result ) != 0) {
|
|
System.err.println("Failure for " + testName + ":\n" +
|
|
"\tFor inputs " + input1 + "\t(" + toHexString(input1) + ") and "
|
|
+ input2 + "\n" +
|
|
"\texpected " + expected + "\t(" + toHexString(expected) + ")\n" +
|
|
"\tgot " + result + "\t(" + toHexString(result) + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public static int test(String testName,
|
|
float input1, float input2, float input3,
|
|
float result, float expected) {
|
|
if (Float.compare(expected, result ) != 0) {
|
|
System.err.println("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) + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
@FunctionalInterface
|
|
public interface DoubleTernaryOperator {
|
|
double applyAsDouble(double input1, double input2, double input3);
|
|
}
|
|
|
|
public static int test(String testName,
|
|
double input1, double input2, double input3,
|
|
DoubleTernaryOperator func, double expected) {
|
|
return test(testName, input1, input2, input3, func.applyAsDouble(input1, input2, input3), expected);
|
|
|
|
}
|
|
|
|
public static int test(String testName,
|
|
double input1, double input2, double input3,
|
|
double result, double expected) {
|
|
if (Double.compare(expected, result ) != 0) {
|
|
System.err.println("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) + ").");
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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 int testUlpDiff(String testName, double input,
|
|
DoubleUnaryOperator func, double expected, double ulps) {
|
|
return testUlpDiff(testName, input, func.applyAsDouble(input), expected, ulps);
|
|
}
|
|
|
|
public static int testUlpDiff(String testName, double input,
|
|
double result, double expected, double ulps) {
|
|
int code = testUlpCore(result, expected, ulps);
|
|
if (code == 1) {
|
|
System.err.println("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);
|
|
}
|
|
return code;
|
|
}
|
|
|
|
// Two input arguments.
|
|
public static int testUlpDiff(String testName, double input1, double input2,
|
|
DoubleBinaryOperator func, double expected, double ulps) {
|
|
return testUlpDiff(testName, input1, input2, func.applyAsDouble(input1, input2), expected, ulps);
|
|
}
|
|
|
|
public static int testUlpDiff(String testName, double input1, double input2,
|
|
double result, double expected, double ulps) {
|
|
int code = testUlpCore(result, expected, ulps);
|
|
if (code == 1) {
|
|
System.err.println("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);
|
|
}
|
|
return code;
|
|
}
|
|
|
|
// 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 int testUlpDiffWithAbsBound(String testName, double input,
|
|
DoubleUnaryOperator func, double expected,
|
|
double ulps, double absBound) {
|
|
return testUlpDiffWithAbsBound(testName, input,
|
|
func.applyAsDouble(input), expected,
|
|
ulps, absBound);
|
|
}
|
|
|
|
public static int 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);
|
|
|
|
if (code == 1) {
|
|
System.err.println("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);
|
|
}
|
|
return code;
|
|
}
|
|
|
|
// 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 int testUlpDiffWithLowerBound(String testName, double input,
|
|
DoubleUnaryOperator func, double expected,
|
|
double ulps, double lowerBound) {
|
|
return testUlpDiffWithLowerBound(testName, input,
|
|
func.applyAsDouble(input), expected,
|
|
ulps, lowerBound);
|
|
}
|
|
|
|
public static int 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);
|
|
}
|
|
|
|
if (code == 1) {
|
|
System.err.println("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);
|
|
}
|
|
return code;
|
|
}
|
|
|
|
public static int testTolerance(String testName, double input,
|
|
DoubleUnaryOperator func, double expected, double tolerance) {
|
|
return testTolerance(testName, input, func.applyAsDouble(input), expected, tolerance);
|
|
|
|
}
|
|
public static int testTolerance(String testName, double input,
|
|
double result, double expected, double tolerance) {
|
|
if (Double.compare(expected, result ) != 0) {
|
|
double difference = expected - result;
|
|
if (isUnordered(expected, result) ||
|
|
Double.isNaN(difference) ||
|
|
// fail if greater than or unordered
|
|
!(Math.abs((difference)/expected) <= StrictMath.pow(10, -tolerance)) ) {
|
|
System.err.println("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);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// For a successful test, the result must be within the upper and
|
|
// lower bounds.
|
|
public static int testBounds(String testName, double input, DoubleUnaryOperator func,
|
|
double bound1, double bound2) {
|
|
return testBounds(testName, input, func.applyAsDouble(input), bound1, bound2);
|
|
}
|
|
|
|
public static int testBounds(String testName, double input, double result,
|
|
double bound1, double bound2) {
|
|
if ((result >= bound1 && result <= bound2) ||
|
|
(result <= bound1 && result >= bound2))
|
|
return 0;
|
|
else {
|
|
double lowerBound = Math.min(bound1, bound2);
|
|
double upperBound = Math.max(bound1, bound2);
|
|
System.err.println("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) + ")]");
|
|
return 1;
|
|
}
|
|
}
|
|
}
|