From 98a5b53cdb2d2d11a272aa8c1f0902add36900d8 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Wed, 16 Apr 2025 09:39:20 -0400 Subject: [PATCH 01/55] Add nthRoot(int) methods and optimize pow(int) --- .../share/classes/java/math/BigInteger.java | 189 ++++++++++++++---- .../classes/java/math/MutableBigInteger.java | 80 ++++++++ 2 files changed, 230 insertions(+), 39 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index fb6d1eca3f562..965823b8094d4 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2593,14 +2593,14 @@ public BigInteger pow(int exponent) { return (exponent == 0 ? ONE : this); } - BigInteger partToSquare = this.abs(); + BigInteger base = this.abs(); // Factor out powers of two from the base, as the exponentiation of // these can be done by left shifts only. // The remaining part can then be exponentiated faster. The // powers of two will be multiplied back at the end. - int powersOfTwo = partToSquare.getLowestSetBit(); - long bitsToShiftLong = (long)powersOfTwo * exponent; + int powersOfTwo = base.getLowestSetBit(); + long bitsToShiftLong = (long) powersOfTwo * exponent; if (bitsToShiftLong > Integer.MAX_VALUE) { reportOverflow(); } @@ -2610,19 +2610,19 @@ public BigInteger pow(int exponent) { // Factor the powers of two out quickly by shifting right, if needed. if (powersOfTwo > 0) { - partToSquare = partToSquare.shiftRight(powersOfTwo); - remainingBits = partToSquare.bitLength(); + base = base.shiftRight(powersOfTwo); + remainingBits = base.bitLength(); if (remainingBits == 1) { // Nothing left but +/- 1? - if (signum < 0 && (exponent&1) == 1) { + if (signum < 0 && (exponent & 1) == 1) { return NEGATIVE_ONE.shiftLeft(bitsToShift); } else { return ONE.shiftLeft(bitsToShift); } } } else { - remainingBits = partToSquare.bitLength(); + remainingBits = base.bitLength(); if (remainingBits == 1) { // Nothing left but +/- 1? - if (signum < 0 && (exponent&1) == 1) { + if (signum < 0 && (exponent & 1) == 1) { return NEGATIVE_ONE; } else { return ONE; @@ -2633,67 +2633,54 @@ public BigInteger pow(int exponent) { // This is a quick way to approximate the size of the result, // similar to doing log2[n] * exponent. This will give an upper bound // of how big the result can be, and which algorithm to use. - long scaleFactor = (long)remainingBits * exponent; + long scaleFactor = (long) remainingBits * exponent; // Use slightly different algorithms for small and large operands. // See if the result will safely fit into a long. (Largest 2^63-1) - if (partToSquare.mag.length == 1 && scaleFactor <= 62) { + if (base.mag.length == 1 && scaleFactor <= 62) { // Small number algorithm. Everything fits into a long. - int newSign = (signum <0 && (exponent&1) == 1 ? -1 : 1); - long result = 1; - long baseToPow2 = partToSquare.mag[0] & LONG_MASK; - - int workingExponent = exponent; - - // Perform exponentiation using repeated squaring trick - while (workingExponent != 0) { - if ((workingExponent & 1) == 1) { - result = result * baseToPow2; - } - - if ((workingExponent >>>= 1) != 0) { - baseToPow2 = baseToPow2 * baseToPow2; - } - } + int newSign = (signum < 0 && (exponent & 1) == 1 ? -1 : 1); + long result = unsignedLongPow(base.mag[0] & LONG_MASK, exponent); // Multiply back the powers of two (quickly, by shifting left) if (powersOfTwo > 0) { if (bitsToShift + scaleFactor <= 62) { // Fits in long? return valueOf((result << bitsToShift) * newSign); } else { - return valueOf(result*newSign).shiftLeft(bitsToShift); + return valueOf(result * newSign).shiftLeft(bitsToShift); } } else { - return valueOf(result*newSign); + return valueOf(result * newSign); } } else { - if ((long)bitLength() * exponent / Integer.SIZE > MAX_MAG_LENGTH) { + if ((long) bitLength() * exponent / Integer.SIZE > MAX_MAG_LENGTH) { reportOverflow(); } // Large number algorithm. This is basically identical to - // the algorithm above, but calls multiply() and square() + // the algorithm above, but calls multiply() // which may use more efficient algorithms for large numbers. BigInteger answer = ONE; - int workingExponent = exponent; + final int expZeros = Integer.numberOfLeadingZeros(exponent); + int expLen = Integer.SIZE - expZeros; + int workingExp = exponent << expZeros; // Perform exponentiation using repeated squaring trick - while (workingExponent != 0) { - if ((workingExponent & 1) == 1) { - answer = answer.multiply(partToSquare); - } + for (; expLen > 0; expLen--) { + answer = answer.multiply(answer); + if (workingExp < 0) // leading bit is set + answer = answer.multiply(base); - if ((workingExponent >>>= 1) != 0) { - partToSquare = partToSquare.square(); - } + workingExp <<= 1; } + // Multiply back the (exponentiated) powers of two (quickly, // by shifting left) if (powersOfTwo > 0) { answer = answer.shiftLeft(bitsToShift); } - if (signum < 0 && (exponent&1) == 1) { + if (signum < 0 && (exponent & 1) == 1) { return answer.negate(); } else { return answer; @@ -2701,6 +2688,56 @@ public BigInteger pow(int exponent) { } } + /** + * Computes {@code x^n} using repeated squaring trick. + * Assumes {@code x != 0 && x^n < 2^Long.SIZE}. + */ + static long unsignedLongPow(long x, int n) { + // Double.PRECISION / bitLength(x) is the largest integer e + // such that x^e fits into a double. If e <= 3, we won't use fp arithmetic. + // This allows to use fp arithmetic where possible. + final int maxExp = Math.max(3, Double.PRECISION / bitLengthForLong(x)); + final int maxExpLen = bitLengthForInt(maxExp); + + final int leadingZeros = Integer.numberOfLeadingZeros(n); + int nLen = Integer.SIZE - leadingZeros; + n <<= leadingZeros; + + long pow = 1L; + int blockLen; + for (; nLen > 0; nLen -= blockLen) { + blockLen = maxExpLen < nLen ? maxExpLen : nLen; + // compute pow^(2^blockLen) + if (pow != 1L) { + for (int i = 0; i < blockLen; i++) + pow *= pow; + } + + // add exp to power's exponent + int exp = n >>> -blockLen; + if (exp > 0) { + // adjust exp to fit x^expAdj into a double + int expAdj = exp <= maxExp ? exp : exp >>> 1; + + // don't use fp arithmetic if expAdj <= 3 + long xToExp = expAdj == 1 ? x : + (expAdj == 2 ? x*x : + (expAdj == 3 ? x*x*x : (long) Math.pow(x, expAdj))); + + // append exp's rightmost bit to expAdj + if (expAdj != exp) { + xToExp *= xToExp; + if ((exp & 1) == 1) + xToExp *= x; + } + pow *= xToExp; + } + n <<= blockLen; // shift to next block of bits + } + + return pow; + } + /** * Returns the integer square root of this BigInteger. The integer square * root of the corresponding mathematical integer {@code n} is the largest @@ -2750,6 +2787,80 @@ public BigInteger[] sqrtAndRemainder() { return new BigInteger[] { sqrtRem[0].toBigInteger(), sqrtRem[1].toBigInteger() }; } + /** + * Returns the integer {@code n}th root of this BigInteger. The integer + * {@code n}th root of the corresponding mathematical integer {@code x} has the + * same sign of {@code x}, and its magnitude is the largest integer {@code r} + * such that {@code r**n <= abs(x)}. It is equal to the value of + * {@code (x.signum() * floor(abs(nthRoot(x, n))))}, where {@code nthRoot(x, n)} + * denotes the real {@code n}th root of {@code x} treated as a real. If {@code n} + * is even and this BigInteger is negative, an {@code ArithmeticException} will be + * thrown. + * + *

Note that the magnitude of the integer {@code n}th root will be less than + * the magnitude of the real {@code n}th root if the latter is not representable + * as an integral value. + * + * @param n the root degree + * @return the integer {@code n}th root of {@code this} + * @throws ArithmeticException if {@code n == 0} (Zeroth roots are not + * defined.) + * @throws ArithmeticException if {@code n} is negative. (This would cause the + * operation to yield a non-integer value.) + * @throws ArithmeticException if {@code n} is even and {@code this} is + * negative. (This would cause the operation to + * yield non-real roots.) + * @see #sqrt() + * @since 25 + */ + public BigInteger nthRoot(int n) { + if (n == 1) + return this; + + if (n == 2) + return sqrt(); + + if (n <= 0) + throw new ArithmeticException("Non-positive root degree"); + + if ((n & 1) == 0 && this.signum < 0) + throw new ArithmeticException("Negative radicand with even root degree"); + + return new MutableBigInteger(this.mag).nthRoot(n).toBigInteger(signum); + } + + /** + * Returns an array of two BigIntegers containing the integer {@code n}th root + * {@code r} of {@code this} and its remainder {@code this - r^n}, + * respectively. + * + * @param n the root degree + * @return an array of two BigIntegers with the integer {@code n}th root at + * offset 0 and the remainder at offset 1 + * @throws ArithmeticException if {@code n == 0} (Zeroth roots are not + * defined.) + * @throws ArithmeticException if {@code n} is negative. (This would cause the + * operation to yield a non-integer value.) + * @throws ArithmeticException if {@code n} is even and {@code this} is + * negative. (This would cause the operation to + * yield non-real roots.) + * @see #sqrt() + * @see #sqrtAndRemainder() + * @see #nthRoot(int) + * @since 25 + */ + public BigInteger[] nthRootAndRemainder(int n) { + if (n == 1) + return new BigInteger[] { this, ZERO }; + + if (n == 2) + return sqrtAndRemainder(); + + BigInteger root = nthRoot(n), rem = this.subtract(root.pow(n)); + assert rem.signum == 0 || rem.signum == this.signum; + return new BigInteger[] { root, rem }; + } + /** * Returns a BigInteger whose value is the greatest common divisor of * {@code abs(this)} and {@code abs(val)}. Returns 0 if diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 6ff435ba1ed3d..ef02bd530e201 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1892,6 +1892,86 @@ private boolean unsignedLongCompare(long one, long two) { return (one+Long.MIN_VALUE) > (two+Long.MIN_VALUE); } + /** + * Calculate the integer {@code n}th root {@code floor(nthRoot(this, n))} where + * {@code nthRoot(., n)} denotes the mathematical {@code n}th root. The contents of + * {@code this} are not changed. The value of {@code this} is assumed + * to be non-negative and the root degree {@code n >= 3}. + * + * @implNote The implementation is based on the material in Henry S. Warren, + * Jr., Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 279-282. + * + * @return the integer {@code n}th of {@code this} + */ + MutableBigInteger nthRoot(int n) { + // Special cases. + if (this.isZero() || this.isOne()) + return this; + + final int bitLength = (int) this.bitLength(); + // if this < 2^n, result is unity + if (bitLength <= n) + return new MutableBigInteger(1); + + MutableBigInteger r; + if (bitLength <= Long.SIZE) { + // Initial estimate is the root of the unsigned long value. + final long x = this.toLong(); + // Use fp arithmetic to get an upper bound of the root + final double base = Math.nextUp(x >= 0 ? x : x + 0x1p64); + final double exp = Math.nextUp(1.0 / n); + long rLong = (long) Math.ceil(Math.nextUp(Math.pow(base, exp))); + + if (BigInteger.bitLengthForLong(rLong) * n <= Long.SIZE) { + // Refine the estimate. + do { + long rToN1 = BigInteger.unsignedLongPow(rLong, n - 1); + long rToN = rToN1 * rLong; + if (Long.compareUnsigned(rToN, x) <= 0) + return new MutableBigInteger(rLong); + + rLong -= Long.divideUnsigned(rToN - x, n * rToN1); + } while (true); + } else { // r^n could overflow long range, use MutableBigInteger loop instead + r = new MutableBigInteger(rLong); + } + } else { + // Set up the initial estimate of the iteration. + // Determine an even valued right shift into unsigned long range. + int shift = bitLength - Long.SIZE; + shift += shift & 1; + + // Shift the value into unsigned long range. + r = new MutableBigInteger(this); + r.rightShift(shift); + + // Use the root of the shifted value as an estimate. + final double base = Math.nextUp(r.toLong() + 0x1p64); + final double exp = Math.nextUp(1.0 / n); + r = new MutableBigInteger((long) Math.ceil(Math.nextUp(Math.pow(base, exp)))); + + // Shift the approximate root back into the original range. + r.leftShift(shift >> 1); + } + + // Refine the estimate. + do { + BigInteger rBig = r.toBigInteger(); + BigInteger rToN1 = rBig.pow(n - 1); + MutableBigInteger rToN = new MutableBigInteger(rToN1.multiply(rBig).mag); + if (rToN.compare(this) <= 0) + return r; + + MutableBigInteger rToN1TimesN = new MutableBigInteger(rToN1.multiply(n).mag); + + MutableBigInteger delta = new MutableBigInteger(); + rToN.subtract(this); + rToN.divide(rToN1TimesN, delta, false); + + r.subtract(delta); + } while (true); + } + /** * Calculate the integer square root {@code floor(sqrt(this))} and the remainder * if needed, where {@code sqrt(.)} denotes the mathematical square root. From a3f14892a0673d02bfc402cad6b66d4ba7aedbe5 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Wed, 16 Apr 2025 10:59:52 -0400 Subject: [PATCH 02/55] Remove trailing whitespaces --- src/java.base/share/classes/java/math/BigInteger.java | 4 ++-- src/java.base/share/classes/java/math/MutableBigInteger.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 965823b8094d4..3400668fd6510 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2718,7 +2718,7 @@ static long unsignedLongPow(long x, int n) { if (exp > 0) { // adjust exp to fit x^expAdj into a double int expAdj = exp <= maxExp ? exp : exp >>> 1; - + // don't use fp arithmetic if expAdj <= 3 long xToExp = expAdj == 1 ? x : (expAdj == 2 ? x*x : @@ -2796,7 +2796,7 @@ public BigInteger[] sqrtAndRemainder() { * denotes the real {@code n}th root of {@code x} treated as a real. If {@code n} * is even and this BigInteger is negative, an {@code ArithmeticException} will be * thrown. - * + * *

