mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-13 17:33:10 +00:00
8331907: BigInteger and BigDecimal should use optimized division
Reviewed-by: rgiulietti, bpb
This commit is contained in:
parent
440782e016
commit
beea5305b0
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 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
|
||||
@ -5680,18 +5680,8 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||
|
||||
tmp = (dividendHi << shift) | (dividendLo >>> 64 - shift);
|
||||
long u2 = tmp & LONG_MASK;
|
||||
long q1, r_tmp;
|
||||
if (v1 == 1) {
|
||||
q1 = tmp;
|
||||
r_tmp = 0;
|
||||
} else if (tmp >= 0) {
|
||||
q1 = tmp / v1;
|
||||
r_tmp = tmp - q1 * v1;
|
||||
} else {
|
||||
long[] rq = divRemNegativeLong(tmp, v1);
|
||||
q1 = rq[1];
|
||||
r_tmp = rq[0];
|
||||
}
|
||||
long q1 = Long.divideUnsigned(tmp, v1);
|
||||
long r_tmp = Long.remainderUnsigned(tmp, v1);
|
||||
|
||||
while(q1 >= DIV_NUM_BASE || unsignedLongCompare(q1*v0, make64(r_tmp, u1))) {
|
||||
q1--;
|
||||
@ -5702,18 +5692,8 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||
|
||||
tmp = mulsub(u2,u1,v1,v0,q1);
|
||||
u1 = tmp & LONG_MASK;
|
||||
long q0;
|
||||
if (v1 == 1) {
|
||||
q0 = tmp;
|
||||
r_tmp = 0;
|
||||
} else if (tmp >= 0) {
|
||||
q0 = tmp / v1;
|
||||
r_tmp = tmp - q0 * v1;
|
||||
} else {
|
||||
long[] rq = divRemNegativeLong(tmp, v1);
|
||||
q0 = rq[1];
|
||||
r_tmp = rq[0];
|
||||
}
|
||||
long q0 = Long.divideUnsigned(tmp, v1);
|
||||
r_tmp = Long.remainderUnsigned(tmp, v1);
|
||||
|
||||
while(q0 >= DIV_NUM_BASE || unsignedLongCompare(q0*v0,make64(r_tmp,u0))) {
|
||||
q0--;
|
||||
@ -5793,37 +5773,6 @@ public class BigDecimal extends Number implements Comparable<BigDecimal> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the quotient and remainder of dividing a negative long by
|
||||
* another long.
|
||||
*
|
||||
* @param n the numerator; must be negative
|
||||
* @param d the denominator; must not be unity
|
||||
* @return a two-element {@code long} array with the remainder and quotient in
|
||||
* the initial and final elements, respectively
|
||||
*/
|
||||
private static long[] divRemNegativeLong(long n, long d) {
|
||||
assert n < 0 : "Non-negative numerator " + n;
|
||||
assert d != 1 : "Unity denominator";
|
||||
|
||||
// Approximate the quotient and remainder
|
||||
long q = (n >>> 1) / (d >>> 1);
|
||||
long r = n - q * d;
|
||||
|
||||
// Correct the approximation
|
||||
while (r < 0) {
|
||||
r += d;
|
||||
q--;
|
||||
}
|
||||
while (r >= d) {
|
||||
r -= d;
|
||||
q++;
|
||||
}
|
||||
|
||||
// n - q*d == r && 0 <= r < d, hence we're done.
|
||||
return new long[] {r, q};
|
||||
}
|
||||
|
||||
private static long make64(long hi, long lo) {
|
||||
return hi<<32 | lo;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 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
|
||||
@ -1092,9 +1092,9 @@ class MutableBigInteger {
|
||||
|
||||
// Special case of one word dividend
|
||||
if (intLen == 1) {
|
||||
long dividendValue = value[offset] & LONG_MASK;
|
||||
int q = (int) (dividendValue / divisorLong);
|
||||
int r = (int) (dividendValue - q * divisorLong);
|
||||
int dividendValue = value[offset];
|
||||
int q = Integer.divideUnsigned(dividendValue, divisor);
|
||||
int r = Integer.remainderUnsigned(dividendValue, divisor);
|
||||
quotient.value[0] = q;
|
||||
quotient.intLen = (q == 0) ? 0 : 1;
|
||||
quotient.offset = 0;
|
||||
@ -1106,41 +1106,17 @@ class MutableBigInteger {
|
||||
quotient.offset = 0;
|
||||
quotient.intLen = intLen;
|
||||
|
||||
// Normalize the divisor
|
||||
int shift = Integer.numberOfLeadingZeros(divisor);
|
||||
|
||||
int rem = value[offset];
|
||||
long remLong = rem & LONG_MASK;
|
||||
if (remLong < divisorLong) {
|
||||
quotient.value[0] = 0;
|
||||
} else {
|
||||
quotient.value[0] = (int)(remLong / divisorLong);
|
||||
rem = (int) (remLong - (quotient.value[0] * divisorLong));
|
||||
remLong = rem & LONG_MASK;
|
||||
}
|
||||
int xlen = intLen;
|
||||
while (--xlen > 0) {
|
||||
long dividendEstimate = (remLong << 32) |
|
||||
long rem = 0;
|
||||
for (int xlen = intLen; xlen > 0; xlen--) {
|
||||
long dividendEstimate = (rem << 32) |
|
||||
(value[offset + intLen - xlen] & LONG_MASK);
|
||||
int q;
|
||||
if (dividendEstimate >= 0) {
|
||||
q = (int) (dividendEstimate / divisorLong);
|
||||
rem = (int) (dividendEstimate - q * divisorLong);
|
||||
} else {
|
||||
long tmp = divWord(dividendEstimate, divisor);
|
||||
q = (int) (tmp & LONG_MASK);
|
||||
rem = (int) (tmp >>> 32);
|
||||
}
|
||||
int q = (int) Long.divideUnsigned(dividendEstimate, divisorLong);
|
||||
rem = Long.remainderUnsigned(dividendEstimate, divisorLong);
|
||||
quotient.value[intLen - xlen] = q;
|
||||
remLong = rem & LONG_MASK;
|
||||
}
|
||||
|
||||
quotient.normalize();
|
||||
// Unnormalize
|
||||
if (shift > 0)
|
||||
return rem % divisor;
|
||||
else
|
||||
return rem;
|
||||
return (int)rem;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1557,14 +1533,8 @@ class MutableBigInteger {
|
||||
skipCorrection = qrem + 0x80000000 < nh2;
|
||||
} else {
|
||||
long nChunk = (((long)nh) << 32) | (nm & LONG_MASK);
|
||||
if (nChunk >= 0) {
|
||||
qhat = (int) (nChunk / dhLong);
|
||||
qrem = (int) (nChunk - (qhat * dhLong));
|
||||
} else {
|
||||
long tmp = divWord(nChunk, dh);
|
||||
qhat = (int) (tmp & LONG_MASK);
|
||||
qrem = (int) (tmp >>> 32);
|
||||
}
|
||||
qhat = (int) Long.divideUnsigned(nChunk, dhLong);
|
||||
qrem = (int) Long.remainderUnsigned(nChunk, dhLong);
|
||||
}
|
||||
|
||||
if (qhat == 0)
|
||||
@ -1616,14 +1586,8 @@ class MutableBigInteger {
|
||||
skipCorrection = qrem + 0x80000000 < nh2;
|
||||
} else {
|
||||
long nChunk = (((long) nh) << 32) | (nm & LONG_MASK);
|
||||
if (nChunk >= 0) {
|
||||
qhat = (int) (nChunk / dhLong);
|
||||
qrem = (int) (nChunk - (qhat * dhLong));
|
||||
} else {
|
||||
long tmp = divWord(nChunk, dh);
|
||||
qhat = (int) (tmp & LONG_MASK);
|
||||
qrem = (int) (tmp >>> 32);
|
||||
}
|
||||
qhat = (int) Long.divideUnsigned(nChunk, dhLong);
|
||||
qrem = (int) Long.remainderUnsigned(nChunk, dhLong);
|
||||
}
|
||||
if (qhat != 0) {
|
||||
if (!skipCorrection) { // Correct qhat
|
||||
@ -1732,14 +1696,8 @@ class MutableBigInteger {
|
||||
skipCorrection = qrem + 0x80000000 < nh2;
|
||||
} else {
|
||||
long nChunk = (((long) nh) << 32) | (nm & LONG_MASK);
|
||||
if (nChunk >= 0) {
|
||||
qhat = (int) (nChunk / dhLong);
|
||||
qrem = (int) (nChunk - (qhat * dhLong));
|
||||
} else {
|
||||
long tmp = divWord(nChunk, dh);
|
||||
qhat =(int)(tmp & LONG_MASK);
|
||||
qrem = (int)(tmp>>>32);
|
||||
}
|
||||
qhat = (int) Long.divideUnsigned(nChunk, dhLong);
|
||||
qrem = (int) Long.remainderUnsigned(nChunk, dhLong);
|
||||
}
|
||||
|
||||
if (qhat == 0)
|
||||
@ -1834,40 +1792,6 @@ class MutableBigInteger {
|
||||
return (one+Long.MIN_VALUE) > (two+Long.MIN_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method divides a long quantity by an int to estimate
|
||||
* qhat for two multi precision numbers. It is used when
|
||||
* the signed value of n is less than zero.
|
||||
* Returns long value where high 32 bits contain remainder value and
|
||||
* low 32 bits contain quotient value.
|
||||
*/
|
||||
static long divWord(long n, int d) {
|
||||
long dLong = d & LONG_MASK;
|
||||
long r;
|
||||
long q;
|
||||
if (dLong == 1) {
|
||||
q = (int)n;
|
||||
r = 0;
|
||||
return (r << 32) | (q & LONG_MASK);
|
||||
}
|
||||
|
||||
// Approximate the quotient and remainder
|
||||
q = (n >>> 1) / (dLong >>> 1);
|
||||
r = n - q*dLong;
|
||||
|
||||
// Correct the approximation
|
||||
while (r < 0) {
|
||||
r += dLong;
|
||||
q--;
|
||||
}
|
||||
while (r >= dLong) {
|
||||
r -= dLong;
|
||||
q++;
|
||||
}
|
||||
// n - q*dlong == r && 0 <= r <dLong, hence we're done.
|
||||
return (r << 32) | (q & LONG_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the integer square root {@code floor(sqrt(this))} where
|
||||
* {@code sqrt(.)} denotes the mathematical square root. The contents of
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 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
|
||||
@ -133,15 +133,6 @@ public class BigDecimals {
|
||||
}
|
||||
}
|
||||
|
||||
/** Invokes the toString method of BigDecimal with various different values. */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TEST_SIZE)
|
||||
public void testToString(Blackhole bh) {
|
||||
for (BigDecimal s : bigDecimals) {
|
||||
bh.consume(s.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the setScale method of BigDecimal with various different values.
|
||||
*/
|
||||
@ -194,6 +185,39 @@ public class BigDecimals {
|
||||
bh.consume(tmp);
|
||||
}
|
||||
|
||||
/** Test divide with huge/small numbers */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TEST_SIZE * TEST_SIZE)
|
||||
public void testHugeSmallDivide(Blackhole bh) {
|
||||
for (BigDecimal s : hugeArray) {
|
||||
for (BigDecimal t : smallArray) {
|
||||
bh.consume(s.divide(t, RoundingMode.DOWN));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Test divide with large/small numbers */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TEST_SIZE * TEST_SIZE)
|
||||
public void testLargeSmallDivide(Blackhole bh) {
|
||||
for (BigDecimal s : largeArray) {
|
||||
for (BigDecimal t : smallArray) {
|
||||
bh.consume(s.divide(t, RoundingMode.DOWN));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Test divide with huge/large numbers */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TEST_SIZE * TEST_SIZE)
|
||||
public void testHugeLargeDivide(Blackhole bh) {
|
||||
for (BigDecimal s : hugeArray) {
|
||||
for (BigDecimal t : largeArray) {
|
||||
bh.consume(s.divide(t, RoundingMode.DOWN));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Invokes the compareTo method of BigDecimal with various different values. */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TEST_SIZE - 1)
|
||||
@ -203,31 +227,4 @@ public class BigDecimals {
|
||||
bh.consume(c.compareTo(s));
|
||||
}
|
||||
}
|
||||
|
||||
/** Test BigDecimal.toString() with huge numbers larger than MAX_LONG */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TEST_SIZE)
|
||||
public void testHugeToString(Blackhole bh) {
|
||||
for (BigDecimal s : hugeArray) {
|
||||
bh.consume(s.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/** Test BigDecimal.toString() with large numbers less than MAX_LONG but larger than MAX_INT */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TEST_SIZE)
|
||||
public void testLargeToString(Blackhole bh) {
|
||||
for (BigDecimal s : largeArray) {
|
||||
bh.consume(s.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/** Test BigDecimal.toString() with small numbers less than MAX_INT */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TEST_SIZE)
|
||||
public void testSmallToString(Blackhole bh) {
|
||||
for (BigDecimal s : smallArray) {
|
||||
bh.consume(s.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 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
|
||||
@ -132,6 +132,39 @@ public class BigIntegers {
|
||||
bh.consume(tmp);
|
||||
}
|
||||
|
||||
/** Test divide with huge/small numbers */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TESTSIZE * TESTSIZE)
|
||||
public void testHugeSmallDivide(Blackhole bh) {
|
||||
for (BigInteger s : hugeArray) {
|
||||
for (BigInteger t : smallArray) {
|
||||
bh.consume(s.divide(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Test divide with large/small numbers */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TESTSIZE * TESTSIZE)
|
||||
public void testLargeSmallDivide(Blackhole bh) {
|
||||
for (BigInteger s : largeArray) {
|
||||
for (BigInteger t : smallArray) {
|
||||
bh.consume(s.divide(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Test divide with huge/large numbers */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TESTSIZE * TESTSIZE)
|
||||
public void testHugeLargeDivide(Blackhole bh) {
|
||||
for (BigInteger s : hugeArray) {
|
||||
for (BigInteger t : largeArray) {
|
||||
bh.consume(s.divide(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Invokes the multiply method of BigInteger with various different values. */
|
||||
@Benchmark
|
||||
@OperationsPerInvocation(TESTSIZE)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user