Skip to content

Commit ab12fbf

Browse files
fabioromano1rgiulietti
authored andcommitted
8077587: BigInteger Roots
Reviewed-by: rgiulietti
1 parent 6765a9d commit ab12fbf

File tree

3 files changed

+433
-3
lines changed

3 files changed

+433
-3
lines changed

src/java.base/share/classes/java/math/BigInteger.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2742,6 +2742,85 @@ public BigInteger[] sqrtAndRemainder() {
27422742
return new BigInteger[] { sqrtRem[0].toBigInteger(), sqrtRem[1].toBigInteger() };
27432743
}
27442744

2745+
/**
2746+
* Returns the integer {@code n}<sup>th</sup> root of this BigInteger. The integer
2747+
* {@code n}<sup>th</sup> root {@code r} of the corresponding mathematical integer {@code x}
2748+
* is defined as follows:
2749+
* <ul>
2750+
* <li>if {@code x} &ge; 0, then {@code r} &ge; 0 is the largest integer such that
2751+
* {@code r}<sup>{@code n}</sup> &le; {@code x};
2752+
* <li>if {@code x} &lt; 0, then {@code r} &le; 0 is the smallest integer such that
2753+
* {@code r}<sup>{@code n}</sup> &ge; {@code x}.
2754+
* </ul>
2755+
* If the root is defined, it is equal to the value of
2756+
* {@code x.signum()}&sdot; &lfloor;{@code |nthRoot(x, n)|}&rfloor;,
2757+
* where {@code nthRoot(x, n)} denotes the real {@code n}<sup>th</sup> root of {@code x}
2758+
* treated as a real.
2759+
* Otherwise, the method throws an {@code ArithmeticException}.
2760+
*
2761+
* <p>Note that the magnitude of the integer {@code n}<sup>th</sup> root will be less than
2762+
* the magnitude of the real {@code n}<sup>th</sup> root if the latter is not representable
2763+
* as an integral value.
2764+
*
2765+
* @param n the root degree
2766+
* @return the integer {@code n}<sup>th</sup> root of {@code this}
2767+
* @throws ArithmeticException if {@code n <= 0}.
2768+
* @throws ArithmeticException if {@code n} is even and {@code this} is negative.
2769+
* @see #sqrt()
2770+
* @since 26
2771+
* @apiNote Note that calling {@code nthRoot(2)} is equivalent to calling {@code sqrt()}.
2772+
*/
2773+
public BigInteger nthRoot(int n) {
2774+
if (n == 1)
2775+
return this;
2776+
2777+
if (n == 2)
2778+
return sqrt();
2779+
2780+
checkRootDegree(n);
2781+
return new MutableBigInteger(this.mag).nthRootRem(n)[0].toBigInteger(signum);
2782+
}
2783+
2784+
/**
2785+
* Returns an array of two BigIntegers containing the integer {@code n}<sup>th</sup> root
2786+
* {@code r} of {@code this} and its remainder {@code this - r}<sup>{@code n}</sup>,
2787+
* respectively.
2788+
*
2789+
* @param n the root degree
2790+
* @return an array of two BigIntegers with the integer {@code n}<sup>th</sup> root at
2791+
* offset 0 and the remainder at offset 1
2792+
* @throws ArithmeticException if {@code n <= 0}.
2793+
* @throws ArithmeticException if {@code n} is even and {@code this} is negative.
2794+
* @see #sqrt()
2795+
* @see #sqrtAndRemainder()
2796+
* @see #nthRoot(int)
2797+
* @since 26
2798+
* @apiNote Note that calling {@code nthRootAndRemainder(2)} is equivalent to calling
2799+
* {@code sqrtAndRemainder()}.
2800+
*/
2801+
public BigInteger[] nthRootAndRemainder(int n) {
2802+
if (n == 1)
2803+
return new BigInteger[] { this, ZERO };
2804+
2805+
if (n == 2)
2806+
return sqrtAndRemainder();
2807+
2808+
checkRootDegree(n);
2809+
MutableBigInteger[] rootRem = new MutableBigInteger(this.mag).nthRootRem(n);
2810+
return new BigInteger[] {
2811+
rootRem[0].toBigInteger(signum),
2812+
rootRem[1].toBigInteger(signum)
2813+
};
2814+
}
2815+
2816+
private void checkRootDegree(int n) {
2817+
if (n <= 0)
2818+
throw new ArithmeticException("Non-positive root degree");
2819+
2820+
if ((n & 1) == 0 && this.signum < 0)
2821+
throw new ArithmeticException("Negative radicand with even root degree");
2822+
}
2823+
27452824
/**
27462825
* Returns a BigInteger whose value is the greatest common divisor of
27472826
* {@code abs(this)} and {@code abs(val)}. Returns 0 if

src/java.base/share/classes/java/math/MutableBigInteger.java

Lines changed: 199 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -1892,6 +1892,204 @@ private boolean unsignedLongCompare(long one, long two) {
18921892
return (one+Long.MIN_VALUE) > (two+Long.MIN_VALUE);
18931893
}
18941894

1895+
/**
1896+
* Calculate the integer {@code n}th root {@code floor(nthRoot(this, n))} and the remainder,
1897+
* where {@code nthRoot(., n)} denotes the mathematical {@code n}th root.
1898+
* The contents of {@code this} are <em>not</em> changed. The value of {@code this}
1899+
* is assumed to be non-negative and the root degree {@code n >= 3}.
1900+
* Assumes {@code bitLength() <= Integer.MAX_VALUE}.
1901+
*
1902+
* @implNote The implementation is based on the material in Richard P. Brent
1903+
* and Paul Zimmermann, <a href="https://maths-people.anu.edu.au/~brent/pd/mca-cup-0.5.9.pdf">
1904+
* Modern Computer Arithmetic</a>, p. 27-28.
1905+
*
1906+
* @param n the root degree
1907+
* @return the integer {@code n}th root of {@code this} and the remainder
1908+
*/
1909+
MutableBigInteger[] nthRootRem(int n) {
1910+
// Special cases.
1911+
if (this.isZero() || this.isOne())
1912+
return new MutableBigInteger[] { this, new MutableBigInteger() };
1913+
1914+
final int bitLength = (int) this.bitLength();
1915+
// if this < 2^n, result is unity
1916+
if (bitLength <= n) {
1917+
MutableBigInteger rem = new MutableBigInteger(this);
1918+
rem.subtract(ONE);
1919+
return new MutableBigInteger[] { new MutableBigInteger(1), rem };
1920+
}
1921+
1922+
MutableBigInteger s;
1923+
if (bitLength <= Long.SIZE) {
1924+
// Initial estimate is the root of the unsigned long value.
1925+
final long x = this.toLong();
1926+
long sLong = (long) nthRootApprox(Math.nextUp(x >= 0 ? x : x + 0x1p64), n) + 1L;
1927+
/* The integer-valued recurrence formula in the algorithm of Brent&Zimmermann
1928+
* simply discards the fraction part of the real-valued Newton recurrence
1929+
* on the function f discussed in the referenced work.
1930+
* Indeed, for real x and integer n > 0, the equality ⌊x/n⌋ == ⌊⌊x⌋/n⌋ holds,
1931+
* from which the claim follows.
1932+
* As a consequence, an initial underestimate (not discussed in BZ)
1933+
* will immediately lead to a (weak) overestimate during the 1st iteration,
1934+
* thus meeting BZ requirements for termination and correctness.
1935+
*/
1936+
if (BigInteger.bitLengthForLong(sLong) * (n - 1) <= Long.SIZE) {
1937+
// Do the 1st iteration outside the loop to ensure an overestimate
1938+
long sToN1 = BigInteger.unsignedLongPow(sLong, n - 1);
1939+
sLong = ((n - 1) * sLong + Long.divideUnsigned(x, sToN1)) / n;
1940+
1941+
if (BigInteger.bitLengthForLong(sLong) * (n - 1) <= Long.SIZE) {
1942+
// Refine the estimate.
1943+
long u = sLong;
1944+
do {
1945+
sLong = u;
1946+
sToN1 = BigInteger.unsignedLongPow(sLong, n - 1);
1947+
u = ((n - 1) * sLong + Long.divideUnsigned(x, sToN1)) / n;
1948+
} while (u < sLong); // Terminate when non-decreasing.
1949+
1950+
return new MutableBigInteger[] {
1951+
new MutableBigInteger(sLong), new MutableBigInteger(x - sToN1 * sLong)
1952+
};
1953+
}
1954+
}
1955+
// s^(n - 1) could overflow long range, use MutableBigInteger loop instead
1956+
s = new MutableBigInteger(sLong);
1957+
} else {
1958+
final int rootLen = (bitLength - 1) / n + 1; // ⌈bitLength / n⌉
1959+
int rootSh;
1960+
double rad = 0.0, approx = 0.0;
1961+
if (n < Double.PRECISION) {
1962+
// Set up the initial estimate of the iteration.
1963+
/* Since the following equality holds:
1964+
* nthRoot(x, n) == nthRoot(x/2^sh, n) * 2^(sh/n),
1965+
*
1966+
* to get an upper bound of the root of x, it suffices to find an integer sh
1967+
* and a real s such that s >= nthRoot(x/2^sh, n) and sh % n == 0.
1968+
* The upper bound will be s * 2^(sh/n), indeed:
1969+
* s * 2^(sh/n) >= nthRoot(x/2^sh, n) * 2^(sh/n) == nthRoot(x, n).
1970+
* To achieve this, we right shift the input of sh bits into finite double range,
1971+
* rounding up the result.
1972+
*
1973+
* The value of the shift sh is chosen in order to have the smallest number of
1974+
* trailing zeros in the double value of s after the significand (minimizing
1975+
* non-significant bits), to avoid losing bits in the significand.
1976+
*/
1977+
// Determine a right shift that is a multiple of n into finite double range.
1978+
rootSh = (bitLength - Double.PRECISION) / n; // rootSh < rootLen
1979+
/* Let x = this, P = Double.PRECISION, ME = Double.MAX_EXPONENT,
1980+
* bl = bitLength, sh = rootSh * n, ex = (bl - P) % n
1981+
*
1982+
* We have bl-sh = bl-((bl-P)-ex) = P + ex
1983+
* Since ex < n < P, we get P + ex ≤ ME, and so bl-sh ≤ ME.
1984+
*
1985+
* Recalling x < 2^bl:
1986+
* x >> sh < 2^(bl-sh) ≤ 2^ME < Double.MAX_VALUE
1987+
* Thus, rad ≤ 2^ME is in the range of finite doubles.
1988+
*
1989+
* Noting that ex ≥ 0, we get bl-sh = P + ex ≥ P
1990+
* which shows that x >> sh has at least P bits of precision,
1991+
* since bl-sh is its bit length.
1992+
*/
1993+
// Shift the value into finite double range
1994+
rad = this.toBigInteger().shiftRight(rootSh * n).doubleValue();
1995+
1996+
// Use the root of the shifted value as an estimate.
1997+
// rad ≤ 2^ME, so Math.nextUp(rad) < Double.MAX_VALUE
1998+
rad = Math.nextUp(rad);
1999+
approx = nthRootApprox(rad, n);
2000+
} else { // fp arithmetic gives too few correct bits
2001+
// Set the root shift to the root's bit length minus 1
2002+
// The initial estimate will be 2^rootLen == 2 << (rootLen - 1)
2003+
rootSh = rootLen - 1;
2004+
}
2005+
2006+
if (rootSh == 0) {
2007+
// approx has at most ⌈Double.PRECISION / n⌉ + 1 ≤ 19 integer bits
2008+
s = new MutableBigInteger((int) approx + 1);
2009+
} else {
2010+
// Allocate ⌈intLen / n⌉ ints to store the final root
2011+
s = new MutableBigInteger(new int[(intLen - 1) / n + 1]);
2012+
2013+
if (n >= Double.PRECISION) { // fp arithmetic gives too few correct bits
2014+
// Set the initial estimate to 2 << (rootLen - 1)
2015+
s.value[0] = 2;
2016+
s.intLen = 1;
2017+
} else {
2018+
// Discard wrong integer bits from the initial estimate
2019+
// The reduced radicand rad has Math.getExponent(rad)+1 integer bits, but only
2020+
// the first Double.PRECISION leftmost bits are correct
2021+
// We scale the corresponding wrong bits of approx in the fraction part.
2022+
int wrongBits = ((Math.getExponent(rad) + 1) - Double.PRECISION) / n;
2023+
// Since rad <= 2^(bitLength - sh), then
2024+
// wrongBits <= ((bitLength - sh + 1) - Double.PRECISION) / n,
2025+
// so wrongBits is less than ⌈(bitLength - sh) / n⌉,
2026+
// the bit length of the exact shifted root,
2027+
// hence wrongBits + rootSh < ⌈(bitLength - sh) / n⌉ + rootSh == rootLen
2028+
rootSh += wrongBits;
2029+
approx = Math.scalb(approx, -wrongBits);
2030+
2031+
// now approx has at most ⌈Double.PRECISION / n⌉ + 1 ≤ 19 integer bits
2032+
s.value[0] = (int) approx + 1;
2033+
s.intLen = 1;
2034+
}
2035+
2036+
/* The Newton's recurrence roughly doubles the correct bits at each iteration.
2037+
* Instead of shifting the approximate root into the original range right now,
2038+
* we only double its bit length and then refine it with Newton's recurrence,
2039+
* using a suitable shifted radicand, in order to avoid computing and
2040+
* carrying trash bits in the approximate root.
2041+
* The shifted radicand is determined by the same reasoning used to get the
2042+
* initial estimate.
2043+
*/
2044+
// Refine the estimate to avoid computing non-significant bits
2045+
// rootSh is always less than rootLen, so correctBits >= 1
2046+
for (int correctBits = rootLen - rootSh; correctBits < rootSh; correctBits <<= 1) {
2047+
s.leftShift(correctBits);
2048+
rootSh -= correctBits;
2049+
// Remove useless bits from the radicand
2050+
MutableBigInteger x = new MutableBigInteger(this);
2051+
x.rightShift(rootSh * n);
2052+
2053+
newtonRecurrenceNthRoot(x, s, n, s.toBigInteger().pow(n - 1));
2054+
s.add(ONE); // round up to ensure s is an upper bound of the root
2055+
}
2056+
2057+
// Shift the approximate root back into the original range.
2058+
s.leftShift(rootSh); // Here rootSh > 0 always
2059+
}
2060+
}
2061+
2062+
// Do the 1st iteration outside the loop to ensure an overestimate
2063+
newtonRecurrenceNthRoot(this, s, n, s.toBigInteger().pow(n - 1));
2064+
// Refine the estimate.
2065+
do {
2066+
BigInteger sBig = s.toBigInteger();
2067+
BigInteger sToN1 = sBig.pow(n - 1);
2068+
MutableBigInteger rem = new MutableBigInteger(sToN1.multiply(sBig).mag);
2069+
if (rem.subtract(this) <= 0)
2070+
return new MutableBigInteger[] { s, rem };
2071+
2072+
newtonRecurrenceNthRoot(this, s, n, sToN1);
2073+
} while (true);
2074+
}
2075+
2076+
private static double nthRootApprox(double x, int n) {
2077+
return Math.nextUp(n == 3 ? Math.cbrt(x) : Math.pow(x, Math.nextUp(1.0 / n)));
2078+
}
2079+
2080+
/**
2081+
* Computes {@code ((n-1)*s + x/sToN1)/n} and places the result in {@code s}.
2082+
*/
2083+
private static void newtonRecurrenceNthRoot(
2084+
MutableBigInteger x, MutableBigInteger s, int n, BigInteger sToN1) {
2085+
MutableBigInteger dividend = new MutableBigInteger();
2086+
s.mul(n - 1, dividend);
2087+
MutableBigInteger xDivSToN1 = new MutableBigInteger();
2088+
x.divide(new MutableBigInteger(sToN1.mag), xDivSToN1, false);
2089+
dividend.add(xDivSToN1);
2090+
dividend.divideOneWord(n, s);
2091+
}
2092+
18952093
/**
18962094
* Calculate the integer square root {@code floor(sqrt(this))} and the remainder
18972095
* if needed, where {@code sqrt(.)} denotes the mathematical square root.

0 commit comments

Comments
 (0)