6506405: Math.abs(float) is slow
Reviewed-by: darcy
This commit is contained in:
parent
357fe09f2e
commit
c0d4efff3c
@ -1527,7 +1527,8 @@ public final class Math {
|
||||
*/
|
||||
@IntrinsicCandidate
|
||||
public static float abs(float a) {
|
||||
return (a <= 0.0F) ? 0.0F - a : a;
|
||||
// Convert to bit field form, zero the sign bit, and convert back
|
||||
return Float.intBitsToFloat(Float.floatToRawIntBits(a) & FloatConsts.MAG_BIT_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1552,7 +1553,9 @@ public final class Math {
|
||||
*/
|
||||
@IntrinsicCandidate
|
||||
public static double abs(double a) {
|
||||
return (a <= 0.0D) ? 0.0D - a : a;
|
||||
// Convert to bit field form, zero the sign bit, and convert back
|
||||
return Double.longBitsToDouble(Double.doubleToRawLongBits(a) & DoubleConsts.MAG_BIT_MASK);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,12 +73,20 @@ public class DoubleConsts {
|
||||
*/
|
||||
public static final long SIGNIF_BIT_MASK = 0x000FFFFFFFFFFFFFL;
|
||||
|
||||
/**
|
||||
* Bit mask to isolate the magnitude bits (combined exponent and
|
||||
* significand fields) of a {@code double}.
|
||||
*/
|
||||
public static final long MAG_BIT_MASK = ~SIGN_BIT_MASK;
|
||||
|
||||
static {
|
||||
// verify bit masks cover all bit positions and that the bit
|
||||
// masks are non-overlapping
|
||||
assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0L) &&
|
||||
(((SIGN_BIT_MASK & EXP_BIT_MASK) == 0L) &&
|
||||
((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0L) &&
|
||||
((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0L)));
|
||||
((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0L)) &&
|
||||
((SIGN_BIT_MASK | MAG_BIT_MASK) == ~0L));
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 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
|
||||
@ -73,12 +73,19 @@ public class FloatConsts {
|
||||
*/
|
||||
public static final int SIGNIF_BIT_MASK = 0x007FFFFF;
|
||||
|
||||
/**
|
||||
* Bit mask to isolate the magnitude bits (combined exponent and
|
||||
* significand fields) of a {@code float}.
|
||||
*/
|
||||
public static final int MAG_BIT_MASK = ~SIGN_BIT_MASK;
|
||||
|
||||
static {
|
||||
// verify bit masks cover all bit positions and that the bit
|
||||
// masks are non-overlapping
|
||||
assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0) &&
|
||||
(((SIGN_BIT_MASK & EXP_BIT_MASK) == 0) &&
|
||||
((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0) &&
|
||||
((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0)));
|
||||
((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0)) &&
|
||||
((SIGN_BIT_MASK | MAG_BIT_MASK) == ~0));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
@ -21,14 +21,52 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.util.function.*;
|
||||
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 8241374
|
||||
* @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) {
|
||||
@ -36,12 +74,16 @@ public class AbsTests {
|
||||
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 = {
|
||||
@ -143,4 +185,65 @@ public class AbsTests {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user