Skip to content

Commit c0d4eff

Browse files
author
Brian Burkhalter
committed
6506405: Math.abs(float) is slow
Reviewed-by: darcy
1 parent 357fe09 commit c0d4eff

File tree

4 files changed

+129
-8
lines changed

4 files changed

+129
-8
lines changed

src/java.base/share/classes/java/lang/Math.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,7 +1527,8 @@ public static long absExact(long a) {
15271527
*/
15281528
@IntrinsicCandidate
15291529
public static float abs(float a) {
1530-
return (a <= 0.0F) ? 0.0F - a : a;
1530+
// Convert to bit field form, zero the sign bit, and convert back
1531+
return Float.intBitsToFloat(Float.floatToRawIntBits(a) & FloatConsts.MAG_BIT_MASK);
15311532
}
15321533

15331534
/**
@@ -1552,7 +1553,9 @@ public static float abs(float a) {
15521553
*/
15531554
@IntrinsicCandidate
15541555
public static double abs(double a) {
1555-
return (a <= 0.0D) ? 0.0D - a : a;
1556+
// Convert to bit field form, zero the sign bit, and convert back
1557+
return Double.longBitsToDouble(Double.doubleToRawLongBits(a) & DoubleConsts.MAG_BIT_MASK);
1558+
15561559
}
15571560

15581561
/**

src/java.base/share/classes/jdk/internal/math/DoubleConsts.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,20 @@ private DoubleConsts() {}
7373
*/
7474
public static final long SIGNIF_BIT_MASK = 0x000FFFFFFFFFFFFFL;
7575

76+
/**
77+
* Bit mask to isolate the magnitude bits (combined exponent and
78+
* significand fields) of a {@code double}.
79+
*/
80+
public static final long MAG_BIT_MASK = ~SIGN_BIT_MASK;
81+
7682
static {
7783
// verify bit masks cover all bit positions and that the bit
7884
// masks are non-overlapping
7985
assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0L) &&
8086
(((SIGN_BIT_MASK & EXP_BIT_MASK) == 0L) &&
8187
((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0L) &&
82-
((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0L)));
88+
((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0L)) &&
89+
((SIGN_BIT_MASK | MAG_BIT_MASK) == ~0L));
90+
8391
}
8492
}

src/java.base/share/classes/jdk/internal/math/FloatConsts.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -73,12 +73,19 @@ private FloatConsts() {}
7373
*/
7474
public static final int SIGNIF_BIT_MASK = 0x007FFFFF;
7575

76+
/**
77+
* Bit mask to isolate the magnitude bits (combined exponent and
78+
* significand fields) of a {@code float}.
79+
*/
80+
public static final int MAG_BIT_MASK = ~SIGN_BIT_MASK;
81+
7682
static {
7783
// verify bit masks cover all bit positions and that the bit
7884
// masks are non-overlapping
7985
assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0) &&
8086
(((SIGN_BIT_MASK & EXP_BIT_MASK) == 0) &&
8187
((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0) &&
82-
((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0)));
88+
((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0)) &&
89+
((SIGN_BIT_MASK | MAG_BIT_MASK) == ~0));
8390
}
8491
}

test/jdk/java/lang/Math/AbsTests.java

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -21,27 +21,69 @@
2121
* questions.
2222
*/
2323

24-
import java.util.function.*;
24+
import java.util.concurrent.atomic.AtomicInteger;
25+
import java.util.function.DoubleUnaryOperator;
26+
import java.util.function.IntUnaryOperator;
27+
import java.util.function.LongUnaryOperator;
28+
import java.util.function.UnaryOperator;
29+
import java.util.stream.DoubleStream;
30+
import jdk.internal.math.DoubleConsts;
31+
import jdk.internal.math.FloatConsts;
2532

