RFR: 8334755: Asymptotically faster implementation of square root algorithm [v50]
fabioromano1
duke at openjdk.org
Thu Aug 1 19:53:37 UTC 2024
On Thu, 1 Aug 2024 19:10:51 GMT, Raffaello Giulietti <rgiulietti at openjdk.org> wrote:
>> fabioromano1 has updated the pull request incrementally with one additional commit since the last revision:
>>
>> Last small changes
>
> I guess the overhead is negligible when compared to the arithmetic operation (shifts, divisions, etc.).
> Also, the maximal stack depth for the recursion itself is quite limited and under control.
> If at all, I would rather postpone that effort to a followup PR, and only if there are noticeable advantages without compromising readability.
@rgiulietti In case, the code should look like this:
/**
* Assumes {@code intLen > 2 && intLen % 2 == 0
* && Integer.numberOfLeadingZeros(value[offset]) <= 1}
*/
private MutableBigInteger[] sqrtRemKaratsuba(boolean needRemainder) {
MutableBigInteger sqrt, rem;
// Base case
{
long x = ((value[offset] & LONG_MASK) << 32) | (value[offset + 1] & LONG_MASK);
long s = unsignedLongSqrt(x);
// Allocate sufficient space to hold the final square root
sqrt = new MutableBigInteger(new int[intLen >> 1]);
// Place the partial square root
sqrt.intLen = 1;
sqrt.value[0] = (int) s;
rem = new MutableBigInteger(x - s * s);
}
// Iterative step
int len;
for (len = 4; len < intLen; len <<= 1) {
final int blockLen = len >> 2;
MutableBigInteger dividend = rem;
dividend.shiftAddDisjoint(getBlockForSqrt(1, len, blockLen), blockLen);
// Compute dividend / (2*sqrt)
MutableBigInteger q = new MutableBigInteger();
MutableBigInteger u = dividend.divide(sqrt, q);
if (q.isOdd())
u.add(sqrt);
q.rightShift(1);
sqrt.shiftAdd(q, blockLen);
// Corresponds to ub + a_0 in the paper
u.shiftAddDisjoint(getBlockForSqrt(0, len, blockLen), blockLen);
BigInteger qBig = q.toBigInteger(); // Cast to BigInteger to use fast multiplication
MutableBigInteger qSqr = new MutableBigInteger(qBig.multiply(qBig).mag);
rem = u;
if (rem.subtract(qSqr) < 0) {
MutableBigInteger twiceSqrt = new MutableBigInteger(sqrt);
twiceSqrt.leftShift(1);
// Since subtract() performs an absolute difference, to get the correct algebraic sum
// we must first add the sum of absolute values of addends concordant with the sign of rem
// and then subtract the sum of absolute values of addends that are discordant
rem.add(ONE);
rem.subtract(twiceSqrt);
sqrt.subtract(ONE);
}
}
// Final step
final int blockLen = len >> 2;
MutableBigInteger dividend = rem;
dividend.shiftAddDisjoint(getBlockForSqrt(1, len, blockLen), blockLen);
MutableBigInteger q = new MutableBigInteger();
MutableBigInteger u = dividend.divide(sqrt, q);
if (q.isOdd())
u.add(sqrt);
q.rightShift(1);
sqrt.shiftAdd(q, blockLen);
u.shiftAddDisjoint(getBlockForSqrt(0, len, blockLen), blockLen);
BigInteger qBig = q.toBigInteger();
MutableBigInteger qSqr = new MutableBigInteger(qBig.multiply(qBig).mag);
if (needRemainder) {
rem = u;
if (rem.subtract(qSqr) < 0) {
MutableBigInteger twiceSqrt = new MutableBigInteger(sqrt);
twiceSqrt.leftShift(1);
rem.add(ONE);
rem.subtract(twiceSqrt);
sqrt.subtract(ONE);
}
} else {
rem = null;
if (u.compare(qSqr) < 0)
sqrt.subtract(ONE);
}
return new MutableBigInteger[] { sqrt, rem };
}
-------------
PR Comment: https://git.openjdk.org/jdk/pull/19710#issuecomment-2263853409
More information about the core-libs-dev
mailing list