jdk-24/test/jdk/java/lang/Math/AbsTests.java
Brian Burkhalter c0d4efff3c 6506405: Math.abs(float) is slow
Reviewed-by: darcy
2021-07-14 15:50:51 +00:00

250 lines
8.4 KiB
Java

/*
* Copyright (c) 2020, 2021, 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.concurrent.atomic.AtomicInteger;
import java.util.function.DoubleUnaryOperator;
import java.util.function.IntUnaryOperator;
import java.util.function.LongUnaryOperator;
import java.util.function.UnaryOperator;
import java.util.stream.DoubleStream;
import jdk.internal.math.DoubleConsts;
import jdk.internal.math.FloatConsts;
/*
* @test
* @bug 6506405 8241374
* @summary Test abs and absExact for Math and StrictMath
* @modules java.base/jdk.internal.math
*/
public class AbsTests {
private static final double GELFOND = Math.exp(Math.PI);
private static final double TAU = 2.0*Math.PI;
// Values for testing float and double abs
private static final double[] FLOATING_POINT_VALUES = new double[] {
0.0,
-0.0,
+0.0,
Double.MIN_VALUE,
Double.MIN_NORMAL,
Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY,
Double.NaN,
Float.MIN_VALUE,
Float.MIN_NORMAL,
Float.NEGATIVE_INFINITY,
Float.POSITIVE_INFINITY,
Float.NaN,
Double.longBitsToDouble((1 << DoubleConsts.SIGNIFICAND_WIDTH) |
((1 << DoubleConsts.SIGNIFICAND_WIDTH) - 1)),
DoubleConsts.MAG_BIT_MASK >>> 1,
Float.intBitsToFloat((1 << FloatConsts.SIGNIFICAND_WIDTH) |
((1 << FloatConsts.SIGNIFICAND_WIDTH) - 1)),
FloatConsts.MAG_BIT_MASK >>> 1,
Math.E,
GELFOND,
Math.PI,
TAU
};
private static int errors = 0;
public static void main(String... args) {
errors += testInRangeIntAbs();
errors += testIntMinValue();
errors += testInRangeLongAbs();
errors += testLongMinValue();
errors += testFloatAbs();
errors += testDoubleAbs();
if (errors > 0) {
throw new RuntimeException(errors + " errors found testing abs.");
}
}
// --------------------------------------------------------------------
private static int testInRangeIntAbs() {
int errors = 0;
int[][] testCases = {
// Argument to abs, expected result
{+0, 0},
{+1, 1},
{-1, 1},
{-2, 2},
{+2, 2},
{-Integer.MAX_VALUE, Integer.MAX_VALUE},
{+Integer.MAX_VALUE, Integer.MAX_VALUE}
};
for(var testCase : testCases) {
errors += testIntAbs(Math::abs, testCase[0], testCase[1]);
errors += testIntAbs(Math::absExact, testCase[0], testCase[1]);
}
return errors;
}
private static int testIntMinValue() {
int errors = 0;
// Strange but true
errors += testIntAbs(Math::abs, Integer.MIN_VALUE, Integer.MIN_VALUE);
// Test exceptional behavior for absExact
try {
int result = Math.absExact(Integer.MIN_VALUE);
System.err.printf("Bad return value %d from Math.absExact(MIN_VALUE)%n",
result);
errors++;
} catch (ArithmeticException ae) {
; // Expected
}
return errors;
}
private static int testIntAbs(IntUnaryOperator absFunc,
int argument, int expected) {
int result = absFunc.applyAsInt(argument);
if (result != expected) {
System.err.printf("Unexpected int abs result %d for argument %d%n",
result, argument);
return 1;
} else {
return 0;
}
}
// --------------------------------------------------------------------
private static long testInRangeLongAbs() {
int errors = 0;
long[][] testCases = {
// Argument to abs, expected result
{+0L, 0L},
{+1L, 1L},
{-1L, 1L},
{-2L, 2L},
{+2L, 2L},
{-Integer.MAX_VALUE, Integer.MAX_VALUE},
{+Integer.MAX_VALUE, Integer.MAX_VALUE},
{ Integer.MIN_VALUE, -((long)Integer.MIN_VALUE)},
{-Long.MAX_VALUE, Long.MAX_VALUE},
};
for(var testCase : testCases) {
errors += testLongAbs(Math::abs, testCase[0], testCase[1]);
errors += testLongAbs(Math::absExact, testCase[0], testCase[1]);
}
return errors;
}
private static int testLongMinValue() {
int errors = 0;
// Strange but true
errors += testLongAbs(Math::abs, Long.MIN_VALUE, Long.MIN_VALUE);
// Test exceptional behavior for absExact
try {
long result = Math.absExact(Long.MIN_VALUE);
System.err.printf("Bad return value %d from Math.absExact(MIN_VALUE)%n",
result);
errors++;
} catch (ArithmeticException ae) {
; // Expected
}
return errors;
}
private static int testLongAbs(LongUnaryOperator absFunc,
long argument, long expected) {
long result = absFunc.applyAsLong(argument);
if (result != expected) {
System.err.printf("Unexpected long abs result %d for argument %d%n",
result, argument);
return 1;
} else {
return 0;
}
}
// --------------------------------------------------------------------
private static int testFloatAbs() {
DoubleStream doubles = DoubleStream.of(FLOATING_POINT_VALUES);
final AtomicInteger errors = new AtomicInteger();
doubles.mapToObj(d -> (float)d).
forEach(f -> {errors.addAndGet(testFloatAbs(f));});
return errors.get();
}
private static int testFloatAbs(float f) {
int errors = testFloatAbs("Math.abs", Math::abs, f);
errors += testFloatAbs("Math.abs", Math::abs, -f);
errors += testFloatAbs("StrictMath.abs", StrictMath::abs, f);
errors += testFloatAbs("StrictMath.abs", StrictMath::abs, -f);
return errors;
}
private static int testFloatAbs(String testName,
UnaryOperator<Float> absFunc, float f) {
float result = absFunc.apply(-f);
if (Float.isNaN(f) && Float.isNaN(result)) {
return 0;
}
float expected = f == -0.0F ? 0.0F : (f < 0.0F ? -f : f);
return Tests.test(testName, f, result, expected);
}
// --------------------------------------------------------------------
private static int testDoubleAbs() {
DoubleStream doubles = DoubleStream.of(FLOATING_POINT_VALUES);
final AtomicInteger errors = new AtomicInteger();
doubles.forEach(d -> {errors.addAndGet(testDoubleAbs(d));});
return errors.get();
}
private static int testDoubleAbs(double d) {
int errors = testDoubleAbs("Math.abs", Math::abs, d);
errors += testDoubleAbs("Math.abs", Math::abs, -d);
errors += testDoubleAbs("StrictMath.abs", StrictMath::abs, d);
errors += testDoubleAbs("StrictMath.abs", StrictMath::abs, -d);
return errors;
}
private static int testDoubleAbs(String testName,
DoubleUnaryOperator absFunc, double d) {
double result = absFunc.applyAsDouble(-d);
if (Double.isNaN(d) && Double.isNaN(result)) {
return 0;
}
double expected = d == -0.0F ? 0.0F : (d < 0.0F ? -d : d);
return Tests.test(testName, d, result, expected);
}
}