8331907: BigInteger and BigDecimal should use optimized division

Reviewed-by: rgiulietti, bpb
This commit is contained in:
Daniel Jeliński 2024-05-14 05:01:51 +00:00
parent 440782e016
commit beea5305b0
4 changed files with 89 additions and 186 deletions

View file

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

View file

@ -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