6506405: Math.abs(float) is slow

Reviewed-by: darcy
This commit is contained in:
Brian Burkhalter 2021-07-14 15:50:51 +00:00
parent 357fe09f2e
commit c0d4efff3c
4 changed files with 129 additions and 8 deletions

View File

@ -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);
}
/**

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}