Note that the magnitude of the integer {@code n}th root will be less than * the magnitude of the real {@code n}th root if the latter is not representable * as an integral value. diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index ef02bd530e201..64f64bfd6ab91 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1929,7 +1929,7 @@ MutableBigInteger nthRoot(int n) { long rToN = rToN1 * rLong; if (Long.compareUnsigned(rToN, x) <= 0) return new MutableBigInteger(rLong); - + rLong -= Long.divideUnsigned(rToN - x, n * rToN1); } while (true); } else { // r^n could overflow long range, use MutableBigInteger loop instead From d280c3708ed58f99c7e0939c26b19969904db54a Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Wed, 16 Apr 2025 18:01:36 -0400 Subject: [PATCH 03/55] Correct loop recurrence according to proof of convergence --- .../share/classes/java/math/MutableBigInteger.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 64f64bfd6ab91..c5dcb9852116c 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1930,7 +1930,12 @@ MutableBigInteger nthRoot(int n) { if (Long.compareUnsigned(rToN, x) <= 0) return new MutableBigInteger(rLong); - rLong -= Long.divideUnsigned(rToN - x, n * rToN1); + // compute rLong - ceil((rToN - x) / (n * rToN1)) + long dividend = rToN - x, divisor = n * rToN1; + if (Long.remainderUnsigned(dividend, divisor) != 0) + rLong--; + + rLong -= Long.divideUnsigned(dividend, divisor); } while (true); } else { // r^n could overflow long range, use MutableBigInteger loop instead r = new MutableBigInteger(rLong); @@ -1962,11 +1967,12 @@ MutableBigInteger nthRoot(int n) { if (rToN.compare(this) <= 0) return r; - MutableBigInteger rToN1TimesN = new MutableBigInteger(rToN1.multiply(n).mag); - + // compute ceil((rToN - this) / (n * rToN1)) MutableBigInteger delta = new MutableBigInteger(); + MutableBigInteger divisor = new MutableBigInteger(rToN1.multiply(n).mag); rToN.subtract(this); - rToN.divide(rToN1TimesN, delta, false); + if(!rToN.divide(divisor, delta).isZero()) + r.subtract(ONE); r.subtract(delta); } while (true); From 91c3d1a98c7b53b5f59ece6a4f669e9f0b789e73 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:59:40 -0400 Subject: [PATCH 04/55] Correct initial estimate of nth root for BigIntegers --- .../classes/java/math/MutableBigInteger.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index c5dcb9852116c..59bd93178538c 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1942,21 +1942,30 @@ MutableBigInteger nthRoot(int n) { } } else { // Set up the initial estimate of the iteration. - // Determine an even valued right shift into unsigned long range. - int shift = bitLength - Long.SIZE; - shift += shift & 1; + // Determine a right shift that is a multiple of n into finite double range. + int shift = Math.max(0, bitLength - Double.MAX_EXPONENT); + int shiftExcess = shift % n; - // Shift the value into unsigned long range. + // Shift the value into finite double range r = new MutableBigInteger(this); r.rightShift(shift); + double base = r.toBigInteger().doubleValue(); + // Complete the shift to a multiple of n, + // avoiding to lose more bits than necessary. + if (shiftExcess != 0) { + int shiftLack = n - shiftExcess; + shift += shiftLack; + base /= Double.valueOf("0x1p" + shiftLack); + } // Use the root of the shifted value as an estimate. - final double base = Math.nextUp(r.toLong() + 0x1p64); + base = Math.nextUp(base); final double exp = Math.nextUp(1.0 / n); - r = new MutableBigInteger((long) Math.ceil(Math.nextUp(Math.pow(base, exp)))); + final double rDouble = Math.ceil(Math.nextUp(Math.pow(base, exp))); + r = new MutableBigInteger(new BigDecimal(rDouble).toBigInteger().mag); // Shift the approximate root back into the original range. - r.leftShift(shift >> 1); + r.leftShift(shift / n); } // Refine the estimate. @@ -1967,7 +1976,7 @@ MutableBigInteger nthRoot(int n) { if (rToN.compare(this) <= 0) return r; - // compute ceil((rToN - this) / (n * rToN1)) + // compute r - ceil((rToN - this) / (n * rToN1)) MutableBigInteger delta = new MutableBigInteger(); MutableBigInteger divisor = new MutableBigInteger(rToN1.multiply(n).mag); rToN.subtract(this); From 1cfb77511468d62de93b894f93ebd6e9fa628f8a Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Thu, 17 Apr 2025 04:02:57 +0200 Subject: [PATCH 05/55] Removed trailing whitespace --- src/java.base/share/classes/java/math/MutableBigInteger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 59bd93178538c..b9ca2bd575f73 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1952,7 +1952,7 @@ MutableBigInteger nthRoot(int n) { double base = r.toBigInteger().doubleValue(); // Complete the shift to a multiple of n, // avoiding to lose more bits than necessary. - if (shiftExcess != 0) { + if (shiftExcess != 0) { int shiftLack = n - shiftExcess; shift += shiftLack; base /= Double.valueOf("0x1p" + shiftLack); From c25bd32800d7ab477f7478e430f3e356838f6136 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Wed, 16 Apr 2025 22:30:10 -0400 Subject: [PATCH 06/55] Avoid an overflow in computing nth root estimate --- .../share/classes/java/math/MutableBigInteger.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 59bd93178538c..3286f7f28b4b0 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1943,18 +1943,18 @@ MutableBigInteger nthRoot(int n) { } else { // Set up the initial estimate of the iteration. // Determine a right shift that is a multiple of n into finite double range. - int shift = Math.max(0, bitLength - Double.MAX_EXPONENT); - int shiftExcess = shift % n; + long shift = Math.max(0, bitLength - Double.MAX_EXPONENT); // use long to avoid overflow later + int shiftExcess = (int) (shift % n); // Shift the value into finite double range r = new MutableBigInteger(this); - r.rightShift(shift); + r.rightShift((int) shift); double base = r.toBigInteger().doubleValue(); // Complete the shift to a multiple of n, // avoiding to lose more bits than necessary. if (shiftExcess != 0) { int shiftLack = n - shiftExcess; - shift += shiftLack; + shift += shiftLack; // shift is long, no overflow base /= Double.valueOf("0x1p" + shiftLack); } @@ -1965,7 +1965,7 @@ MutableBigInteger nthRoot(int n) { r = new MutableBigInteger(new BigDecimal(rDouble).toBigInteger().mag); // Shift the approximate root back into the original range. - r.leftShift(shift / n); + r.leftShift((int) (shift / n)); } // Refine the estimate. From 7ff919b307cc3a6e5cbe82f6f2c04bb48a6246bd Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Thu, 17 Apr 2025 07:22:48 -0400 Subject: [PATCH 07/55] optimize division in loop iteration of nth root --- src/java.base/share/classes/java/math/MutableBigInteger.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index fa1db28265c60..a1229952ba963 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1977,10 +1977,11 @@ MutableBigInteger nthRoot(int n) { return r; // compute r - ceil((rToN - this) / (n * rToN1)) + MutableBigInteger q1 = new MutableBigInteger(); MutableBigInteger delta = new MutableBigInteger(); - MutableBigInteger divisor = new MutableBigInteger(rToN1.multiply(n).mag); rToN.subtract(this); - if(!rToN.divide(divisor, delta).isZero()) + // Don't use conditional-or to ensure to do both divisions + if(rToN.divide(n, q1) != 0 | !q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero()) r.subtract(ONE); r.subtract(delta); From 1f5a9b488a03d6703cb1472c2ba1ab17b6abbc3f Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Thu, 17 Apr 2025 08:16:20 -0400 Subject: [PATCH 08/55] An optimization --- src/java.base/share/classes/java/math/MutableBigInteger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index a1229952ba963..44581076a7e57 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1981,7 +1981,7 @@ MutableBigInteger nthRoot(int n) { MutableBigInteger delta = new MutableBigInteger(); rToN.subtract(this); // Don't use conditional-or to ensure to do both divisions - if(rToN.divide(n, q1) != 0 | !q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero()) + if(rToN.divideOneWord(n, q1) != 0 | !q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero()) r.subtract(ONE); r.subtract(delta); From b6c332055c2edf618c5e0824fa00bc4ca68d3496 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Fri, 18 Apr 2025 01:17:46 +0200 Subject: [PATCH 09/55] Format code --- src/java.base/share/classes/java/math/MutableBigInteger.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 44581076a7e57..992587619421d 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1981,7 +1981,8 @@ MutableBigInteger nthRoot(int n) { MutableBigInteger delta = new MutableBigInteger(); rToN.subtract(this); // Don't use conditional-or to ensure to do both divisions - if(rToN.divideOneWord(n, q1) != 0 | !q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero()) + if (rToN.divideOneWord(n, q1) != 0 + | !q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero()) r.subtract(ONE); r.subtract(delta); From 5d971facb6475cda19e372696c768ab0d6b36443 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Fri, 18 Apr 2025 02:06:28 +0200 Subject: [PATCH 10/55] Correct left shift if shift is zero --- src/java.base/share/classes/java/math/MutableBigInteger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 992587619421d..b3592e1c6334a 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1965,7 +1965,7 @@ MutableBigInteger nthRoot(int n) { r = new MutableBigInteger(new BigDecimal(rDouble).toBigInteger().mag); // Shift the approximate root back into the original range. - r.leftShift((int) (shift / n)); + r.safeLeftShift((int) (shift / n)); } // Refine the estimate. From e459c2317d5a15f216531450c88ad100904d1e55 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Fri, 18 Apr 2025 07:33:16 -0400 Subject: [PATCH 11/55] Memory usage optimization --- src/java.base/share/classes/java/math/MutableBigInteger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index b3592e1c6334a..847a13a7d71fe 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1962,7 +1962,7 @@ MutableBigInteger nthRoot(int n) { base = Math.nextUp(base); final double exp = Math.nextUp(1.0 / n); final double rDouble = Math.ceil(Math.nextUp(Math.pow(base, exp))); - r = new MutableBigInteger(new BigDecimal(rDouble).toBigInteger().mag); + r.copyValue(new BigDecimal(rDouble).toBigInteger().mag); // Shift the approximate root back into the original range. r.safeLeftShift((int) (shift / n)); From 48650de287df59d2390680e6fb6d19e103cce176 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:21:57 -0400 Subject: [PATCH 12/55] An optimization --- src/java.base/share/classes/java/math/MutableBigInteger.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 847a13a7d71fe..594f74ab8f8b0 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1973,13 +1973,12 @@ MutableBigInteger nthRoot(int n) { BigInteger rBig = r.toBigInteger(); BigInteger rToN1 = rBig.pow(n - 1); MutableBigInteger rToN = new MutableBigInteger(rToN1.multiply(rBig).mag); - if (rToN.compare(this) <= 0) + if (rToN.subtract(this) <= 0) return r; - // compute r - ceil((rToN - this) / (n * rToN1)) + // compute r - ceil((r^n - this) / (n * r^(n - 1))) MutableBigInteger q1 = new MutableBigInteger(); MutableBigInteger delta = new MutableBigInteger(); - rToN.subtract(this); // Don't use conditional-or to ensure to do both divisions if (rToN.divideOneWord(n, q1) != 0 | !q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero()) From 54ec8f85a7af92d6e0bffc7bdb51cc0b4081ad6e Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Fri, 18 Apr 2025 19:28:23 -0400 Subject: [PATCH 13/55] BigIntegers nth root's initial estimate optimization --- .../classes/java/math/MutableBigInteger.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 594f74ab8f8b0..2980c6aead8d1 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -158,6 +158,29 @@ private void init(int val) { value = Arrays.copyOfRange(val.value, val.offset, val.offset + intLen); } + /** + * Returns a MutableBigInteger with a magnitude specified by + * the double val. Any fractional part is discarded. + * + * Assume {@code 1.0 <= val < Double.POSITIVE_INFINITY} + */ + static MutableBigInteger valueOf(double val) { + // Translate the double into exponent and significand, according + // to the formulae in JLS, Section 20.10.22. + long valBits = Double.doubleToLongBits(val); + int exponent = (int) ((valBits >> 52) & 0x7ffL) - 1075; + long significand = (valBits & ((1L << 52) - 1)) | (1L << 52); + // At this point, val == significand * 2^exponent. + MutableBigInteger result; + if (exponent > 0) { + result = new MutableBigInteger(significand); + result.leftShift(exponent); + } else { + result = new MutableBigInteger(significand >> -exponent); + } + return result; + } + /** * Makes this number an {@code n}-int number all of whose bits are ones. * Used by Burnikel-Ziegler division. @@ -1947,9 +1970,7 @@ MutableBigInteger nthRoot(int n) { int shiftExcess = (int) (shift % n); // Shift the value into finite double range - r = new MutableBigInteger(this); - r.rightShift((int) shift); - double base = r.toBigInteger().doubleValue(); + double base = this.toBigInteger().shiftRight((int) shift).doubleValue(); // Complete the shift to a multiple of n, // avoiding to lose more bits than necessary. if (shiftExcess != 0) { @@ -1961,8 +1982,7 @@ MutableBigInteger nthRoot(int n) { // Use the root of the shifted value as an estimate. base = Math.nextUp(base); final double exp = Math.nextUp(1.0 / n); - final double rDouble = Math.ceil(Math.nextUp(Math.pow(base, exp))); - r.copyValue(new BigDecimal(rDouble).toBigInteger().mag); + r = valueOf(Math.ceil(Math.nextUp(Math.pow(base, exp)))); // Shift the approximate root back into the original range. r.safeLeftShift((int) (shift / n)); From 3ea519027a188678bfd28d6f4ed126aa70870eef Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Sat, 19 Apr 2025 04:28:48 -0400 Subject: [PATCH 14/55] Extend use cases of MutableBigInteger.valueOf(double) --- .../share/classes/java/math/MutableBigInteger.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 2980c6aead8d1..91a560465c2f1 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -160,11 +160,13 @@ private void init(int val) { /** * Returns a MutableBigInteger with a magnitude specified by - * the double val. Any fractional part is discarded. + * the absolute value of the double val. Any fractional part is discarded. * - * Assume {@code 1.0 <= val < Double.POSITIVE_INFINITY} + * Assume val is in the finite double range. */ static MutableBigInteger valueOf(double val) { + if (Math.abs(val) < 1.0) + return new MutableBigInteger(); // Translate the double into exponent and significand, according // to the formulae in JLS, Section 20.10.22. long valBits = Double.doubleToLongBits(val); From 6c9b364e26e791ea535171875b0b27123cd26c17 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Sat, 19 Apr 2025 05:40:51 -0400 Subject: [PATCH 15/55] An optimization --- src/java.base/share/classes/java/math/MutableBigInteger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 91a560465c2f1..dd70ba2d49fab 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -169,7 +169,7 @@ static MutableBigInteger valueOf(double val) { return new MutableBigInteger(); // Translate the double into exponent and significand, according // to the formulae in JLS, Section 20.10.22. - long valBits = Double.doubleToLongBits(val); + long valBits = Double.doubleToRawLongBits(val); int exponent = (int) ((valBits >> 52) & 0x7ffL) - 1075; long significand = (valBits & ((1L << 52) - 1)) | (1L << 52); // At this point, val == significand * 2^exponent. From 3ca7d29cc0591fdef31642dc90e44fe0e5b90bca Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Sat, 19 Apr 2025 05:56:21 -0400 Subject: [PATCH 16/55] An optimization --- src/java.base/share/classes/java/math/MutableBigInteger.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index dd70ba2d49fab..c13d37c8744d6 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -165,8 +165,9 @@ private void init(int val) { * Assume val is in the finite double range. */ static MutableBigInteger valueOf(double val) { - if (Math.abs(val) < 1.0) - return new MutableBigInteger(); + val = Math.abs(val); + if (val < 0x1p63) + return new MutableBigInteger((long) val); // Translate the double into exponent and significand, according // to the formulae in JLS, Section 20.10.22. long valBits = Double.doubleToRawLongBits(val); From 4516d88d2e8b1227e4ce40ab8035c54316ed088a Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:20:02 +0200 Subject: [PATCH 17/55] Format code --- src/java.base/share/classes/java/math/BigInteger.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 3400668fd6510..30a1b1e417f85 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2663,10 +2663,9 @@ public BigInteger pow(int exponent) { BigInteger answer = ONE; final int expZeros = Integer.numberOfLeadingZeros(exponent); - int expLen = Integer.SIZE - expZeros; int workingExp = exponent << expZeros; // Perform exponentiation using repeated squaring trick - for (; expLen > 0; expLen--) { + for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { answer = answer.multiply(answer); if (workingExp < 0) // leading bit is set answer = answer.multiply(base); From b427091f8c908bf59f790367c455dea6e5f65f24 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:23:21 +0200 Subject: [PATCH 18/55] Format code --- src/java.base/share/classes/java/math/BigInteger.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 30a1b1e417f85..58401af2caa20 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2698,13 +2698,12 @@ static long unsignedLongPow(long x, int n) { final int maxExp = Math.max(3, Double.PRECISION / bitLengthForLong(x)); final int maxExpLen = bitLengthForInt(maxExp); - final int leadingZeros = Integer.numberOfLeadingZeros(n); - int nLen = Integer.SIZE - leadingZeros; - n <<= leadingZeros; + final int nZeros = Integer.numberOfLeadingZeros(n); + n <<= nZeros; long pow = 1L; int blockLen; - for (; nLen > 0; nLen -= blockLen) { + for (int nLen = Integer.SIZE - nZeros; nLen > 0; nLen -= blockLen) { blockLen = maxExpLen < nLen ? maxExpLen : nLen; // compute pow^(2^blockLen) if (pow != 1L) { From 21fbf2784e5b50f8fb11152799f63b0677956c45 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Sun, 20 Apr 2025 18:05:05 +0200 Subject: [PATCH 19/55] Code simplification --- .../share/classes/java/math/MutableBigInteger.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index c13d37c8744d6..38910124bc4eb 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -173,14 +173,9 @@ static MutableBigInteger valueOf(double val) { long valBits = Double.doubleToRawLongBits(val); int exponent = (int) ((valBits >> 52) & 0x7ffL) - 1075; long significand = (valBits & ((1L << 52) - 1)) | (1L << 52); - // At this point, val == significand * 2^exponent. - MutableBigInteger result; - if (exponent > 0) { - result = new MutableBigInteger(significand); - result.leftShift(exponent); - } else { - result = new MutableBigInteger(significand >> -exponent); - } + // At this point, val == significand * 2^exponent, with exponent > 0 + MutableBigInteger result = new MutableBigInteger(significand); + result.leftShift(exponent); return result; } From e11b32f5ef47d9a472bd02c637c37a45ee9ce776 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Mon, 21 Apr 2025 16:27:46 +0200 Subject: [PATCH 20/55] Added reference for proof of convergence in the comment --- src/java.base/share/classes/java/math/MutableBigInteger.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 38910124bc4eb..00d26691355cf 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1921,6 +1921,9 @@ private boolean unsignedLongCompare(long one, long two) { * * @implNote The implementation is based on the material in Henry S. Warren, * Jr., Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 279-282. + * + * Here is a proof for the convergence of the recurrence used by the + * algorithm. * * @return the integer {@code n}th of {@code this} */ From b527fa2a20d850137c680b2c5293958d7652e961 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Mon, 21 Apr 2025 11:00:50 -0400 Subject: [PATCH 21/55] Revert format code changes --- .../share/classes/java/math/BigInteger.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 58401af2caa20..6ff1e8721f2cb 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2600,7 +2600,7 @@ public BigInteger pow(int exponent) { // The remaining part can then be exponentiated faster. The // powers of two will be multiplied back at the end. int powersOfTwo = base.getLowestSetBit(); - long bitsToShiftLong = (long) powersOfTwo * exponent; + long bitsToShiftLong = (long)powersOfTwo * exponent; if (bitsToShiftLong > Integer.MAX_VALUE) { reportOverflow(); } @@ -2613,7 +2613,7 @@ public BigInteger pow(int exponent) { base = base.shiftRight(powersOfTwo); remainingBits = base.bitLength(); if (remainingBits == 1) { // Nothing left but +/- 1? - if (signum < 0 && (exponent & 1) == 1) { + if (signum < 0 && (exponent&1) == 1) { return NEGATIVE_ONE.shiftLeft(bitsToShift); } else { return ONE.shiftLeft(bitsToShift); @@ -2622,7 +2622,7 @@ public BigInteger pow(int exponent) { } else { remainingBits = base.bitLength(); if (remainingBits == 1) { // Nothing left but +/- 1? - if (signum < 0 && (exponent & 1) == 1) { + if (signum < 0 && (exponent&1) == 1) { return NEGATIVE_ONE; } else { return ONE; @@ -2633,13 +2633,13 @@ public BigInteger pow(int exponent) { // This is a quick way to approximate the size of the result, // similar to doing log2[n] * exponent. This will give an upper bound // of how big the result can be, and which algorithm to use. - long scaleFactor = (long) remainingBits * exponent; + long scaleFactor = (long)remainingBits * exponent; // Use slightly different algorithms for small and large operands. // See if the result will safely fit into a long. (Largest 2^63-1) if (base.mag.length == 1 && scaleFactor <= 62) { // Small number algorithm. Everything fits into a long. - int newSign = (signum < 0 && (exponent & 1) == 1 ? -1 : 1); + int newSign = (signum <0 && (exponent&1) == 1 ? -1 : 1); long result = unsignedLongPow(base.mag[0] & LONG_MASK, exponent); // Multiply back the powers of two (quickly, by shifting left) @@ -2647,13 +2647,13 @@ public BigInteger pow(int exponent) { if (bitsToShift + scaleFactor <= 62) { // Fits in long? return valueOf((result << bitsToShift) * newSign); } else { - return valueOf(result * newSign).shiftLeft(bitsToShift); + return valueOf(result*newSign).shiftLeft(bitsToShift); } } else { - return valueOf(result * newSign); + return valueOf(result*newSign); } } else { - if ((long) bitLength() * exponent / Integer.SIZE > MAX_MAG_LENGTH) { + if ((long)bitLength() * exponent / Integer.SIZE > MAX_MAG_LENGTH) { reportOverflow(); } @@ -2679,7 +2679,7 @@ public BigInteger pow(int exponent) { answer = answer.shiftLeft(bitsToShift); } - if (signum < 0 && (exponent & 1) == 1) { + if (signum < 0 && (exponent&1) == 1) { return answer.negate(); } else { return answer; From 1942fd100cca7ae659076c899a560220fd870e20 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Mon, 21 Apr 2025 21:02:26 -0400 Subject: [PATCH 22/55] Suggested change --- autoconf | 1 + .../share/classes/java/math/MutableBigInteger.java | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 160000 autoconf diff --git a/autoconf b/autoconf new file mode 160000 index 0000000000000..fa76d00401519 --- /dev/null +++ b/autoconf @@ -0,0 +1 @@ +Subproject commit fa76d00401519123a782af1963b7d7ba677347b3 diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 00d26691355cf..7192af8562673 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1997,12 +1997,13 @@ MutableBigInteger nthRoot(int n) { if (rToN.subtract(this) <= 0) return r; - // compute r - ceil((r^n - this) / (n * r^(n - 1))) + // compute r - ceil((r^n - this) / (n * r^(n-1))) MutableBigInteger q1 = new MutableBigInteger(); MutableBigInteger delta = new MutableBigInteger(); - // Don't use conditional-or to ensure to do both divisions - if (rToN.divideOneWord(n, q1) != 0 - | !q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero()) + // Don't use conditional-or, in order to do both divisions + // and make delta == (r^n - this) / (n * r^(n-1)) + if ((rToN.divideOneWord(n, q1) != 0) + | (!q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero())) r.subtract(ONE); r.subtract(delta); From 0e1a99e8484b49409a76da0ae3579a3080021b31 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 22 Apr 2025 08:27:53 -0400 Subject: [PATCH 23/55] Delete useless folder --- autoconf | 1 - 1 file changed, 1 deletion(-) delete mode 160000 autoconf diff --git a/autoconf b/autoconf deleted file mode 160000 index fa76d00401519..0000000000000 --- a/autoconf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fa76d00401519123a782af1963b7d7ba677347b3 From 9cd136cc3f4d0ed8f9db696a7470c974eb831b34 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 22 Apr 2025 10:51:16 -0400 Subject: [PATCH 24/55] Optimized computation of nth root's remainder --- .../share/classes/java/math/BigInteger.java | 38 +++++++++---------- .../classes/java/math/MutableBigInteger.java | 29 ++++++++------ 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 6ff1e8721f2cb..07c2dbbe80c6e 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2812,19 +2812,7 @@ public BigInteger[] sqrtAndRemainder() { * @since 25 */ public BigInteger nthRoot(int n) { - if (n == 1) - return this; - - if (n == 2) - return sqrt(); - - if (n <= 0) - throw new ArithmeticException("Non-positive root degree"); - - if ((n & 1) == 0 && this.signum < 0) - throw new ArithmeticException("Negative radicand with even root degree"); - - return new MutableBigInteger(this.mag).nthRoot(n).toBigInteger(signum); + return n == 1 ? this : (n == 2 ? sqrt() : nthRootAndRemainder(n, false)[0]); } /** @@ -2848,15 +2836,25 @@ public BigInteger nthRoot(int n) { * @since 25 */ public BigInteger[] nthRootAndRemainder(int n) { - if (n == 1) - return new BigInteger[] { this, ZERO }; + return n == 1 ? new BigInteger[] { this, ZERO } + : (n == 2 ? sqrtAndRemainder() : nthRootAndRemainder(n, true)); + } - if (n == 2) - return sqrtAndRemainder(); + /** + * Assume {@code n != 1 && n != 2} + */ + private BigInteger[] nthRootAndRemainder(int n, boolean needRemainder) { + if (n <= 0) + throw new ArithmeticException("Non-positive root degree"); - BigInteger root = nthRoot(n), rem = this.subtract(root.pow(n)); - assert rem.signum == 0 || rem.signum == this.signum; - return new BigInteger[] { root, rem }; + if ((n & 1) == 0 && this.signum < 0) + throw new ArithmeticException("Negative radicand with even root degree"); + + MutableBigInteger[] rootRem = new MutableBigInteger(this.mag).nthRootRem(n); + return new BigInteger[] { + rootRem[0].toBigInteger(signum), + needRemainder ? rootRem[1].toBigInteger(signum) : null + }; } /** diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 7192af8562673..fe795f726ded7 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1914,8 +1914,8 @@ private boolean unsignedLongCompare(long one, long two) { } /** - * Calculate the integer {@code n}th root {@code floor(nthRoot(this, n))} where - * {@code nthRoot(., n)} denotes the mathematical {@code n}th root. The contents of + * Calculate the integer {@code n}th root {@code floor(nthRoot(this, n))} and the remainder, + * where {@code nthRoot(., n)} denotes the mathematical {@code n}th root. The contents of * {@code this} are not changed. The value of {@code this} is assumed * to be non-negative and the root degree {@code n >= 3}. * @@ -1925,17 +1925,20 @@ private boolean unsignedLongCompare(long one, long two) { * Here is a proof for the convergence of the recurrence used by the * algorithm. * - * @return the integer {@code n}th of {@code this} + * @return the integer {@code n}th of {@code this} and the remainder */ - MutableBigInteger nthRoot(int n) { + MutableBigInteger[] nthRootRem(int n) { // Special cases. if (this.isZero() || this.isOne()) - return this; + return new MutableBigInteger[] { this, new MutableBigInteger() }; final int bitLength = (int) this.bitLength(); // if this < 2^n, result is unity - if (bitLength <= n) - return new MutableBigInteger(1); + if (bitLength <= n) { + MutableBigInteger rem = new MutableBigInteger(this); + rem.subtract(ONE); + return new MutableBigInteger[] { new MutableBigInteger(1), rem }; + } MutableBigInteger r; if (bitLength <= Long.SIZE) { @@ -1952,7 +1955,9 @@ MutableBigInteger nthRoot(int n) { long rToN1 = BigInteger.unsignedLongPow(rLong, n - 1); long rToN = rToN1 * rLong; if (Long.compareUnsigned(rToN, x) <= 0) - return new MutableBigInteger(rLong); + return new MutableBigInteger[] { + new MutableBigInteger(rLong), new MutableBigInteger(x - rToN) + }; // compute rLong - ceil((rToN - x) / (n * rToN1)) long dividend = rToN - x, divisor = n * rToN1; @@ -1993,16 +1998,16 @@ MutableBigInteger nthRoot(int n) { do { BigInteger rBig = r.toBigInteger(); BigInteger rToN1 = rBig.pow(n - 1); - MutableBigInteger rToN = new MutableBigInteger(rToN1.multiply(rBig).mag); - if (rToN.subtract(this) <= 0) - return r; + MutableBigInteger rem = new MutableBigInteger(rToN1.multiply(rBig).mag); + if (rem.subtract(this) <= 0) + return new MutableBigInteger[] { r, rem }; // compute r - ceil((r^n - this) / (n * r^(n-1))) MutableBigInteger q1 = new MutableBigInteger(); MutableBigInteger delta = new MutableBigInteger(); // Don't use conditional-or, in order to do both divisions // and make delta == (r^n - this) / (n * r^(n-1)) - if ((rToN.divideOneWord(n, q1) != 0) + if ((rem.divideOneWord(n, q1) != 0) | (!q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero())) r.subtract(ONE); From c3bd1b2c461eb3f3f3414186fee1b11adeacb09e Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 22 Apr 2025 11:03:37 -0400 Subject: [PATCH 25/55] Format code --- src/java.base/share/classes/java/math/BigInteger.java | 8 ++++---- .../share/classes/java/math/MutableBigInteger.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 07c2dbbe80c6e..e6f4b823e434a 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2836,8 +2836,8 @@ public BigInteger nthRoot(int n) { * @since 25 */ public BigInteger[] nthRootAndRemainder(int n) { - return n == 1 ? new BigInteger[] { this, ZERO } - : (n == 2 ? sqrtAndRemainder() : nthRootAndRemainder(n, true)); + return n == 1 ? new BigInteger[] { this, ZERO } + : (n == 2 ? sqrtAndRemainder() : nthRootAndRemainder(n, true)); } /** @@ -2852,8 +2852,8 @@ private BigInteger[] nthRootAndRemainder(int n, boolean needRemainder) { MutableBigInteger[] rootRem = new MutableBigInteger(this.mag).nthRootRem(n); return new BigInteger[] { - rootRem[0].toBigInteger(signum), - needRemainder ? rootRem[1].toBigInteger(signum) : null + rootRem[0].toBigInteger(signum), + needRemainder ? rootRem[1].toBigInteger(signum) : null }; } diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index fe795f726ded7..415a41b921cb0 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1956,7 +1956,7 @@ MutableBigInteger[] nthRootRem(int n) { long rToN = rToN1 * rLong; if (Long.compareUnsigned(rToN, x) <= 0) return new MutableBigInteger[] { - new MutableBigInteger(rLong), new MutableBigInteger(x - rToN) + new MutableBigInteger(rLong), new MutableBigInteger(x - rToN) }; // compute rLong - ceil((rToN - x) / (n * rToN1)) From f0d06055b184c962ddb06553ce0f3a8a0eec470e Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 22 Apr 2025 13:00:07 -0400 Subject: [PATCH 26/55] An optimization --- .../classes/java/math/MutableBigInteger.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 415a41b921cb0..32c84ab303201 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1945,9 +1945,9 @@ MutableBigInteger[] nthRootRem(int n) { // Initial estimate is the root of the unsigned long value. final long x = this.toLong(); // Use fp arithmetic to get an upper bound of the root - final double base = Math.nextUp(x >= 0 ? x : x + 0x1p64); - final double exp = Math.nextUp(1.0 / n); - long rLong = (long) Math.ceil(Math.nextUp(Math.pow(base, exp))); + final double rad = Math.nextUp(x >= 0 ? x : x + 0x1p64); + final double approx = n == 3 ? Math.cbrt(rad) : Math.pow(rad, Math.nextUp(1.0 / n)); + long rLong = (long) Math.ceil(Math.nextUp(approx)); if (BigInteger.bitLengthForLong(rLong) * n <= Long.SIZE) { // Refine the estimate. @@ -1976,19 +1976,19 @@ MutableBigInteger[] nthRootRem(int n) { int shiftExcess = (int) (shift % n); // Shift the value into finite double range - double base = this.toBigInteger().shiftRight((int) shift).doubleValue(); + double rad = this.toBigInteger().shiftRight((int) shift).doubleValue(); // Complete the shift to a multiple of n, // avoiding to lose more bits than necessary. if (shiftExcess != 0) { int shiftLack = n - shiftExcess; shift += shiftLack; // shift is long, no overflow - base /= Double.valueOf("0x1p" + shiftLack); + rad /= Double.valueOf("0x1p" + shiftLack); } // Use the root of the shifted value as an estimate. - base = Math.nextUp(base); - final double exp = Math.nextUp(1.0 / n); - r = valueOf(Math.ceil(Math.nextUp(Math.pow(base, exp)))); + rad = Math.nextUp(rad); + final double approx = n == 3 ? Math.cbrt(rad) : Math.pow(rad, Math.nextUp(1.0 / n)); + r = valueOf(Math.ceil(Math.nextUp(approx))); // Shift the approximate root back into the original range. r.safeLeftShift((int) (shift / n)); From f20d19be23acdca2a29e19beda1664b24a5640f4 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Thu, 24 Apr 2025 05:29:46 -0400 Subject: [PATCH 27/55] Optimized BigInteger.pow(int) for single-word values --- .../share/classes/java/math/BigInteger.java | 73 ++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index e6f4b823e434a..a3490e204ca5f 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2660,17 +2660,22 @@ public BigInteger pow(int exponent) { // Large number algorithm. This is basically identical to // the algorithm above, but calls multiply() // which may use more efficient algorithms for large numbers. - BigInteger answer = ONE; + BigInteger answer; + if (base.mag.length == 1) { + answer = unsignedIntPow(base.mag[0], exponent); + } else { + answer = ONE; - final int expZeros = Integer.numberOfLeadingZeros(exponent); - int workingExp = exponent << expZeros; - // Perform exponentiation using repeated squaring trick - for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { - answer = answer.multiply(answer); - if (workingExp < 0) // leading bit is set - answer = answer.multiply(base); + final int expZeros = Integer.numberOfLeadingZeros(exponent); + int workingExp = exponent << expZeros; + // Perform exponentiation using repeated squaring trick + for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { + answer = answer.multiply(answer); + if (workingExp < 0) // leading bit is set + answer = answer.multiply(base); - workingExp <<= 1; + workingExp <<= 1; + } } // Multiply back the (exponentiated) powers of two (quickly, @@ -2687,6 +2692,56 @@ public BigInteger pow(int exponent) { } } + /** + * Computes {@code x^n} using repeated squaring trick. + * Assumes {@code x != 0}. + */ + static BigInteger unsignedIntPow(int x, int n) { + // Double.PRECISION / bitLength(x) is the largest integer e + // such that x^e fits into a double. If e <= 2, we won't use fp arithmetic. + // This allows to use fp arithmetic where possible. + final int maxExp = Math.max(2, Double.PRECISION / bitLengthForInt(x)); + final int maxExpLen = bitLengthForInt(maxExp); + + final int nZeros = Integer.numberOfLeadingZeros(n); + n <<= nZeros; + + BigInteger pow = ONE; + int blockLen; + for (int nLen = Integer.SIZE - nZeros; nLen > 0; nLen -= blockLen) { + blockLen = maxExpLen < nLen ? maxExpLen : nLen; + // compute pow^(2^blockLen) + if (!pow.equals(ONE)) { + for (int i = 0; i < blockLen; i++) + pow = pow.multiply(pow); + } + + // add exp to power's exponent + int exp = n >>> -blockLen; + if (exp > 0) { + // adjust exp to fit x^expAdj into a double + int expAdj = exp <= maxExp ? exp : exp >>> 1; + + long xLong = x & LONG_MASK; + // don't use fp arithmetic if expAdj <= 2 + long xToExpAdj = expAdj == 1 ? xLong : + (expAdj == 2 ? xLong*xLong : (long) Math.pow(xLong, expAdj)); + + // append exp's rightmost bit to expAdj + BigInteger xToExp = new BigInteger(1, new int[] { (int) (xToExpAdj >>> 32), (int) xToExpAdj }); + if (expAdj != exp) { + xToExp = xToExp.multiply(xToExp); + if ((exp & 1) == 1) + xToExp = xToExp.multiply(xLong); + } + pow = pow.multiply(xToExp); + } + n <<= blockLen; // shift to next block of bits + } + + return pow; + } + /** * Computes {@code x^n} using repeated squaring trick. * Assumes {@code x != 0 && x^n < 2^Long.SIZE}. From 8676af7c8d9ae00b6559f0a8ca946847213b7eb6 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Thu, 24 Apr 2025 06:31:30 -0400 Subject: [PATCH 28/55] Optimized repeated squaring trick using cache for powers --- .../share/classes/java/math/BigInteger.java | 72 ++++++++++++------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index a3490e204ca5f..e6030c21c3a01 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2702,6 +2702,7 @@ static BigInteger unsignedIntPow(int x, int n) { // This allows to use fp arithmetic where possible. final int maxExp = Math.max(2, Double.PRECISION / bitLengthForInt(x)); final int maxExpLen = bitLengthForInt(maxExp); + final BigInteger[] powerCache = new BigInteger[1 << maxExpLen]; final int nZeros = Integer.numberOfLeadingZeros(n); n <<= nZeros; @@ -2719,20 +2720,28 @@ static BigInteger unsignedIntPow(int x, int n) { // add exp to power's exponent int exp = n >>> -blockLen; if (exp > 0) { - // adjust exp to fit x^expAdj into a double - int expAdj = exp <= maxExp ? exp : exp >>> 1; - - long xLong = x & LONG_MASK; - // don't use fp arithmetic if expAdj <= 2 - long xToExpAdj = expAdj == 1 ? xLong : - (expAdj == 2 ? xLong*xLong : (long) Math.pow(xLong, expAdj)); - - // append exp's rightmost bit to expAdj - BigInteger xToExp = new BigInteger(1, new int[] { (int) (xToExpAdj >>> 32), (int) xToExpAdj }); - if (expAdj != exp) { - xToExp = xToExp.multiply(xToExp); - if ((exp & 1) == 1) - xToExp = xToExp.multiply(xLong); + BigInteger xToExp = powerCache[exp]; + if (xToExp == null) { + // adjust exp to fit x^expAdj into a double + int expAdj = exp <= maxExp ? exp : exp >>> 1; + + long xLong = x & LONG_MASK; + // don't use fp arithmetic if expAdj <= 2 + long xToExpAdj = expAdj == 1 ? xLong : + (expAdj == 2 ? xLong*xLong : (long) Math.pow(xLong, expAdj)); + + xToExp = new BigInteger(1, new int[] { (int) (xToExpAdj >>> 32), (int) xToExpAdj }); + powerCache[expAdj] = xToExp; + // append exp's rightmost bit to expAdj + if (expAdj != exp) { + xToExp = xToExp.multiply(xToExp); + powerCache[expAdj << 1] = xToExp; + + if ((exp & 1) == 1) { + xToExp = xToExp.multiply(xLong); + powerCache[exp] = xToExp; + } + } } pow = pow.multiply(xToExp); } @@ -2752,6 +2761,7 @@ static long unsignedLongPow(long x, int n) { // This allows to use fp arithmetic where possible. final int maxExp = Math.max(3, Double.PRECISION / bitLengthForLong(x)); final int maxExpLen = bitLengthForInt(maxExp); + final long[] powerCache = new long[1 << maxExpLen]; final int nZeros = Integer.numberOfLeadingZeros(n); n <<= nZeros; @@ -2769,19 +2779,27 @@ static long unsignedLongPow(long x, int n) { // add exp to power's exponent int exp = n >>> -blockLen; if (exp > 0) { - // adjust exp to fit x^expAdj into a double - int expAdj = exp <= maxExp ? exp : exp >>> 1; - - // don't use fp arithmetic if expAdj <= 3 - long xToExp = expAdj == 1 ? x : - (expAdj == 2 ? x*x : - (expAdj == 3 ? x*x*x : (long) Math.pow(x, expAdj))); - - // append exp's rightmost bit to expAdj - if (expAdj != exp) { - xToExp *= xToExp; - if ((exp & 1) == 1) - xToExp *= x; + long xToExp = powerCache[exp]; + if (xToExp == 0) { + // adjust exp to fit x^expAdj into a double + int expAdj = exp <= maxExp ? exp : exp >>> 1; + + // don't use fp arithmetic if expAdj <= 3 + xToExp = expAdj == 1 ? x : + (expAdj == 2 ? x*x : + (expAdj == 3 ? x*x*x : (long) Math.pow(x, expAdj))); + powerCache[expAdj] = xToExp; + + // append exp's rightmost bit to expAdj + if (expAdj != exp) { + xToExp *= xToExp; + powerCache[expAdj << 1] = xToExp; + + if ((exp & 1) == 1) { + xToExp *= x; + powerCache[exp] = xToExp; + } + } } pow *= xToExp; } From 3cf820b2d74a885ca05f6dbd446670d615aa139c Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Thu, 24 Apr 2025 10:59:36 -0400 Subject: [PATCH 29/55] Some optimizations --- src/java.base/share/classes/java/math/BigInteger.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index e6030c21c3a01..c2aa02ac9ba4a 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2589,7 +2589,7 @@ public BigInteger pow(int exponent) { if (exponent < 0) { throw new ArithmeticException("Negative exponent"); } - if (signum == 0) { + if (signum == 0 || this.equals(ONE)) { return (exponent == 0 ? ONE : this); } @@ -2696,7 +2696,9 @@ public BigInteger pow(int exponent) { * Computes {@code x^n} using repeated squaring trick. * Assumes {@code x != 0}. */ - static BigInteger unsignedIntPow(int x, int n) { + private static BigInteger unsignedIntPow(int x, int n) { + if (x == 1) + return ONE; // Double.PRECISION / bitLength(x) is the largest integer e // such that x^e fits into a double. If e <= 2, we won't use fp arithmetic. // This allows to use fp arithmetic where possible. @@ -2756,6 +2758,11 @@ static BigInteger unsignedIntPow(int x, int n) { * Assumes {@code x != 0 && x^n < 2^Long.SIZE}. */ static long unsignedLongPow(long x, int n) { + if (x == 1L) + return 1L; + + if (x == 2L) + return 1L << n; // Double.PRECISION / bitLength(x) is the largest integer e // such that x^e fits into a double. If e <= 3, we won't use fp arithmetic. // This allows to use fp arithmetic where possible. From 23914e8ac6a61ce495219a7381fdf85bd82cfd82 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Thu, 24 Apr 2025 11:10:56 -0400 Subject: [PATCH 30/55] Systematization of special cases in BigInteger.pow(int) --- src/java.base/share/classes/java/math/BigInteger.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index c2aa02ac9ba4a..cc0ea647255a9 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2589,9 +2589,11 @@ public BigInteger pow(int exponent) { if (exponent < 0) { throw new ArithmeticException("Negative exponent"); } - if (signum == 0 || this.equals(ONE)) { - return (exponent == 0 ? ONE : this); - } + if (exponent == 0 || this.equals(ONE)) + return ONE; + + if (signum == 0) + return ZERO; BigInteger base = this.abs(); From bf099e4e426b9e21ff67ce48b1ae6ac022a2a3be Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Sat, 26 Apr 2025 11:14:18 -0400 Subject: [PATCH 31/55] Optimized nth root iteration loop --- .../share/classes/java/math/BigInteger.java | 38 +++--- .../classes/java/math/MutableBigInteger.java | 126 ++++++++++-------- 2 files changed, 90 insertions(+), 74 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index cc0ea647255a9..4d8154ed7c71a 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2894,7 +2894,19 @@ public BigInteger[] sqrtAndRemainder() { * @since 25 */ public BigInteger nthRoot(int n) { - return n == 1 ? this : (n == 2 ? sqrt() : nthRootAndRemainder(n, false)[0]); + if (n == 1) + return this; + + if (n == 2) + return sqrt(); + + if (n <= 0) + throw new ArithmeticException("Non-positive root degree"); + + if ((n & 1) == 0 && this.signum < 0) + throw new ArithmeticException("Negative radicand with even root degree"); + + return new MutableBigInteger(this.mag).nthRoot(n).toBigInteger(signum); } /** @@ -2918,25 +2930,15 @@ public BigInteger nthRoot(int n) { * @since 25 */ public BigInteger[] nthRootAndRemainder(int n) { - return n == 1 ? new BigInteger[] { this, ZERO } - : (n == 2 ? sqrtAndRemainder() : nthRootAndRemainder(n, true)); - } + if (n == 1) + return new BigInteger[] { this, ZERO }; - /** - * Assume {@code n != 1 && n != 2} - */ - private BigInteger[] nthRootAndRemainder(int n, boolean needRemainder) { - if (n <= 0) - throw new ArithmeticException("Non-positive root degree"); + if (n == 2) + return sqrtAndRemainder(); - if ((n & 1) == 0 && this.signum < 0) - throw new ArithmeticException("Negative radicand with even root degree"); - - MutableBigInteger[] rootRem = new MutableBigInteger(this.mag).nthRootRem(n); - return new BigInteger[] { - rootRem[0].toBigInteger(signum), - needRemainder ? rootRem[1].toBigInteger(signum) : null - }; + BigInteger root = nthRoot(n), rem = this.subtract(root.pow(n)); + assert rem.signum == 0 || rem.signum == this.signum; + return new BigInteger[] { root, rem }; } /** diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 32c84ab303201..7e6dc40b16931 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1914,33 +1914,32 @@ private boolean unsignedLongCompare(long one, long two) { } /** - * Calculate the integer {@code n}th root {@code floor(nthRoot(this, n))} and the remainder, - * where {@code nthRoot(., n)} denotes the mathematical {@code n}th root. The contents of + * Calculate the integer {@code n}th root {@code floor(nthRoot(this, n))} where + * {@code nthRoot(., n)} denotes the mathematical {@code n}th root. The contents of * {@code this} are not changed. The value of {@code this} is assumed * to be non-negative and the root degree {@code n >= 3}. * - * @implNote The implementation is based on the material in Henry S. Warren, - * Jr., Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 279-282. - * - * Here is a proof for the convergence of the recurrence used by the - * algorithm. + * @implNote The implementation is based on the material in Richard P. Brent + * and Paul Zimmermann, + * Modern Computer Arithmetic, 27-28. * - * @return the integer {@code n}th of {@code this} and the remainder + * @return the integer {@code n}th of {@code this} */ - MutableBigInteger[] nthRootRem(int n) { + MutableBigInteger nthRoot(int n) { // Special cases. if (this.isZero() || this.isOne()) - return new MutableBigInteger[] { this, new MutableBigInteger() }; + return this; final int bitLength = (int) this.bitLength(); // if this < 2^n, result is unity if (bitLength <= n) { MutableBigInteger rem = new MutableBigInteger(this); rem.subtract(ONE); - return new MutableBigInteger[] { new MutableBigInteger(1), rem }; + return new MutableBigInteger(1); } MutableBigInteger r; + long shift = 0L; if (bitLength <= Long.SIZE) { // Initial estimate is the root of the unsigned long value. final long x = this.toLong(); @@ -1949,70 +1948,85 @@ MutableBigInteger[] nthRootRem(int n) { final double approx = n == 3 ? Math.cbrt(rad) : Math.pow(rad, Math.nextUp(1.0 / n)); long rLong = (long) Math.ceil(Math.nextUp(approx)); - if (BigInteger.bitLengthForLong(rLong) * n <= Long.SIZE) { + if (BigInteger.bitLengthForLong(rLong) * (n - 1) <= Long.SIZE) { // Refine the estimate. + long r1 = rLong; do { + rLong = r1; long rToN1 = BigInteger.unsignedLongPow(rLong, n - 1); - long rToN = rToN1 * rLong; - if (Long.compareUnsigned(rToN, x) <= 0) - return new MutableBigInteger[] { - new MutableBigInteger(rLong), new MutableBigInteger(x - rToN) - }; - - // compute rLong - ceil((rToN - x) / (n * rToN1)) - long dividend = rToN - x, divisor = n * rToN1; - if (Long.remainderUnsigned(dividend, divisor) != 0) - rLong--; - - rLong -= Long.divideUnsigned(dividend, divisor); - } while (true); - } else { // r^n could overflow long range, use MutableBigInteger loop instead + r1 = ((n - 1) * rLong + x / rToN1) / n; + } while (r1 < rLong); // Terminate when non-decreasing. + + return new MutableBigInteger(rLong); + } else { // r^(n - 1) could overflow long range, use MutableBigInteger loop instead r = new MutableBigInteger(rLong); } } else { // Set up the initial estimate of the iteration. // Determine a right shift that is a multiple of n into finite double range. - long shift = Math.max(0, bitLength - Double.MAX_EXPONENT); // use long to avoid overflow later - int shiftExcess = (int) (shift % n); - - // Shift the value into finite double range - double rad = this.toBigInteger().shiftRight((int) shift).doubleValue(); - // Complete the shift to a multiple of n, - // avoiding to lose more bits than necessary. - if (shiftExcess != 0) { - int shiftLack = n - shiftExcess; - shift += shiftLack; // shift is long, no overflow - rad /= Double.valueOf("0x1p" + shiftLack); + double rad; + if (bitLength > Double.MAX_EXPONENT) { + shift = bitLength - Double.MAX_EXPONENT; + int shiftExcess = (int) (shift % n); + + // Shift the value into finite double range + rad = this.toBigInteger().shiftRight((int) shift).doubleValue(); + // Complete the shift to a multiple of n, + // avoiding to lose more bits than necessary. + if (shiftExcess != 0) { + int shiftLack = n - shiftExcess; + shift += shiftLack; // shift is long, no overflow + rad /= Double.valueOf("0x1p" + shiftLack); + } + } else { + rad = this.toBigInteger().doubleValue(); } // Use the root of the shifted value as an estimate. rad = Math.nextUp(rad); final double approx = n == 3 ? Math.cbrt(rad) : Math.pow(rad, Math.nextUp(1.0 / n)); r = valueOf(Math.ceil(Math.nextUp(approx))); + } - // Shift the approximate root back into the original range. - r.safeLeftShift((int) (shift / n)); + // Refine the estimate, avoiding to compute non-significant bits + final int trailingZeros = this.getLowestSetBit(); + int rootShift = (int) (shift / n); + for (int rootBits = (int) r.bitLength(); rootShift >= rootBits + && rootBits > 0; rootBits <<= 1) { // avoid overflow + r.leftShift(rootBits); + rootShift -= rootBits; + + // Remove useless bits from the radicand + MutableBigInteger x = new MutableBigInteger(this); + int removedBits = rootShift * n; + x.rightShift(removedBits); + if (removedBits > trailingZeros) + x.add(ONE); // round up to ensure r1 is an upper bound of the root + + MutableBigInteger rToN1 = new MutableBigInteger(r.toBigInteger().pow(n - 1).mag); + MutableBigInteger dividend = new MutableBigInteger(); + r.mul(n - 1, dividend); + MutableBigInteger xDivRToN1 = new MutableBigInteger(); + x.divide(rToN1, xDivRToN1, false); + dividend.add(xDivRToN1); + dividend.divideOneWord(n, r); } // Refine the estimate. + r.safeLeftShift(rootShift); + MutableBigInteger r1 = r; do { - BigInteger rBig = r.toBigInteger(); - BigInteger rToN1 = rBig.pow(n - 1); - MutableBigInteger rem = new MutableBigInteger(rToN1.multiply(rBig).mag); - if (rem.subtract(this) <= 0) - return new MutableBigInteger[] { r, rem }; - - // compute r - ceil((r^n - this) / (n * r^(n-1))) - MutableBigInteger q1 = new MutableBigInteger(); - MutableBigInteger delta = new MutableBigInteger(); - // Don't use conditional-or, in order to do both divisions - // and make delta == (r^n - this) / (n * r^(n-1)) - if ((rem.divideOneWord(n, q1) != 0) - | (!q1.divide(new MutableBigInteger(rToN1.mag), delta).isZero())) - r.subtract(ONE); - - r.subtract(delta); - } while (true); + r = r1; + MutableBigInteger rToN1 = new MutableBigInteger(r.toBigInteger().pow(n - 1).mag); + MutableBigInteger dividend = new MutableBigInteger(); + r.mul(n - 1, dividend); + MutableBigInteger xDivRToN1 = new MutableBigInteger(); + this.divide(rToN1, xDivRToN1); + dividend.add(xDivRToN1); + dividend.divideOneWord(n, r1); + } while (r1.compare(r) < 0); // Terminate when non-decreasing. + + return r; } /** From 7ceae871018b82a104eeb448b1af0c9ccbf32d4e Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Sat, 26 Apr 2025 12:25:13 -0400 Subject: [PATCH 32/55] Moved nth-root implementation to a dependent PR --- .../share/classes/java/math/BigInteger.java | 74 ----------- .../classes/java/math/MutableBigInteger.java | 116 ------------------ 2 files changed, 190 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 4d8154ed7c71a..17c1e2de57e50 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2867,80 +2867,6 @@ public BigInteger[] sqrtAndRemainder() { return new BigInteger[] { sqrtRem[0].toBigInteger(), sqrtRem[1].toBigInteger() }; } - /** - * Returns the integer {@code n}th root of this BigInteger. The integer - * {@code n}th root of the corresponding mathematical integer {@code x} has the - * same sign of {@code x}, and its magnitude is the largest integer {@code r} - * such that {@code r**n <= abs(x)}. It is equal to the value of - * {@code (x.signum() * floor(abs(nthRoot(x, n))))}, where {@code nthRoot(x, n)} - * denotes the real {@code n}th root of {@code x} treated as a real. If {@code n} - * is even and this BigInteger is negative, an {@code ArithmeticException} will be - * thrown. - * - *

Note that the magnitude of the integer {@code n}th root will be less than - * the magnitude of the real {@code n}th root if the latter is not representable - * as an integral value. - * - * @param n the root degree - * @return the integer {@code n}th root of {@code this} - * @throws ArithmeticException if {@code n == 0} (Zeroth roots are not - * defined.) - * @throws ArithmeticException if {@code n} is negative. (This would cause the - * operation to yield a non-integer value.) - * @throws ArithmeticException if {@code n} is even and {@code this} is - * negative. (This would cause the operation to - * yield non-real roots.) - * @see #sqrt() - * @since 25 - */ - public BigInteger nthRoot(int n) { - if (n == 1) - return this; - - if (n == 2) - return sqrt(); - - if (n <= 0) - throw new ArithmeticException("Non-positive root degree"); - - if ((n & 1) == 0 && this.signum < 0) - throw new ArithmeticException("Negative radicand with even root degree"); - - return new MutableBigInteger(this.mag).nthRoot(n).toBigInteger(signum); - } - - /** - * Returns an array of two BigIntegers containing the integer {@code n}th root - * {@code r} of {@code this} and its remainder {@code this - r^n}, - * respectively. - * - * @param n the root degree - * @return an array of two BigIntegers with the integer {@code n}th root at - * offset 0 and the remainder at offset 1 - * @throws ArithmeticException if {@code n == 0} (Zeroth roots are not - * defined.) - * @throws ArithmeticException if {@code n} is negative. (This would cause the - * operation to yield a non-integer value.) - * @throws ArithmeticException if {@code n} is even and {@code this} is - * negative. (This would cause the operation to - * yield non-real roots.) - * @see #sqrt() - * @see #sqrtAndRemainder() - * @see #nthRoot(int) - * @since 25 - */ - public BigInteger[] nthRootAndRemainder(int n) { - if (n == 1) - return new BigInteger[] { this, ZERO }; - - if (n == 2) - return sqrtAndRemainder(); - - BigInteger root = nthRoot(n), rem = this.subtract(root.pow(n)); - assert rem.signum == 0 || rem.signum == this.signum; - return new BigInteger[] { root, rem }; - } - /** * Returns a BigInteger whose value is the greatest common divisor of * {@code abs(this)} and {@code abs(val)}. Returns 0 if diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index 7e6dc40b16931..b5308874d929d 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -1913,122 +1913,6 @@ private boolean unsignedLongCompare(long one, long two) { return (one+Long.MIN_VALUE) > (two+Long.MIN_VALUE); } - /** - * Calculate the integer {@code n}th root {@code floor(nthRoot(this, n))} where - * {@code nthRoot(., n)} denotes the mathematical {@code n}th root. The contents of - * {@code this} are not changed. The value of {@code this} is assumed - * to be non-negative and the root degree {@code n >= 3}. - * - * @implNote The implementation is based on the material in Richard P. Brent - * and Paul Zimmermann, - * Modern Computer Arithmetic, 27-28. - * - * @return the integer {@code n}th of {@code this} - */ - MutableBigInteger nthRoot(int n) { - // Special cases. - if (this.isZero() || this.isOne()) - return this; - - final int bitLength = (int) this.bitLength(); - // if this < 2^n, result is unity - if (bitLength <= n) { - MutableBigInteger rem = new MutableBigInteger(this); - rem.subtract(ONE); - return new MutableBigInteger(1); - } - - MutableBigInteger r; - long shift = 0L; - if (bitLength <= Long.SIZE) { - // Initial estimate is the root of the unsigned long value. - final long x = this.toLong(); - // Use fp arithmetic to get an upper bound of the root - final double rad = Math.nextUp(x >= 0 ? x : x + 0x1p64); - final double approx = n == 3 ? Math.cbrt(rad) : Math.pow(rad, Math.nextUp(1.0 / n)); - long rLong = (long) Math.ceil(Math.nextUp(approx)); - - if (BigInteger.bitLengthForLong(rLong) * (n - 1) <= Long.SIZE) { - // Refine the estimate. - long r1 = rLong; - do { - rLong = r1; - long rToN1 = BigInteger.unsignedLongPow(rLong, n - 1); - r1 = ((n - 1) * rLong + x / rToN1) / n; - } while (r1 < rLong); // Terminate when non-decreasing. - - return new MutableBigInteger(rLong); - } else { // r^(n - 1) could overflow long range, use MutableBigInteger loop instead - r = new MutableBigInteger(rLong); - } - } else { - // Set up the initial estimate of the iteration. - // Determine a right shift that is a multiple of n into finite double range. - double rad; - if (bitLength > Double.MAX_EXPONENT) { - shift = bitLength - Double.MAX_EXPONENT; - int shiftExcess = (int) (shift % n); - - // Shift the value into finite double range - rad = this.toBigInteger().shiftRight((int) shift).doubleValue(); - // Complete the shift to a multiple of n, - // avoiding to lose more bits than necessary. - if (shiftExcess != 0) { - int shiftLack = n - shiftExcess; - shift += shiftLack; // shift is long, no overflow - rad /= Double.valueOf("0x1p" + shiftLack); - } - } else { - rad = this.toBigInteger().doubleValue(); - } - - // Use the root of the shifted value as an estimate. - rad = Math.nextUp(rad); - final double approx = n == 3 ? Math.cbrt(rad) : Math.pow(rad, Math.nextUp(1.0 / n)); - r = valueOf(Math.ceil(Math.nextUp(approx))); - } - - // Refine the estimate, avoiding to compute non-significant bits - final int trailingZeros = this.getLowestSetBit(); - int rootShift = (int) (shift / n); - for (int rootBits = (int) r.bitLength(); rootShift >= rootBits - && rootBits > 0; rootBits <<= 1) { // avoid overflow - r.leftShift(rootBits); - rootShift -= rootBits; - - // Remove useless bits from the radicand - MutableBigInteger x = new MutableBigInteger(this); - int removedBits = rootShift * n; - x.rightShift(removedBits); - if (removedBits > trailingZeros) - x.add(ONE); // round up to ensure r1 is an upper bound of the root - - MutableBigInteger rToN1 = new MutableBigInteger(r.toBigInteger().pow(n - 1).mag); - MutableBigInteger dividend = new MutableBigInteger(); - r.mul(n - 1, dividend); - MutableBigInteger xDivRToN1 = new MutableBigInteger(); - x.divide(rToN1, xDivRToN1, false); - dividend.add(xDivRToN1); - dividend.divideOneWord(n, r); - } - - // Refine the estimate. - r.safeLeftShift(rootShift); - MutableBigInteger r1 = r; - do { - r = r1; - MutableBigInteger rToN1 = new MutableBigInteger(r.toBigInteger().pow(n - 1).mag); - MutableBigInteger dividend = new MutableBigInteger(); - r.mul(n - 1, dividend); - MutableBigInteger xDivRToN1 = new MutableBigInteger(); - this.divide(rToN1, xDivRToN1); - dividend.add(xDivRToN1); - dividend.divideOneWord(n, r1); - } while (r1.compare(r) < 0); // Terminate when non-decreasing. - - return r; - } - /** * Calculate the integer square root {@code floor(sqrt(this))} and the remainder * if needed, where {@code sqrt(.)} denotes the mathematical square root. From cb61ddc1aa98196e0757e04b929fb8b2d38540c9 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Sat, 26 Apr 2025 12:40:56 -0400 Subject: [PATCH 33/55] Removed method used by nth-root --- .../classes/java/math/MutableBigInteger.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java index b5308874d929d..6ff435ba1ed3d 100644 --- a/src/java.base/share/classes/java/math/MutableBigInteger.java +++ b/src/java.base/share/classes/java/math/MutableBigInteger.java @@ -158,27 +158,6 @@ private void init(int val) { value = Arrays.copyOfRange(val.value, val.offset, val.offset + intLen); } - /** - * Returns a MutableBigInteger with a magnitude specified by - * the absolute value of the double val. Any fractional part is discarded. - * - * Assume val is in the finite double range. - */ - static MutableBigInteger valueOf(double val) { - val = Math.abs(val); - if (val < 0x1p63) - return new MutableBigInteger((long) val); - // Translate the double into exponent and significand, according - // to the formulae in JLS, Section 20.10.22. - long valBits = Double.doubleToRawLongBits(val); - int exponent = (int) ((valBits >> 52) & 0x7ffL) - 1075; - long significand = (valBits & ((1L << 52) - 1)) | (1L << 52); - // At this point, val == significand * 2^exponent, with exponent > 0 - MutableBigInteger result = new MutableBigInteger(significand); - result.leftShift(exponent); - return result; - } - /** * Makes this number an {@code n}-int number all of whose bits are ones. * Used by Burnikel-Ziegler division. From 10e122e00bab51d23e639fd24e21d55d8c5953b4 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Sat, 26 Apr 2025 15:08:06 -0400 Subject: [PATCH 34/55] Put power's computation in a stand-alone method --- .../share/classes/java/math/BigInteger.java | 145 +++++++++++------- 1 file changed, 92 insertions(+), 53 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 17c1e2de57e50..70c9cb0ea408f 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2703,7 +2703,7 @@ private static BigInteger unsignedIntPow(int x, int n) { return ONE; // Double.PRECISION / bitLength(x) is the largest integer e // such that x^e fits into a double. If e <= 2, we won't use fp arithmetic. - // This allows to use fp arithmetic where possible. + // This allows to use fp arithmetic in computePower(), where possible. final int maxExp = Math.max(2, Double.PRECISION / bitLengthForInt(x)); final int maxExpLen = bitLengthForInt(maxExp); final BigInteger[] powerCache = new BigInteger[1 << maxExpLen]; @@ -2723,38 +2723,58 @@ private static BigInteger unsignedIntPow(int x, int n) { // add exp to power's exponent int exp = n >>> -blockLen; - if (exp > 0) { - BigInteger xToExp = powerCache[exp]; - if (xToExp == null) { - // adjust exp to fit x^expAdj into a double - int expAdj = exp <= maxExp ? exp : exp >>> 1; - - long xLong = x & LONG_MASK; - // don't use fp arithmetic if expAdj <= 2 - long xToExpAdj = expAdj == 1 ? xLong : - (expAdj == 2 ? xLong*xLong : (long) Math.pow(xLong, expAdj)); - - xToExp = new BigInteger(1, new int[] { (int) (xToExpAdj >>> 32), (int) xToExpAdj }); - powerCache[expAdj] = xToExp; - // append exp's rightmost bit to expAdj - if (expAdj != exp) { - xToExp = xToExp.multiply(xToExp); - powerCache[expAdj << 1] = xToExp; - - if ((exp & 1) == 1) { - xToExp = xToExp.multiply(xLong); - powerCache[exp] = xToExp; - } - } - } - pow = pow.multiply(xToExp); - } + if (exp > 0) + pow = pow.multiply(computePower(powerCache, x & LONG_MASK, exp, maxExp)); + n <<= blockLen; // shift to next block of bits } return pow; } + /** + * Returns {@code x^exp}. This method is used by {@code unsignedIntPow(int, int)}. + * Assumes {@code maxExp == max(2, Double.PRECISION / bitLength(x))}. + */ + private static BigInteger computePower(BigInteger[] powerCache, long x, int exp, int maxExp) { + BigInteger xToExp = powerCache[exp]; + if (xToExp == null) { + if (exp <= maxExp) { + // don't use fp arithmetic if exp <= 2 + long pow = exp == 1 ? x : + (exp == 2 ? x*x : (long) Math.pow(x, exp)); + + xToExp = new BigInteger(1, new int[] { (int) (pow >>> 32), (int) pow }); + powerCache[exp] = xToExp; + } else { + // adjust exp to fit x^expAdj into a double + final int expAdj = exp >>> 1; + + xToExp = powerCache[expAdj << 1]; + if (xToExp == null) { // compute x^(expAdj << 1) + xToExp = powerCache[expAdj]; + if (xToExp == null) { // compute x^expAdj + // don't use fp arithmetic if expAdj <= 2 + long xToExpAdj = expAdj == 1 ? x : + (expAdj == 2 ? x*x : (long) Math.pow(x, expAdj)); + + xToExp = new BigInteger(1, new int[] { (int) (xToExpAdj >>> 32), (int) xToExpAdj }); + powerCache[expAdj] = xToExp; + } + xToExp = xToExp.multiply(xToExp); + powerCache[expAdj << 1] = xToExp; + } + + // append exp's rightmost bit to expAdj + if ((exp & 1) == 1) { + xToExp = xToExp.multiply(x); + powerCache[exp] = xToExp; + } + } + } + return xToExp; + } + /** * Computes {@code x^n} using repeated squaring trick. * Assumes {@code x != 0 && x^n < 2^Long.SIZE}. @@ -2767,7 +2787,7 @@ static long unsignedLongPow(long x, int n) { return 1L << n; // Double.PRECISION / bitLength(x) is the largest integer e // such that x^e fits into a double. If e <= 3, we won't use fp arithmetic. - // This allows to use fp arithmetic where possible. + // This allows to use fp arithmetic in computePower(), where possible. final int maxExp = Math.max(3, Double.PRECISION / bitLengthForLong(x)); final int maxExpLen = bitLengthForInt(maxExp); final long[] powerCache = new long[1 << maxExpLen]; @@ -2787,37 +2807,56 @@ static long unsignedLongPow(long x, int n) { // add exp to power's exponent int exp = n >>> -blockLen; - if (exp > 0) { - long xToExp = powerCache[exp]; - if (xToExp == 0) { - // adjust exp to fit x^expAdj into a double - int expAdj = exp <= maxExp ? exp : exp >>> 1; - - // don't use fp arithmetic if expAdj <= 3 - xToExp = expAdj == 1 ? x : - (expAdj == 2 ? x*x : - (expAdj == 3 ? x*x*x : (long) Math.pow(x, expAdj))); - powerCache[expAdj] = xToExp; - - // append exp's rightmost bit to expAdj - if (expAdj != exp) { - xToExp *= xToExp; - powerCache[expAdj << 1] = xToExp; - - if ((exp & 1) == 1) { - xToExp *= x; - powerCache[exp] = xToExp; - } - } - } - pow *= xToExp; - } + if (exp > 0) + pow *= computePower(powerCache, x, exp, maxExp); + n <<= blockLen; // shift to next block of bits } return pow; } + /** + * Returns {@code x^exp}. This method is used by {@code unsignedLongPow(long, int)}. + * Assumes {@code maxExp == max(3, Double.PRECISION / bitLength(x))}. + */ + private static long computePower(long[] powerCache, long x, int exp, int maxExp) { + long xToExp = powerCache[exp]; + if (xToExp == 0) { + if (exp <= maxExp) { + // don't use fp arithmetic if exp <= 3 + xToExp = exp == 1 ? x : + (exp == 2 ? x*x : + (exp == 3 ? x*x*x : (long) Math.pow(x, exp))); + powerCache[exp] = xToExp; + } else { + // adjust exp to fit x^expAdj into a double + final int expAdj = exp >>> 1; + + xToExp = powerCache[expAdj << 1]; + if (xToExp == 0) { // compute x^(expAdj << 1) + xToExp = powerCache[expAdj]; + if (xToExp == 0) { // compute x^expAdj + // don't use fp arithmetic if expAdj <= 3 + xToExp = expAdj == 1 ? x : + (expAdj == 2 ? x*x : + (expAdj == 3 ? x*x*x : (long) Math.pow(x, expAdj))); + powerCache[expAdj] = xToExp; + } + xToExp *= xToExp; + powerCache[expAdj << 1] = xToExp; + } + + // append exp's rightmost bit to expAdj + if ((exp & 1) == 1) { + xToExp *= x; + powerCache[exp] = xToExp; + } + } + } + return xToExp; + } + /** * Returns the integer square root of this BigInteger. The integer square * root of the corresponding mathematical integer {@code n} is the largest From b94ca7e0acbecea25deb8c29a9f6edae0d971933 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Sat, 26 Apr 2025 16:42:30 -0400 Subject: [PATCH 35/55] Optimized BigInteger.pow(int) to support unsigned long bases --- .../share/classes/java/math/BigInteger.java | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 70c9cb0ea408f..817517c73f683 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -1246,6 +1246,16 @@ else if (val < 0 && val >= -MAX_CONSTANT) return new BigInteger(val); } + /** + * Constructs a BigInteger with magnitude specified by the long, + * which may not be zero, and the signum specified by the int. + */ + private BigInteger(long mag, int signum) { + assert mag != 0 && signum != 0; + this.signum = signum; + this.mag = toMagArray(mag); + } + /** * Constructs a BigInteger with the specified value, which may not be zero. */ @@ -1256,16 +1266,14 @@ private BigInteger(long val) { } else { signum = 1; } + mag = toMagArray(val); + } - int highWord = (int)(val >>> 32); - if (highWord == 0) { - mag = new int[1]; - mag[0] = (int)val; - } else { - mag = new int[2]; - mag[0] = highWord; - mag[1] = (int)val; - } + private static int[] toMagArray(long mag) { + int highWord = (int) (mag >>> 32); + return highWord == 0 + ? new int[] { (int) mag } + : new int[] { highWord, (int) mag }; } /** @@ -2638,22 +2646,18 @@ public BigInteger pow(int exponent) { long scaleFactor = (long)remainingBits * exponent; // Use slightly different algorithms for small and large operands. - // See if the result will safely fit into a long. (Largest 2^63-1) - if (base.mag.length == 1 && scaleFactor <= 62) { - // Small number algorithm. Everything fits into a long. + // See if the result will safely fit into an unsigned long. (Largest 2^64-1) + if (base.mag.length == 1 && scaleFactor <= Long.SIZE) { + // Small number algorithm. Everything fits into an unsigned long. int newSign = (signum <0 && (exponent&1) == 1 ? -1 : 1); long result = unsignedLongPow(base.mag[0] & LONG_MASK, exponent); // Multiply back the powers of two (quickly, by shifting left) - if (powersOfTwo > 0) { - if (bitsToShift + scaleFactor <= 62) { // Fits in long? - return valueOf((result << bitsToShift) * newSign); - } else { - return valueOf(result*newSign).shiftLeft(bitsToShift); - } - } else { - return valueOf(result*newSign); - } + return powersOfTwo == 0 + ? new BigInteger(result, newSign) + : (bitsToShift + scaleFactor <= Long.SIZE // Fits in long? + ? new BigInteger(result << bitsToShift, newSign) + : new BigInteger(result, newSign).shiftLeft(bitsToShift)); } else { if ((long)bitLength() * exponent / Integer.SIZE > MAX_MAG_LENGTH) { reportOverflow(); @@ -2714,7 +2718,7 @@ private static BigInteger unsignedIntPow(int x, int n) { BigInteger pow = ONE; int blockLen; for (int nLen = Integer.SIZE - nZeros; nLen > 0; nLen -= blockLen) { - blockLen = maxExpLen < nLen ? maxExpLen : nLen; + blockLen = Math.min(maxExpLen, nLen); // compute pow^(2^blockLen) if (!pow.equals(ONE)) { for (int i = 0; i < blockLen; i++) @@ -2798,7 +2802,7 @@ static long unsignedLongPow(long x, int n) { long pow = 1L; int blockLen; for (int nLen = Integer.SIZE - nZeros; nLen > 0; nLen -= blockLen) { - blockLen = maxExpLen < nLen ? maxExpLen : nLen; + blockLen = Math.min(maxExpLen, nLen); // compute pow^(2^blockLen) if (pow != 1L) { for (int i = 0; i < blockLen; i++) From 139735d93f27f58010c2e4131bce80db251967ee Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Sat, 26 Apr 2025 17:15:56 -0400 Subject: [PATCH 36/55] Use BigInteger(long, int) constructor --- src/java.base/share/classes/java/math/BigInteger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 817517c73f683..b18d6015a41ad 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2762,7 +2762,7 @@ private static BigInteger computePower(BigInteger[] powerCache, long x, int exp, long xToExpAdj = expAdj == 1 ? x : (expAdj == 2 ? x*x : (long) Math.pow(x, expAdj)); - xToExp = new BigInteger(1, new int[] { (int) (xToExpAdj >>> 32), (int) xToExpAdj }); + xToExp = new BigInteger(xToExpAdj, 1); powerCache[expAdj] = xToExp; } xToExp = xToExp.multiply(xToExp); From fcd5d5505bc94761d79eae5cc76a1e44a452d300 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Sat, 26 Apr 2025 17:45:17 -0400 Subject: [PATCH 37/55] Throw away unsignedIntPow(int, int) --- .../share/classes/java/math/BigInteger.java | 104 ++---------------- 1 file changed, 9 insertions(+), 95 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index b18d6015a41ad..d4c9cbd8fb64d 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2666,22 +2666,17 @@ public BigInteger pow(int exponent) { // Large number algorithm. This is basically identical to // the algorithm above, but calls multiply() // which may use more efficient algorithms for large numbers. - BigInteger answer; - if (base.mag.length == 1) { - answer = unsignedIntPow(base.mag[0], exponent); - } else { - answer = ONE; + BigInteger answer = ONE; - final int expZeros = Integer.numberOfLeadingZeros(exponent); - int workingExp = exponent << expZeros; - // Perform exponentiation using repeated squaring trick - for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { - answer = answer.multiply(answer); - if (workingExp < 0) // leading bit is set - answer = answer.multiply(base); + final int expZeros = Integer.numberOfLeadingZeros(exponent); + int workingExp = exponent << expZeros; + // Perform exponentiation using repeated squaring trick + for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { + answer = answer.multiply(answer); + if (workingExp < 0) // leading bit is set + answer = answer.multiply(base); - workingExp <<= 1; - } + workingExp <<= 1; } // Multiply back the (exponentiated) powers of two (quickly, @@ -2698,87 +2693,6 @@ public BigInteger pow(int exponent) { } } - /** - * Computes {@code x^n} using repeated squaring trick. - * Assumes {@code x != 0}. - */ - private static BigInteger unsignedIntPow(int x, int n) { - if (x == 1) - return ONE; - // Double.PRECISION / bitLength(x) is the largest integer e - // such that x^e fits into a double. If e <= 2, we won't use fp arithmetic. - // This allows to use fp arithmetic in computePower(), where possible. - final int maxExp = Math.max(2, Double.PRECISION / bitLengthForInt(x)); - final int maxExpLen = bitLengthForInt(maxExp); - final BigInteger[] powerCache = new BigInteger[1 << maxExpLen]; - - final int nZeros = Integer.numberOfLeadingZeros(n); - n <<= nZeros; - - BigInteger pow = ONE; - int blockLen; - for (int nLen = Integer.SIZE - nZeros; nLen > 0; nLen -= blockLen) { - blockLen = Math.min(maxExpLen, nLen); - // compute pow^(2^blockLen) - if (!pow.equals(ONE)) { - for (int i = 0; i < blockLen; i++) - pow = pow.multiply(pow); - } - - // add exp to power's exponent - int exp = n >>> -blockLen; - if (exp > 0) - pow = pow.multiply(computePower(powerCache, x & LONG_MASK, exp, maxExp)); - - n <<= blockLen; // shift to next block of bits - } - - return pow; - } - - /** - * Returns {@code x^exp}. This method is used by {@code unsignedIntPow(int, int)}. - * Assumes {@code maxExp == max(2, Double.PRECISION / bitLength(x))}. - */ - private static BigInteger computePower(BigInteger[] powerCache, long x, int exp, int maxExp) { - BigInteger xToExp = powerCache[exp]; - if (xToExp == null) { - if (exp <= maxExp) { - // don't use fp arithmetic if exp <= 2 - long pow = exp == 1 ? x : - (exp == 2 ? x*x : (long) Math.pow(x, exp)); - - xToExp = new BigInteger(1, new int[] { (int) (pow >>> 32), (int) pow }); - powerCache[exp] = xToExp; - } else { - // adjust exp to fit x^expAdj into a double - final int expAdj = exp >>> 1; - - xToExp = powerCache[expAdj << 1]; - if (xToExp == null) { // compute x^(expAdj << 1) - xToExp = powerCache[expAdj]; - if (xToExp == null) { // compute x^expAdj - // don't use fp arithmetic if expAdj <= 2 - long xToExpAdj = expAdj == 1 ? x : - (expAdj == 2 ? x*x : (long) Math.pow(x, expAdj)); - - xToExp = new BigInteger(xToExpAdj, 1); - powerCache[expAdj] = xToExp; - } - xToExp = xToExp.multiply(xToExp); - powerCache[expAdj << 1] = xToExp; - } - - // append exp's rightmost bit to expAdj - if ((exp & 1) == 1) { - xToExp = xToExp.multiply(x); - powerCache[exp] = xToExp; - } - } - } - return xToExp; - } - /** * Computes {@code x^n} using repeated squaring trick. * Assumes {@code x != 0 && x^n < 2^Long.SIZE}. From b831d0179a97bedd79ca19faf16d01dc9ae7116b Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Mon, 28 Apr 2025 11:04:34 -0400 Subject: [PATCH 38/55] Pre-cache the powers of x up to x^3 to simplify the code --- .../share/classes/java/math/BigInteger.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index d4c9cbd8fb64d..6cc5e7259d840 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2709,6 +2709,9 @@ static long unsignedLongPow(long x, int n) { final int maxExp = Math.max(3, Double.PRECISION / bitLengthForLong(x)); final int maxExpLen = bitLengthForInt(maxExp); final long[] powerCache = new long[1 << maxExpLen]; + powerCache[1] = x; + powerCache[2] = x * x; + powerCache[3] = x * x * x; final int nZeros = Integer.numberOfLeadingZeros(n); n <<= nZeros; @@ -2742,10 +2745,7 @@ private static long computePower(long[] powerCache, long x, int exp, int maxExp) long xToExp = powerCache[exp]; if (xToExp == 0) { if (exp <= maxExp) { - // don't use fp arithmetic if exp <= 3 - xToExp = exp == 1 ? x : - (exp == 2 ? x*x : - (exp == 3 ? x*x*x : (long) Math.pow(x, exp))); + xToExp = (long) Math.pow(x, exp); // don't use fp arithmetic if exp <= 3 powerCache[exp] = xToExp; } else { // adjust exp to fit x^expAdj into a double @@ -2755,10 +2755,7 @@ private static long computePower(long[] powerCache, long x, int exp, int maxExp) if (xToExp == 0) { // compute x^(expAdj << 1) xToExp = powerCache[expAdj]; if (xToExp == 0) { // compute x^expAdj - // don't use fp arithmetic if expAdj <= 3 - xToExp = expAdj == 1 ? x : - (expAdj == 2 ? x*x : - (expAdj == 3 ? x*x*x : (long) Math.pow(x, expAdj))); + xToExp = (long) Math.pow(x, expAdj); // don't use fp arithmetic if expAdj <= 3 powerCache[expAdj] = xToExp; } xToExp *= xToExp; From 51272bc6867b21361e0fcf025be66990aaca6835 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Mon, 28 Apr 2025 12:44:36 -0400 Subject: [PATCH 39/55] Added tests for memory consumption --- .../bench/java/math/BigIntegerPow.java | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 test/micro/org/openjdk/bench/java/math/BigIntegerPow.java diff --git a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java new file mode 100644 index 0000000000000..0c1cf778e7d9d --- /dev/null +++ b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2025, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.math; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OperationsPerInvocation; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.profile.GCProfiler; + +import java.math.BigInteger; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@Fork(value = 3) +public class BigIntegerPow { + + private BigInteger[] xsArray, sArray, mArray, lArray, xlArray; + private int xsExp, sExp, mExp, lExp, xlExp; + private static final int TESTSIZE = 2; + + /* + * You can run this test via the command line: + * $ mvn clean install + * $ java -jar target/benchmarks.jar BigIntegerPow -prof gc + */ + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(BigIntegerPow.class.getSimpleName()) + .addProfiler(GCProfiler.class) + .build(); + + new Runner(opt).run(); + } + + @Setup + public void setup() { + Random r = new Random(1123); + + xsExp = (1 << 25) - 1; + xsArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 64 bits + * in size + */ + sExp = (1 << 23) - 1; + sArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 256 bits + * in size + */ + mExp = (1 << 21) - 1; + mArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 1024 bits + * in size + */ + lExp = (1 << 19) - 1; + lArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 4096 bits + * in size + */ + xlExp = (1 << 17) - 1; + xlArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 16384 bits + * in size + */ + + for (int i = 0; i < TESTSIZE; i++) { + xsArray[i] = new BigInteger(64, r); + sArray[i] = new BigInteger(256, r); + mArray[i] = new BigInteger(1024, r); + lArray[i] = new BigInteger(4096, r); + xlArray[i] = new BigInteger(16384, r); + } + } + + /** Test BigInteger.pow() with numbers long at most 64 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowXS(Blackhole bh) { + for (BigInteger s : xsArray) { + bh.consume(s.pow(xsExp)); + } + } + + /** Test BigInteger.pow() with numbers long at most 256 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowS(Blackhole bh) { + for (BigInteger s : sArray) { + bh.consume(s.pow(sExp)); + } + } + + /** Test BigInteger.pow() with numbers long at most 1024 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowM(Blackhole bh) { + for (BigInteger s : mArray) { + bh.consume(s.pow(mExp)); + } + } + + /** Test BigInteger.pow() with numbers long at most 4096 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowL(Blackhole bh) { + for (BigInteger s : lArray) { + bh.consume(s.pow(lExp)); + } + } + + /** Test BigInteger.pow() with numbers long at most 16384 bits */ + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowXL(Blackhole bh) { + for (BigInteger s : xlArray) { + bh.consume(s.pow(xlExp)); + } + } +} From 70da95cb4abb592a2eec0f0f3759f70bbb6b4eac Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 29 Apr 2025 06:51:19 -0400 Subject: [PATCH 40/55] Decrease exponents in tests --- .../org/openjdk/bench/java/math/BigIntegerPow.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java index 0c1cf778e7d9d..e7d23de8b57c9 100644 --- a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java +++ b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java @@ -75,27 +75,27 @@ public static void main(String[] args) throws RunnerException { public void setup() { Random r = new Random(1123); - xsExp = (1 << 25) - 1; + xsExp = (1 << 20) - 1; xsArray = new BigInteger[TESTSIZE]; /* * Each array entry is atmost 64 bits * in size */ - sExp = (1 << 23) - 1; + sExp = (1 << 18) - 1; sArray = new BigInteger[TESTSIZE]; /* * Each array entry is atmost 256 bits * in size */ - mExp = (1 << 21) - 1; + mExp = (1 << 16) - 1; mArray = new BigInteger[TESTSIZE]; /* * Each array entry is atmost 1024 bits * in size */ - lExp = (1 << 19) - 1; + lExp = (1 << 14) - 1; lArray = new BigInteger[TESTSIZE]; /* * Each array entry is atmost 4096 bits * in size */ - xlExp = (1 << 17) - 1; + xlExp = (1 << 12) - 1; xlArray = new BigInteger[TESTSIZE]; /* * Each array entry is atmost 16384 bits * in size From d85a634e7d4fa93209a5d278a3e9c1d1ecb45503 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 29 Apr 2025 07:04:14 -0400 Subject: [PATCH 41/55] Update test parameters --- test/micro/org/openjdk/bench/java/math/BigIntegerPow.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java index e7d23de8b57c9..ebdd9dd055505 100644 --- a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java +++ b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java @@ -48,14 +48,14 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @State(Scope.Thread) -@Warmup(iterations = 5, time = 1) -@Measurement(iterations = 5, time = 1) +@Warmup(iterations = 1, time = 1) +@Measurement(iterations = 1, time = 1) @Fork(value = 3) public class BigIntegerPow { private BigInteger[] xsArray, sArray, mArray, lArray, xlArray; private int xsExp, sExp, mExp, lExp, xlExp; - private static final int TESTSIZE = 2; + private static final int TESTSIZE = 1; /* * You can run this test via the command line: From 9a5d69656445c34db74a7855f76ed0ecc20e1aa2 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 29 Apr 2025 10:24:55 -0400 Subject: [PATCH 42/55] Removed needless condition --- src/java.base/share/classes/java/math/BigInteger.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 6cc5e7259d840..f781da1b4434f 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2647,9 +2647,9 @@ public BigInteger pow(int exponent) { // Use slightly different algorithms for small and large operands. // See if the result will safely fit into an unsigned long. (Largest 2^64-1) - if (base.mag.length == 1 && scaleFactor <= Long.SIZE) { + if (scaleFactor <= Long.SIZE) { // Small number algorithm. Everything fits into an unsigned long. - int newSign = (signum <0 && (exponent&1) == 1 ? -1 : 1); + int newSign = (signum < 0 && (exponent & 1) == 1 ? -1 : 1); long result = unsignedLongPow(base.mag[0] & LONG_MASK, exponent); // Multiply back the powers of two (quickly, by shifting left) From 6033d2568a6275380369303ec5961c71d027f84a Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 29 Apr 2025 10:33:49 -0400 Subject: [PATCH 43/55] Don't exclude a priori valid results Don't exclude a priori values that may be inside the supported range, leave the bound checks to the subroutines. --- src/java.base/share/classes/java/math/BigInteger.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index f781da1b4434f..4fd0ed22c1567 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2659,10 +2659,6 @@ public BigInteger pow(int exponent) { ? new BigInteger(result << bitsToShift, newSign) : new BigInteger(result, newSign).shiftLeft(bitsToShift)); } else { - if ((long)bitLength() * exponent / Integer.SIZE > MAX_MAG_LENGTH) { - reportOverflow(); - } - // Large number algorithm. This is basically identical to // the algorithm above, but calls multiply() // which may use more efficient algorithms for large numbers. From 5deb21aec5fddb285aa6ffcb35bc864e82bb24eb Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 29 Apr 2025 10:37:32 -0400 Subject: [PATCH 44/55] Take into account special case exponent == 1 --- src/java.base/share/classes/java/math/BigInteger.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 4fd0ed22c1567..c15ac67fd836d 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2600,8 +2600,8 @@ public BigInteger pow(int exponent) { if (exponent == 0 || this.equals(ONE)) return ONE; - if (signum == 0) - return ZERO; + if (signum == 0 || exponent == 1) + return this; BigInteger base = this.abs(); From 280859b81a152f447731524d76e5dda742966158 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 29 Apr 2025 11:05:35 -0400 Subject: [PATCH 45/55] Use a more loose formula to do range check Use a more loose formula to do range check, in order not to exclude a priori values that may be inside the supported range --- src/java.base/share/classes/java/math/BigInteger.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index c15ac67fd836d..d9d00bb3d7fe6 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2659,6 +2659,10 @@ public BigInteger pow(int exponent) { ? new BigInteger(result << bitsToShift, newSign) : new BigInteger(result, newSign).shiftLeft(bitsToShift)); } else { + if (((bitLength() - 1L) * exponent + 1) / Integer.SIZE > MAX_MAG_LENGTH) { + reportOverflow(); + } + // Large number algorithm. This is basically identical to // the algorithm above, but calls multiply() // which may use more efficient algorithms for large numbers. From 925806bd26f6d025e0abb0e0b724140c69de992a Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 29 Apr 2025 11:07:31 -0400 Subject: [PATCH 46/55] Adjust the type of operand --- src/java.base/share/classes/java/math/BigInteger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index d9d00bb3d7fe6..877424346b486 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2659,7 +2659,7 @@ public BigInteger pow(int exponent) { ? new BigInteger(result << bitsToShift, newSign) : new BigInteger(result, newSign).shiftLeft(bitsToShift)); } else { - if (((bitLength() - 1L) * exponent + 1) / Integer.SIZE > MAX_MAG_LENGTH) { + if (((bitLength() - 1L) * exponent + 1L) / Integer.SIZE > MAX_MAG_LENGTH) { reportOverflow(); } From ad49b56eefbfa1ca8b8bf3b995bc7e0747a691d7 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 29 Apr 2025 16:18:11 -0400 Subject: [PATCH 47/55] Use a more accurate formula to detect certain overflows --- src/java.base/share/classes/java/math/BigInteger.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 877424346b486..d396847b33092 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2659,7 +2659,9 @@ public BigInteger pow(int exponent) { ? new BigInteger(result << bitsToShift, newSign) : new BigInteger(result, newSign).shiftLeft(bitsToShift)); } else { - if (((bitLength() - 1L) * exponent + 1L) / Integer.SIZE > MAX_MAG_LENGTH) { + final long ansBitsLower = (bitLength() - 1L) * exponent + 1L; + final long ansIntsLower = (ansBitsLower >>> 5) + ((ansBitsLower & 31L) == 0L ? 0L : 1L); + if (ansIntsLower > MAX_MAG_LENGTH) { reportOverflow(); } From 68959267cdeee77ad9a783fc81bae223bf55802a Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Tue, 29 Apr 2025 16:30:18 -0400 Subject: [PATCH 48/55] Simplified the formula for detecting overflows --- src/java.base/share/classes/java/math/BigInteger.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index d396847b33092..64234b1a67788 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2659,9 +2659,7 @@ public BigInteger pow(int exponent) { ? new BigInteger(result << bitsToShift, newSign) : new BigInteger(result, newSign).shiftLeft(bitsToShift)); } else { - final long ansBitsLower = (bitLength() - 1L) * exponent + 1L; - final long ansIntsLower = (ansBitsLower >>> 5) + ((ansBitsLower & 31L) == 0L ? 0L : 1L); - if (ansIntsLower > MAX_MAG_LENGTH) { + if ((((bitLength() - 1L) * exponent) >>> 5) + 1L > MAX_MAG_LENGTH) { reportOverflow(); } From b8ca4fe439ef8ce00a3afe49398c15912ba0545a Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Wed, 30 Apr 2025 11:56:02 -0400 Subject: [PATCH 49/55] Simplify long power computing --- .../share/classes/java/math/BigInteger.java | 70 +++---------------- 1 file changed, 8 insertions(+), 62 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 64234b1a67788..12798bf73f081 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2703,75 +2703,21 @@ static long unsignedLongPow(long x, int n) { if (x == 2L) return 1L << n; - // Double.PRECISION / bitLength(x) is the largest integer e - // such that x^e fits into a double. If e <= 3, we won't use fp arithmetic. - // This allows to use fp arithmetic in computePower(), where possible. - final int maxExp = Math.max(3, Double.PRECISION / bitLengthForLong(x)); - final int maxExpLen = bitLengthForInt(maxExp); - final long[] powerCache = new long[1 << maxExpLen]; - powerCache[1] = x; - powerCache[2] = x * x; - powerCache[3] = x * x * x; - - final int nZeros = Integer.numberOfLeadingZeros(n); - n <<= nZeros; + /* + * The method assumption means that n <= 40 here. + * Thus, the loop body executes at most 5 times. + */ long pow = 1L; - int blockLen; - for (int nLen = Integer.SIZE - nZeros; nLen > 0; nLen -= blockLen) { - blockLen = Math.min(maxExpLen, nLen); - // compute pow^(2^blockLen) - if (pow != 1L) { - for (int i = 0; i < blockLen; i++) - pow *= pow; - } - - // add exp to power's exponent - int exp = n >>> -blockLen; - if (exp > 0) - pow *= computePower(powerCache, x, exp, maxExp); + for (; n != 0; n >>>= 1) { + if ((n & 1) != 0) + pow *= x; - n <<= blockLen; // shift to next block of bits + x *= x; } - return pow; } - /** - * Returns {@code x^exp}. This method is used by {@code unsignedLongPow(long, int)}. - * Assumes {@code maxExp == max(3, Double.PRECISION / bitLength(x))}. - */ - private static long computePower(long[] powerCache, long x, int exp, int maxExp) { - long xToExp = powerCache[exp]; - if (xToExp == 0) { - if (exp <= maxExp) { - xToExp = (long) Math.pow(x, exp); // don't use fp arithmetic if exp <= 3 - powerCache[exp] = xToExp; - } else { - // adjust exp to fit x^expAdj into a double - final int expAdj = exp >>> 1; - - xToExp = powerCache[expAdj << 1]; - if (xToExp == 0) { // compute x^(expAdj << 1) - xToExp = powerCache[expAdj]; - if (xToExp == 0) { // compute x^expAdj - xToExp = (long) Math.pow(x, expAdj); // don't use fp arithmetic if expAdj <= 3 - powerCache[expAdj] = xToExp; - } - xToExp *= xToExp; - powerCache[expAdj << 1] = xToExp; - } - - // append exp's rightmost bit to expAdj - if ((exp & 1) == 1) { - xToExp *= x; - powerCache[exp] = xToExp; - } - } - } - return xToExp; - } - /** * Returns the integer square root of this BigInteger. The integer square * root of the corresponding mathematical integer {@code n} is the largest From 4103e4917d3dc74dc6e61552d69d5d95d5acc3e4 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Wed, 7 May 2025 10:50:38 -0400 Subject: [PATCH 50/55] Suggested changes --- .../share/classes/java/math/BigInteger.java | 60 +++++---------- .../bench/java/math/BigIntegerPow.java | 75 ++++++++++++++----- 2 files changed, 74 insertions(+), 61 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 12798bf73f081..68baf318c2810 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2604,6 +2604,7 @@ public BigInteger pow(int exponent) { return this; BigInteger base = this.abs(); + final boolean negative = signum < 0 && (exponent & 1) == 1; // Factor out powers of two from the base, as the exponentiation of // these can be done by left shifts only. @@ -2616,29 +2617,11 @@ public BigInteger pow(int exponent) { } int bitsToShift = (int)bitsToShiftLong; - int remainingBits; - - // Factor the powers of two out quickly by shifting right, if needed. - if (powersOfTwo > 0) { - base = base.shiftRight(powersOfTwo); - remainingBits = base.bitLength(); - if (remainingBits == 1) { // Nothing left but +/- 1? - if (signum < 0 && (exponent&1) == 1) { - return NEGATIVE_ONE.shiftLeft(bitsToShift); - } else { - return ONE.shiftLeft(bitsToShift); - } - } - } else { - remainingBits = base.bitLength(); - if (remainingBits == 1) { // Nothing left but +/- 1? - if (signum < 0 && (exponent&1) == 1) { - return NEGATIVE_ONE; - } else { - return ONE; - } - } - } + // Factor the powers of two out quickly by shifting right. + base = base.shiftRight(powersOfTwo); + int remainingBits = base.bitLength(); + if (remainingBits == 1) // Nothing left but +/- 1? + return (negative ? NEGATIVE_ONE : ONE).shiftLeft(bitsToShift); // This is a quick way to approximate the size of the result, // similar to doing log2[n] * exponent. This will give an upper bound @@ -2649,17 +2632,15 @@ public BigInteger pow(int exponent) { // See if the result will safely fit into an unsigned long. (Largest 2^64-1) if (scaleFactor <= Long.SIZE) { // Small number algorithm. Everything fits into an unsigned long. - int newSign = (signum < 0 && (exponent & 1) == 1 ? -1 : 1); - long result = unsignedLongPow(base.mag[0] & LONG_MASK, exponent); + final int newSign = negative ? -1 : 1; + final long result = unsignedLongPow(base.mag[0] & LONG_MASK, exponent); // Multiply back the powers of two (quickly, by shifting left) - return powersOfTwo == 0 - ? new BigInteger(result, newSign) - : (bitsToShift + scaleFactor <= Long.SIZE // Fits in long? - ? new BigInteger(result << bitsToShift, newSign) - : new BigInteger(result, newSign).shiftLeft(bitsToShift)); + return bitsToShift + scaleFactor <= Long.SIZE // Fits in long? + ? new BigInteger(result << bitsToShift, newSign) + : new BigInteger(result, newSign).shiftLeft(bitsToShift); } else { - if ((((bitLength() - 1L) * exponent) >>> 5) + 1L > MAX_MAG_LENGTH) { + if (((bitLength() - 1L) * exponent) >= (long) MAX_MAG_LENGTH << 5) { reportOverflow(); } @@ -2681,15 +2662,8 @@ public BigInteger pow(int exponent) { // Multiply back the (exponentiated) powers of two (quickly, // by shifting left) - if (powersOfTwo > 0) { - answer = answer.shiftLeft(bitsToShift); - } - - if (signum < 0 && (exponent&1) == 1) { - return answer.negate(); - } else { - return answer; - } + answer = answer.shiftLeft(bitsToShift); + return negative ? answer.negate() : answer; } } @@ -2698,7 +2672,7 @@ public BigInteger pow(int exponent) { * Assumes {@code x != 0 && x^n < 2^Long.SIZE}. */ static long unsignedLongPow(long x, int n) { - if (x == 1L) + if (x == 1L || n == 0) return 1L; if (x == 2L) @@ -2709,13 +2683,13 @@ static long unsignedLongPow(long x, int n) { * Thus, the loop body executes at most 5 times. */ long pow = 1L; - for (; n != 0; n >>>= 1) { + for (; n != 1; n >>>= 1) { if ((n & 1) != 0) pow *= x; x *= x; } - return pow; + return pow * x; } /** diff --git a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java index ebdd9dd055505..3124c28541255 100644 --- a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java +++ b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java @@ -55,21 +55,13 @@ public class BigIntegerPow { private BigInteger[] xsArray, sArray, mArray, lArray, xlArray; private int xsExp, sExp, mExp, lExp, xlExp; + private int[] randomExps; private static final int TESTSIZE = 1; /* * You can run this test via the command line: - * $ mvn clean install - * $ java -jar target/benchmarks.jar BigIntegerPow -prof gc + * $ make test TEST="micro:java.math.BigIntegerPow" MICRO="OPTIONS=-prof gc" */ - public static void main(String[] args) throws RunnerException { - Options opt = new OptionsBuilder() - .include(BigIntegerPow.class.getSimpleName()) - .addProfiler(GCProfiler.class) - .build(); - - new Runner(opt).run(); - } @Setup public void setup() { @@ -101,12 +93,14 @@ public void setup() { * in size */ + randomExps = new int[TESTSIZE]; for (int i = 0; i < TESTSIZE; i++) { xsArray[i] = new BigInteger(64, r); sArray[i] = new BigInteger(256, r); mArray[i] = new BigInteger(1024, r); lArray[i] = new BigInteger(4096, r); xlArray[i] = new BigInteger(16384, r); + randomExps[i] = r.nextInt(1 << 12); } } @@ -114,8 +108,17 @@ public void setup() { @Benchmark @OperationsPerInvocation(TESTSIZE) public void testPowXS(Blackhole bh) { - for (BigInteger s : xsArray) { - bh.consume(s.pow(xsExp)); + for (BigInteger xs : xsArray) { + bh.consume(xs.pow(xsExp)); + } + } + + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowXSRandomExps(Blackhole bh) { + int i = 0; + for (BigInteger xs : xsArray) { + bh.consume(xs.pow(randomExps[i++])); } } @@ -128,12 +131,30 @@ public void testPowS(Blackhole bh) { } } + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowSRandomExps(Blackhole bh) { + int i = 0; + for (BigInteger s : sArray) { + bh.consume(s.pow(randomExps[i++])); + } + } + /** Test BigInteger.pow() with numbers long at most 1024 bits */ @Benchmark @OperationsPerInvocation(TESTSIZE) public void testPowM(Blackhole bh) { - for (BigInteger s : mArray) { - bh.consume(s.pow(mExp)); + for (BigInteger m : mArray) { + bh.consume(m.pow(mExp)); + } + } + + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowMRandomExps(Blackhole bh) { + int i = 0; + for (BigInteger m : mArray) { + bh.consume(m.pow(randomExps[i++])); } } @@ -141,8 +162,17 @@ public void testPowM(Blackhole bh) { @Benchmark @OperationsPerInvocation(TESTSIZE) public void testPowL(Blackhole bh) { - for (BigInteger s : lArray) { - bh.consume(s.pow(lExp)); + for (BigInteger l : lArray) { + bh.consume(l.pow(lExp)); + } + } + + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowLRandomExps(Blackhole bh) { + int i = 0; + for (BigInteger l : lArray) { + bh.consume(l.pow(randomExps[i++])); } } @@ -150,8 +180,17 @@ public void testPowL(Blackhole bh) { @Benchmark @OperationsPerInvocation(TESTSIZE) public void testPowXL(Blackhole bh) { - for (BigInteger s : xlArray) { - bh.consume(s.pow(xlExp)); + for (BigInteger xl : xlArray) { + bh.consume(xl.pow(xlExp)); + } + } + + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testPowXLRandomExps(Blackhole bh) { + int i = 0; + for (BigInteger xl : xlArray) { + bh.consume(xl.pow(randomExps[i++])); } } } From 5ebc16b79dae8ecdb3aec619486d83872f08e9b0 Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Wed, 7 May 2025 17:00:26 +0200 Subject: [PATCH 51/55] Removed needless brackets --- src/java.base/share/classes/java/math/BigInteger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 68baf318c2810..1c2bcce04738a 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2640,7 +2640,7 @@ public BigInteger pow(int exponent) { ? new BigInteger(result << bitsToShift, newSign) : new BigInteger(result, newSign).shiftLeft(bitsToShift); } else { - if (((bitLength() - 1L) * exponent) >= (long) MAX_MAG_LENGTH << 5) { + if ((bitLength() - 1L) * exponent >= (long) MAX_MAG_LENGTH << 5) { reportOverflow(); } From e0816d5520b011e97a6cd5abf2601a01a5bf050f Mon Sep 17 00:00:00 2001 From: fabioromano1 <51378941+fabioromano1@users.noreply.github.com> Date: Wed, 7 May 2025 17:45:05 +0200 Subject: [PATCH 52/55] Code simplification --- src/java.base/share/classes/java/math/BigInteger.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 1c2bcce04738a..53a5e3729f18c 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2611,11 +2611,11 @@ public BigInteger pow(int exponent) { // The remaining part can then be exponentiated faster. The // powers of two will be multiplied back at the end. int powersOfTwo = base.getLowestSetBit(); - long bitsToShiftLong = (long)powersOfTwo * exponent; - if (bitsToShiftLong > Integer.MAX_VALUE) { + long bitsToShiftLong = (long) powersOfTwo * exponent; + int bitsToShift = (int) bitsToShiftLong; + if (bitsToShift != bitsToShiftLong) { reportOverflow(); } - int bitsToShift = (int)bitsToShiftLong; // Factor the powers of two out quickly by shifting right. base = base.shiftRight(powersOfTwo); From 009937b9e0bcefe59e86bba6683aad1c59798241 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Wed, 7 May 2025 14:16:00 -0400 Subject: [PATCH 53/55] Suggested changes --- .../share/classes/java/math/BigInteger.java | 14 +++-- .../bench/java/math/BigIntegerPow.java | 56 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 53a5e3729f18c..06a391f9e7409 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2610,23 +2610,23 @@ public BigInteger pow(int exponent) { // these can be done by left shifts only. // The remaining part can then be exponentiated faster. The // powers of two will be multiplied back at the end. - int powersOfTwo = base.getLowestSetBit(); - long bitsToShiftLong = (long) powersOfTwo * exponent; - int bitsToShift = (int) bitsToShiftLong; + final int powersOfTwo = base.getLowestSetBit(); + final long bitsToShiftLong = (long) powersOfTwo * exponent; + final int bitsToShift = (int) bitsToShiftLong; if (bitsToShift != bitsToShiftLong) { reportOverflow(); } // Factor the powers of two out quickly by shifting right. base = base.shiftRight(powersOfTwo); - int remainingBits = base.bitLength(); + final int remainingBits = base.bitLength(); if (remainingBits == 1) // Nothing left but +/- 1? return (negative ? NEGATIVE_ONE : ONE).shiftLeft(bitsToShift); // This is a quick way to approximate the size of the result, // similar to doing log2[n] * exponent. This will give an upper bound // of how big the result can be, and which algorithm to use. - long scaleFactor = (long)remainingBits * exponent; + final long scaleFactor = (long)remainingBits * exponent; // Use slightly different algorithms for small and large operands. // See if the result will safely fit into an unsigned long. (Largest 2^64-1) @@ -2640,7 +2640,7 @@ public BigInteger pow(int exponent) { ? new BigInteger(result << bitsToShift, newSign) : new BigInteger(result, newSign).shiftLeft(bitsToShift); } else { - if ((bitLength() - 1L) * exponent >= (long) MAX_MAG_LENGTH << 5) { + if ((bitLength() - 1L) * exponent >= Integer.MAX_VALUE) { reportOverflow(); } @@ -2652,6 +2652,8 @@ public BigInteger pow(int exponent) { final int expZeros = Integer.numberOfLeadingZeros(exponent); int workingExp = exponent << expZeros; // Perform exponentiation using repeated squaring trick + // The loop relies on this invariant: + // base^exponent == answer^(2^expLen) * base^(exponent & (2^expLen - 1)) for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { answer = answer.multiply(answer); if (workingExp < 0) // leading bit is set diff --git a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java index 3124c28541255..4131707c5cd17 100644 --- a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java +++ b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java @@ -53,11 +53,35 @@ @Fork(value = 3) public class BigIntegerPow { - private BigInteger[] xsArray, sArray, mArray, lArray, xlArray; - private int xsExp, sExp, mExp, lExp, xlExp; - private int[] randomExps; private static final int TESTSIZE = 1; + private int xsExp = (1 << 20) - 1; + private BigInteger[] xsArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 64 bits + * in size + */ + private int sExp = (1 << 18) - 1; + private BigInteger[] sArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 256 bits + * in size + */ + private int mExp = (1 << 16) - 1; + private BigInteger[] mArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 1024 bits + * in size + */ + private int lExp = (1 << 14) - 1; + private BigInteger[] lArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 4096 bits + * in size + */ + private int xlExp = (1 << 12) - 1; + private BigInteger[] xlArray = new BigInteger[TESTSIZE]; /* + * Each array entry is atmost 16384 bits + * in size + */ + private int[] randomExps; + /* * You can run this test via the command line: * $ make test TEST="micro:java.math.BigIntegerPow" MICRO="OPTIONS=-prof gc" @@ -67,32 +91,6 @@ public class BigIntegerPow { public void setup() { Random r = new Random(1123); - xsExp = (1 << 20) - 1; - xsArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 64 bits - * in size - */ - sExp = (1 << 18) - 1; - sArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 256 bits - * in size - */ - mExp = (1 << 16) - 1; - mArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 1024 bits - * in size - */ - lExp = (1 << 14) - 1; - lArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 4096 bits - * in size - */ - xlExp = (1 << 12) - 1; - xlArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 16384 bits - * in size - */ - randomExps = new int[TESTSIZE]; for (int i = 0; i < TESTSIZE; i++) { xsArray[i] = new BigInteger(64, r); From 2e08f7754b4a0a5240ace1449213d2c2ea60eae5 Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Thu, 8 May 2025 11:30:24 -0400 Subject: [PATCH 54/55] Suggested changes --- .../share/classes/java/math/BigInteger.java | 2 +- .../bench/java/math/BigIntegerPow.java | 35 ++++++++----------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 06a391f9e7409..826abb0eec759 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -2653,7 +2653,7 @@ public BigInteger pow(int exponent) { int workingExp = exponent << expZeros; // Perform exponentiation using repeated squaring trick // The loop relies on this invariant: - // base^exponent == answer^(2^expLen) * base^(exponent & (2^expLen - 1)) + // base^exponent == answer^(2^expLen) * base^(workingExp >>> (32-expLen)) for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { answer = answer.multiply(answer); if (workingExp < 0) // leading bit is set diff --git a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java index 4131707c5cd17..007d9bb975a04 100644 --- a/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java +++ b/test/micro/org/openjdk/bench/java/math/BigIntegerPow.java @@ -56,30 +56,25 @@ public class BigIntegerPow { private static final int TESTSIZE = 1; private int xsExp = (1 << 20) - 1; - private BigInteger[] xsArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 64 bits - * in size - */ + /* Each array entry is atmost 64 bits in size */ + private BigInteger[] xsArray = new BigInteger[TESTSIZE]; + private int sExp = (1 << 18) - 1; - private BigInteger[] sArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 256 bits - * in size - */ + /* Each array entry is atmost 256 bits in size */ + private BigInteger[] sArray = new BigInteger[TESTSIZE]; + private int mExp = (1 << 16) - 1; - private BigInteger[] mArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 1024 bits - * in size - */ + /* Each array entry is atmost 1024 bits in size */ + private BigInteger[] mArray = new BigInteger[TESTSIZE]; + private int lExp = (1 << 14) - 1; - private BigInteger[] lArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 4096 bits - * in size - */ + /* Each array entry is atmost 4096 bits in size */ + private BigInteger[] lArray = new BigInteger[TESTSIZE]; + private int xlExp = (1 << 12) - 1; - private BigInteger[] xlArray = new BigInteger[TESTSIZE]; /* - * Each array entry is atmost 16384 bits - * in size - */ + /* Each array entry is atmost 16384 bits in size */ + private BigInteger[] xlArray = new BigInteger[TESTSIZE]; + private int[] randomExps; /* From 261dd315537f6135a4f065b3dcbcfe045d4a27db Mon Sep 17 00:00:00 2001 From: Fabio Romano Date: Thu, 8 May 2025 12:44:47 -0400 Subject: [PATCH 55/55] Code simplification --- .../share/classes/java/math/BigInteger.java | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java index 826abb0eec759..b0a7f784aab09 100644 --- a/src/java.base/share/classes/java/math/BigInteger.java +++ b/src/java.base/share/classes/java/math/BigInteger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2025, 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 @@ -2626,7 +2626,7 @@ public BigInteger pow(int exponent) { // This is a quick way to approximate the size of the result, // similar to doing log2[n] * exponent. This will give an upper bound // of how big the result can be, and which algorithm to use. - final long scaleFactor = (long)remainingBits * exponent; + final long scaleFactor = (long) remainingBits * exponent; // Use slightly different algorithms for small and large operands. // See if the result will safely fit into an unsigned long. (Largest 2^64-1) @@ -2639,34 +2639,34 @@ public BigInteger pow(int exponent) { return bitsToShift + scaleFactor <= Long.SIZE // Fits in long? ? new BigInteger(result << bitsToShift, newSign) : new BigInteger(result, newSign).shiftLeft(bitsToShift); - } else { - if ((bitLength() - 1L) * exponent >= Integer.MAX_VALUE) { - reportOverflow(); - } + } - // Large number algorithm. This is basically identical to - // the algorithm above, but calls multiply() - // which may use more efficient algorithms for large numbers. - BigInteger answer = ONE; - - final int expZeros = Integer.numberOfLeadingZeros(exponent); - int workingExp = exponent << expZeros; - // Perform exponentiation using repeated squaring trick - // The loop relies on this invariant: - // base^exponent == answer^(2^expLen) * base^(workingExp >>> (32-expLen)) - for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { - answer = answer.multiply(answer); - if (workingExp < 0) // leading bit is set - answer = answer.multiply(base); - - workingExp <<= 1; - } + if ((bitLength() - 1L) * exponent >= Integer.MAX_VALUE) { + reportOverflow(); + } + + // Large number algorithm. This is basically identical to + // the algorithm above, but calls multiply() + // which may use more efficient algorithms for large numbers. + BigInteger answer = ONE; - // Multiply back the (exponentiated) powers of two (quickly, - // by shifting left) - answer = answer.shiftLeft(bitsToShift); - return negative ? answer.negate() : answer; + final int expZeros = Integer.numberOfLeadingZeros(exponent); + int workingExp = exponent << expZeros; + // Perform exponentiation using repeated squaring trick + // The loop relies on this invariant: + // base^exponent == answer^(2^expLen) * base^(workingExp >>> (32-expLen)) + for (int expLen = Integer.SIZE - expZeros; expLen > 0; expLen--) { + answer = answer.multiply(answer); + if (workingExp < 0) // leading bit is set + answer = answer.multiply(base); + + workingExp <<= 1; } + + // Multiply back the (exponentiated) powers of two (quickly, + // by shifting left) + answer = answer.shiftLeft(bitsToShift); + return negative ? answer.negate() : answer; } /**