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;
}
/**