2633
/*
2734
* @test
28-
* @bug 8241374
35+
* @bug 6506405 8241374
2936
* @summary Test abs and absExact for Math and StrictMath
37+
* @modules java.base/jdk.internal.math
3038
*/
3139
public class AbsTests {
40+
private static final double GELFOND = Math.exp(Math.PI);
41+
private static final double TAU = 2.0*Math.PI;
42+
43+
// Values for testing float and double abs
44+
private static final double[] FLOATING_POINT_VALUES = new double[] {
45+
0.0,
46+
-0.0,
47+
+0.0,
48+
Double.MIN_VALUE,
49+
Double.MIN_NORMAL,
50+
Double.NEGATIVE_INFINITY,
51+
Double.POSITIVE_INFINITY,
52+
Double.NaN,
53+
Float.MIN_VALUE,
54+
Float.MIN_NORMAL,
55+
Float.NEGATIVE_INFINITY,
56+
Float.POSITIVE_INFINITY,
57+
Float.NaN,
58+
Double.longBitsToDouble((1 << DoubleConsts.SIGNIFICAND_WIDTH) |
59+
((1 << DoubleConsts.SIGNIFICAND_WIDTH) - 1)),
60+
DoubleConsts.MAG_BIT_MASK >>> 1,
61+
Float.intBitsToFloat((1 << FloatConsts.SIGNIFICAND_WIDTH) |
62+
((1 << FloatConsts.SIGNIFICAND_WIDTH) - 1)),
63+
FloatConsts.MAG_BIT_MASK >>> 1,
64+
Math.E,
65+
GELFOND,
66+
Math.PI,
67+
TAU
68+
};
69+
3270
private static int errors = 0;
3371

3472
public static void main(String... args) {
3573
errors += testInRangeIntAbs();
3674
errors += testIntMinValue();
3775
errors += testInRangeLongAbs();
3876
errors += testLongMinValue();
77+
errors += testFloatAbs();
78+
errors += testDoubleAbs();
3979

4080
if (errors > 0) {
4181
throw new RuntimeException(errors + " errors found testing abs.");
4282
}
4383
}
4484

85+
// --------------------------------------------------------------------
86+
4587
private static int testInRangeIntAbs() {
4688
int errors = 0;
4789
int[][] testCases = {
@@ -143,4 +185,65 @@ private static int testLongAbs(LongUnaryOperator absFunc,
143185
return 0;
144186
}
145187
}
188+
189+
// --------------------------------------------------------------------
190+
191+
private static int testFloatAbs() {
192+
DoubleStream doubles = DoubleStream.of(FLOATING_POINT_VALUES);
193+
194+
final AtomicInteger errors = new AtomicInteger();
195+
doubles.mapToObj(d -> (float)d).
196+
forEach(f -> {errors.addAndGet(testFloatAbs(f));});
197+
198+
return errors.get();
199+
}
200+
201+
private static int testFloatAbs(float f) {
202+
int errors = testFloatAbs("Math.abs", Math::abs, f);
203+
errors += testFloatAbs("Math.abs", Math::abs, -f);
204+
errors += testFloatAbs("StrictMath.abs", StrictMath::abs, f);
205+
errors += testFloatAbs("StrictMath.abs", StrictMath::abs, -f);
206+
return errors;
207+
}
208+
209+
private static int testFloatAbs(String testName,
210+
UnaryOperator<Float> absFunc, float f) {
211+
float result = absFunc.apply(-f);
212+
if (Float.isNaN(f) && Float.isNaN(result)) {
213+
return 0;
214+
}
215+
216+
float expected = f == -0.0F ? 0.0F : (f < 0.0F ? -f : f);
217+
return Tests.test(testName, f, result, expected);
218+
}
219+
220+
// --------------------------------------------------------------------
221+
222+
private static int testDoubleAbs() {
223+
DoubleStream doubles = DoubleStream.of(FLOATING_POINT_VALUES);
224+
225+
final AtomicInteger errors = new AtomicInteger();
226+
doubles.forEach(d -> {errors.addAndGet(testDoubleAbs(d));});
227+
228+
return errors.get();
229+
}
230+
231+
private static int testDoubleAbs(double d) {
232+
int errors = testDoubleAbs("Math.abs", Math::abs, d);
233+
errors += testDoubleAbs("Math.abs", Math::abs, -d);
234+
errors += testDoubleAbs("StrictMath.abs", StrictMath::abs, d);
235+
errors += testDoubleAbs("StrictMath.abs", StrictMath::abs, -d);
236+
return errors;
237+
}
238+
239+
private static int testDoubleAbs(String testName,
240+
DoubleUnaryOperator absFunc, double d) {
241+
double result = absFunc.applyAsDouble(-d);
242+
if (Double.isNaN(d) && Double.isNaN(result)) {
243+
return 0;
244+
}
245+
246+
double expected = d == -0.0F ? 0.0F : (d < 0.0F ? -d : d);
247+
return Tests.test(testName, d, result, expected);
248+
}
146249
}

0 commit comments

Comments
 (0)