diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 5f664b54cdf5b..9956077bb0931 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -3419,4 +3419,229 @@ static float powerOfTwoF(int n) { (FloatConsts.SIGNIFICAND_WIDTH-1)) & FloatConsts.EXP_BIT_MASK); } + + /** + * Returns the product of the unsigned arguments, + * throwing an exception if the result overflows an unsigned {@code int}. + * + * @param x the first unsigned value + * @param y the second unsigned value + * @return the result + * @throws ArithmeticException if the result overflows an unsigned int + * @since 25 + */ + public static int unsignedMultiplyExact(int x, int y) { + long r = (x & 0xFFFF_FFFFL) * (y & 0xFFFF_FFFFL); + if (r >>> 32 != 0) { + throw new ArithmeticException("unsigned integer overflow"); + } + return (int)r; + } + + /** + * Returns the product of the unsigned arguments, + * throwing an exception if the result overflows an unsigned {@code long}. + * + * @param x the first unsigned value + * @param y the second unsigned value + * @return the result + * @throws ArithmeticException if the result overflows an unsigned long + * @since 25 + */ + public static long unsignedMultiplyExact(long x, int y) { + return unsignedMultiplyExact(x, y & 0xFFFF_FFFFL); + } + + /** + * Returns the product of the unsigned arguments, + * throwing an exception if the result overflows an unsigned {@code long}. + * + * @param x the first unsigned value + * @param y the second unsigned value + * @return the result + * @throws ArithmeticException if the result overflows an unsigned long + * @since 25 + */ + public static long unsignedMultiplyExact(long x, long y) { + long l = x * y; + long h = unsignedMultiplyHigh(x, y); + if (h == 0) { + return l; + } + throw new ArithmeticException("unsigned long overflow"); + } + + /** + * Returns {@code x} raised to the power of {@code n}, + * throwing an exception if the result overflows an {@code int}. + * When {@code n} is 0, the returned value is 1. + * + * @param x the base. + * @param n the exponent. + * @return {@code x} raised to the power of {@code n}. + * @throws ArithmeticException when {@code n} is negative, + * or when the result overflows an int. + * @since 25 + */ + public static int powExact(int x, int n) { + /* See the comment in unsignedPowExact(long,int) for the details. */ + if (n < 0) { + throw new ArithmeticException("negative exponent"); + } + if (n == 0) { + return 1; + } + if (x == 0 || x == 1) { + return x; + } + if (x == -1) { + return (n & 0b1) == 0 ? 1 : -1; + } + + int p = 1; + while (n > 1) { + if ((n & 0b1) != 0) { + p *= x; + } + x = multiplyExact(x, x); + n >>>= 1; + } + return multiplyExact(p, x); + } + + /** + * Returns unsigned {@code x} raised to the power of {@code n}, + * throwing an exception if the result overflows an unsigned {@code int}. + * When {@code n} is 0, the returned value is 1. + * + * @param x the unsigned base. + * @param n the exponent. + * @return {@code x} raised to the power of {@code n}. + * @throws ArithmeticException when {@code n} is negative, + * or when the result overflows an unsigned int. + * @since 25 + */ + public static int unsignedPowExact(int x, int n) { + /* See the comment in unsignedPowExact(long,int) for the details. */ + if (n < 0) { + throw new ArithmeticException("negative exponent"); + } + if (n == 0) { + return 1; + } + if (x == 0 || x == 1) { + return x; + } + + int p = 1; + while (n > 1) { + if ((n & 0b1) != 0) { + p *= x; + } + x = unsignedMultiplyExact(x, x); + n >>>= 1; + } + return unsignedMultiplyExact(p, x); + } + + /** + * Returns {@code x} raised to the power of {@code n}, + * throwing an exception if the result overflows a {@code long}. + * When {@code n} is 0, the returned value is 1. + * + * @param x the base. + * @param n the exponent. + * @return {@code x} raised to the power of {@code n}. + * @throws ArithmeticException when {@code n} is negative, + * or when the result overflows a long. + * @since 25 + */ + public static long powExact(long x, int n) { + /* See the comment in unsignedPowExact(long,int) for the details. */ + if (n < 0) { + throw new ArithmeticException("negative exponent"); + } + if (n == 0) { + return 1; + } + if (x == 0 || x == 1) { + return x; + } + if (x == -1) { + return (n & 0b1) != 0 ? -1 : 1; + } + + long p = 1; + while (n > 1) { + if ((n & 0b1) != 0) { + p *= x; + } + x = multiplyExact(x, x); + n >>>= 1; + } + return multiplyExact(p, x); + } + + /** + * Returns unsigned {@code x} raised to the power of {@code n}, + * throwing an exception if the result overflows an unsigned {@code long}. + * When {@code n} is 0, the returned value is 1. + * + * @param x the unsigned base. + * @param n the exponent. + * @return {@code x} raised to the power of {@code n}. + * @throws ArithmeticException when {@code n} is negative, + * or when the result overflows an unsigned long. + * @since 25 + */ + public static long unsignedPowExact(long x, int n) { + if (n < 0) { + throw new ArithmeticException("negative exponent"); + } + if (n == 0) { + return 1; + } + /* + * To keep the code as simple as possible, there are intentionally + * no fast paths, except for |x| <= 1. + * The reason is that the number of loop iterations below can be kept + * very small when |x| > 1, but not necessarily when |x| <= 1. + */ + if (x == 0 || x == 1) { + return x; + } + + /* + * Let x0 and n0 > 0 be the entry values of x and n, resp. + * The useful loop invariants are: + * p * x^n = x0^n0 + * |p| < |x| + * + * Since |x0| >= 2 here, and since |x0|^(2^6) >= 2^Long.SIZE, the squaring + * of x in the loop overflows at latest during the 6th iteration, + * so by then the method throws. + * Thus, the loop executes at most 5 successful iterations, and fails + * not later than at the 6th. + * + * But n is right-shifted at each iteration. + * If the method returns, there are thus floor(log2(n0)) iterations. + */ + long p = 1; + while (n > 1) { + if ((n & 0b1) != 0) { + /* + * The invariant |p| < |x| holds, so we have |p*x| < |x*x|. + * That is, if p*x overflows, so does x*x below, which is + * always executed. + * In other words, a plain * can be used here, since we are + * piggybacking on the squaring of x to throw. + */ + p *= x; + } + x = unsignedMultiplyExact(x, x); + n >>>= 1; + } + return unsignedMultiplyExact(p, x); + } + } diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java index e3b51ed752c6d..266d98e394744 100644 --- a/src/java.base/share/classes/java/lang/StrictMath.java +++ b/src/java.base/share/classes/java/lang/StrictMath.java @@ -2594,4 +2594,111 @@ public static double scalb(double d, int scaleFactor) { public static float scalb(float f, int scaleFactor) { return Math.scalb(f, scaleFactor); } + + /** + * Returns the product of the unsigned arguments, + * throwing an exception if the result overflows an unsigned {@code int}. + * + * @param x the first unsigned value + * @param y the second unsigned value + * @return the result + * @throws ArithmeticException if the result overflows an unsigned int + * @since 25 + */ + public static int unsignedMultiplyExact(int x, int y) { + return Math.unsignedMultiplyExact(x, y); + } + + /** + * Returns the product of the unsigned arguments, + * throwing an exception if the result overflows an unsigned {@code long}. + * + * @param x the first unsigned value + * @param y the second unsigned value + * @return the result + * @throws ArithmeticException if the result overflows an unsigned long + * @since 25 + */ + public static long unsignedMultiplyExact(long x, int y) { + return Math.unsignedMultiplyExact(x, y); + } + + /** + * Returns the product of the unsigned arguments, + * throwing an exception if the result overflows an unsigned {@code long}. + * + * @param x the first unsigned value + * @param y the second unsigned value + * @return the result + * @throws ArithmeticException if the result overflows an unsigned long + * @since 25 + */ + public static long unsignedMultiplyExact(long x, long y) { + return Math.unsignedMultiplyExact(x, y); + } + + /** + * Returns {@code x} raised to the power of {@code n}, + * throwing an exception if the result overflows an {@code int}. + * When {@code n} is 0, the returned value is 1. + * + * @param x the base. + * @param n the exponent. + * @return {@code x} raised to the power of {@code n}. + * @throws ArithmeticException when {@code n} is negative, + * or when the result overflows an int. + * @since 25 + */ + public static int powExact(int x, int n) { + return Math.powExact(x, n); + } + + /** + * Returns unsigned {@code x} raised to the power of {@code n}, + * throwing an exception if the result overflows an unsigned {@code int}. + * When {@code n} is 0, the returned value is 1. + * + * @param x the unsigned base. + * @param n the exponent. + * @return {@code x} raised to the power of {@code n}. + * @throws ArithmeticException when {@code n} is negative, + * or when the result overflows an unsigned int. + * @since 25 + */ + public static int unsignedPowExact(int x, int n) { + return Math.unsignedPowExact(x, n); + } + + /** + * Returns {@code x} raised to the power of {@code n}, + * throwing an exception if the result overflows a {@code long}. + * When {@code n} is 0, the returned value is 1. + * + * @param x the base. + * @param n the exponent. + * @return {@code x} raised to the power of {@code n}. + * @throws ArithmeticException when {@code n} is negative, + * or when the result overflows a long. + * @since 25 + */ + public static long powExact(long x, int n) { + return Math.powExact(x, n); + } + + /** + * Returns unsigned {@code x} raised to the power of {@code n}, + * throwing an exception if the result overflows an unsigned {@code long}. + * When {@code n} is 0, the returned value is 1. + * + * @param x the unsigned base. + * @param n the exponent. + * @return {@code x} raised to the power of {@code n}. + * @throws ArithmeticException when {@code n} is negative, + * or when the result overflows an unsigned long. + * @since 25 + */ + public static long unsignedPowExact(long x, int n) { + return Math.unsignedPowExact(x, n); + } + } diff --git a/test/jdk/java/lang/Math/IntegralPowTest.java b/test/jdk/java/lang/Math/IntegralPowTest.java new file mode 100644 index 0000000000000..b0aadcc8d3b4a --- /dev/null +++ b/test/jdk/java/lang/Math/IntegralPowTest.java @@ -0,0 +1,392 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8355992 + * @summary Tests for StrictMath.*PowExact and .*unsignedMultiplyExact + * @run junit IntegralPowTest + */ + +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; + +import static java.lang.StrictMath.*; +import static java.math.BigInteger.ONE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class IntegralPowTest { + + private static final long MASK_32 = (1L << Integer.SIZE) - 1; // 2^32 - 1 + private static final BigInteger MASK_64 = ONE.shiftLeft(Long.SIZE).subtract(ONE); // 2^64 - 1 + private static final double INT_F = Integer.SIZE * Math.log(2); + private static final double LONG_F = Long.SIZE * Math.log(2); + private static final int INT_XMAX = 0x1_0000; + private static final int LONG_XMAX = 0x10_0000; + + private static BigInteger unsignedBigInteger(int x) { + return BigInteger.valueOf(x & MASK_32); + } + + private static BigInteger unsignedBigInteger(long x) { + return BigInteger.valueOf(x).and(MASK_64); + } + + private static int slowPowExact(int x, int n) { + BigInteger pow = BigInteger.valueOf(x).pow(n); + return pow.intValueExact(); + } + + private static int slowUnsignedPowExact(int x, int n) { + BigInteger pow = unsignedBigInteger(x).pow(n); + if (pow.bitLength() > Integer.SIZE) { + throw new ArithmeticException(); + } + return pow.intValue(); + } + + private static long slowPowExact(long x, int n) { + BigInteger pow = BigInteger.valueOf(x).pow(n); + return pow.longValueExact(); + } + + private static long slowUnsignedPowExact(long x, int n) { + BigInteger pow = unsignedBigInteger(x).pow(n); + if (pow.bitLength() > Long.SIZE) { + throw new ArithmeticException(); + } + return pow.longValue(); + } + + @Test + void testIntUnsignedMultiplyExact() { + assertEquals(0, unsignedMultiplyExact(0, 0)); + assertEquals(0, unsignedMultiplyExact(1, 0)); + assertEquals(0, unsignedMultiplyExact(-1, 0)); + assertEquals(1, unsignedMultiplyExact(1, 1)); + assertEquals(-1, unsignedMultiplyExact(1, -1)); + assertEquals(1 << 31, unsignedMultiplyExact(1 << 15, 1 << 16)); + assertEquals(1 << 31, unsignedMultiplyExact(1 << 10, 1 << 21)); + /* 2^32 - 1 = (2^16 + 1) (2^16 - 1) */ + assertEquals(-1, unsignedMultiplyExact((1 << 16) + 1, (1 << 16) - 1)); + + assertThrows(ArithmeticException.class, () -> unsignedMultiplyExact(-1, -1)); + } + + @Test + void testLongIntUnsignedMultiplyExact() { + assertEquals(0L, unsignedMultiplyExact(0L, 0)); + assertEquals(0L, unsignedMultiplyExact(1L, 0)); + assertEquals(0L, unsignedMultiplyExact(-1L, 0)); + assertEquals(1L, unsignedMultiplyExact(1L, 1)); + assertEquals(-3 & MASK_32, unsignedMultiplyExact(1L, -3)); + assertEquals(1L << 50, unsignedMultiplyExact(1L << 25, 1 << 25)); + /* 2^64 - 1 = (2^32 + 1) (2^32 - 1) */ + assertEquals(-1L, unsignedMultiplyExact((1L << 32) + 1, -1)); + + assertThrows(ArithmeticException.class, () -> unsignedMultiplyExact(-1L, -1)); + } + + @Test + void testLongUnsignedMultiplyExact() { + assertEquals(0L, unsignedMultiplyExact(0L, 0L)); + assertEquals(0L, unsignedMultiplyExact(1L, 0L)); + assertEquals(0L, unsignedMultiplyExact(-1L, 0L)); + assertEquals(1L, unsignedMultiplyExact(1L, 1L)); + assertEquals(-1L, unsignedMultiplyExact(1L, -1L)); + assertEquals(1L << 63, unsignedMultiplyExact(1L << 31, 1L << 32)); + assertEquals(1L << 63, unsignedMultiplyExact(1L << 25, 1L << 38)); + /* 2^64 - 1 = (2^32 + 1) (2^32 - 1) */ + assertEquals(-1L, unsignedMultiplyExact((1L << 32) + 1, (1L << 32) - 1)); + + assertThrows(ArithmeticException.class, () -> unsignedMultiplyExact(-1L, -1L)); + } + + @Test + void testIntPowExact() { + assertEquals(1, powExact(0, 0)); + assertEquals(0, powExact(0, 1_000_000)); + assertEquals(1, powExact(1, 0)); + assertEquals(1, powExact(1, 1_000_000)); + assertEquals(1, powExact(-1, 0)); + assertEquals(1, powExact(-1, 1_000_000)); + assertEquals(-1, powExact(-1, 1_000_001)); + + assertEquals(1 << -2, powExact(2, Integer.SIZE - 2)); + assertEquals(-1 << -1, powExact(-2, Integer.SIZE - 1)); + assertEquals(1_000_000_000, powExact(10, 9)); + assertEquals(-1_000_000_000, powExact(-10, 9)); + + assertThrows(ArithmeticException.class, () -> powExact(0, -1_000_000)); + assertThrows(ArithmeticException.class, () -> powExact(1, -1_000_000)); + assertThrows(ArithmeticException.class, () -> powExact(2, Integer.SIZE - 1)); + assertThrows(ArithmeticException.class, () -> powExact(10, 10)); + assertThrows(ArithmeticException.class, () -> powExact(-10, 10)); + } + + @Test + void testUnsignedIntPowExact() { + assertEquals(1, unsignedPowExact(0, 0)); + assertEquals(0, unsignedPowExact(0, 1_000_000)); + assertEquals(1, unsignedPowExact(1, 0)); + assertEquals(1, unsignedPowExact(1, 1_000_000)); + assertEquals(1, unsignedPowExact(-1, 0)); + assertEquals(-1, unsignedPowExact(-1, 1)); + + assertEquals(1 << -1, unsignedPowExact(2, Integer.SIZE - 1)); + assertEquals(1_000_000_000, unsignedPowExact(10, 9)); + + assertThrows(ArithmeticException.class, () -> unsignedPowExact(0, -1_000_000)); + assertThrows(ArithmeticException.class, () -> unsignedPowExact(1, -1_000_000)); + assertThrows(ArithmeticException.class, () -> unsignedPowExact(-1, 2)); + assertThrows(ArithmeticException.class, () -> unsignedPowExact(2, Integer.SIZE)); + assertThrows(ArithmeticException.class, () -> unsignedPowExact(10, 10)); + } + + @Test + void testLongPowExact() { + assertEquals(1L, powExact(0L, 0)); + assertEquals(0L, powExact(0L, 1_000_000)); + assertEquals(1L, powExact(1L, 0)); + assertEquals(1L, powExact(1L, 1_000_000)); + assertEquals(1L, powExact(-1L, 0)); + assertEquals(1L, powExact(-1L, 1_000_000)); + assertEquals(-1L, powExact(-1L, 1_000_001)); + + assertEquals(1L << -2, powExact(2L, Long.SIZE - 2)); + assertEquals(-1L << -1, powExact(-2L, Long.SIZE - 1)); + assertEquals(1_000_000_000_000_000_000L, powExact(10L, 18)); + assertEquals(-100_000_000_000_000_000L, powExact(-10L, 17)); + assertEquals(1_000_000_000_000_000_000L, powExact(-10L, 18)); + + assertThrows(ArithmeticException.class, () -> powExact(0L, -1_000_000)); + assertThrows(ArithmeticException.class, () -> powExact(1L, -1_000_000)); + assertThrows(ArithmeticException.class, () -> powExact(2L, Long.SIZE - 1)); + assertThrows(ArithmeticException.class, () -> powExact(10L, 19)); + assertThrows(ArithmeticException.class, () -> powExact(-10L, 19)); + } + + @Test + void testUnsignedLongPowExact() { + assertEquals(1L, unsignedPowExact(0L, 0)); + assertEquals(0L, unsignedPowExact(0L, 1_000_000)); + assertEquals(1L, unsignedPowExact(1L, 0)); + assertEquals(1L, unsignedPowExact(1L, 1_000_000)); + assertEquals(1L, unsignedPowExact(-1L, 0)); + assertEquals(-1L, unsignedPowExact(-1L, 1)); + + assertEquals(1L << -1, unsignedPowExact(2L, Long.SIZE - 1)); + assertEquals(10 * 1_000_000_000_000_000_000L, unsignedPowExact(10L, 19)); + + assertThrows(ArithmeticException.class, () -> unsignedPowExact(0L, -1_000_000)); + assertThrows(ArithmeticException.class, () -> unsignedPowExact(1L, -1_000_000)); + assertThrows(ArithmeticException.class, () -> unsignedPowExact(-1L, 2)); + assertThrows(ArithmeticException.class, () -> unsignedPowExact(2L, Long.SIZE)); + assertThrows(ArithmeticException.class, () -> unsignedPowExact(10L, 20)); + } + + /* + * Assumes that x^n != 0 + * Returns x^n, or 0 on overflow + */ + private static int expected(int x, int n) { + try { + return powExact(x, n); + } catch (ArithmeticException ignore) { + return 0; + } + } + + /* + * Assumes that x^n != 0 + * Returns x^n, or 0 on overflow + */ + private static int actual(int x, int n) { + try { + return slowPowExact(x, n); + } catch (ArithmeticException ignore) { + return 0; + } + } + + /* + * Assumes that x^n != 0 + * Returns x^n, or 0 on overflow + */ + private static int expectedUnsigned(int x, int n) { + try { + return unsignedPowExact(x, n); + } catch (ArithmeticException ignore) { + return 0; + } + } + + /* + * Assumes that x^n != 0 + * Returns x^n, or 0 on overflow + */ + private static int actualUnsigned(int x, int n) { + try { + return slowUnsignedPowExact(x, n); + } catch (ArithmeticException ignore) { + return 0; + } + } + + /* + * Assumes that x^n != 0 + * Returns x^n, or 0 on overflow + */ + private static long expected(long x, int n) { + try { + return powExact(x, n); + } catch (ArithmeticException ignore) { + return 0; + } + } + + /* + * Assumes that x^n != 0 + * Returns x^n, or 0 on overflow + */ + private static long actual(long x, int n) { + try { + return slowPowExact(x, n); + } catch (ArithmeticException ignore) { + return 0; + } + } + + /* + * Assumes that x^n != 0 + * Returns x^n, or 0 on overflow + */ + private static long expectedUnsigned(long x, int n) { + try { + return unsignedPowExact(x, n); + } catch (ArithmeticException ignore) { + return 0; + } + } + + /* + * Assumes that x^n != 0 + * Returns x^n, or 0 on overflow + */ + private static long actualUnsigned(long x, int n) { + try { + return slowUnsignedPowExact(x, n); + } catch (ArithmeticException ignore) { + return 0; + } + } + + /* signed int */ + + @Test + void testPositiveIntPowExact() { + for (int x = 2; x <= INT_XMAX; x += 1) { + /* An estimate for the max n such that x^n does not overflow. */ + int nmax = (int) ceil(INT_F / log(x)); + for (int n = 0; n <= nmax; ++n) { + assertEquals(actual(x, n), expected(x, n)); + } + int x0 = x; + assertThrows(ArithmeticException.class, () -> powExact(x0, nmax + 1)); + } + } + + @Test + void testNegativeIntPowExact() { + for (int x = 2; x <= INT_XMAX; x += 1) { + /* An estimate for the max n such that (-x)^n does not overflow. */ + int nmax = (int) ceil(INT_F / log(x)); + for (int n = 0; n <= nmax; ++n) { + assertEquals(actual(-x, n), expected(-x, n)); + } + int x0 = x; + assertThrows(ArithmeticException.class, () -> powExact(-x0, nmax + 1)); + } + } + + /* unsigned int */ + + @Test + void testSmallUnsignedIntPowExact() { + for (int x = 2; x <= INT_XMAX; x += 1) { + /* An estimate for the max n such that x^n does not overflow. */ + int nmax = (int) ceil(INT_F / log(x)); + for (int n = 0; n <= nmax; ++n) { + assertEquals(actualUnsigned(x, n), expectedUnsigned(x, n)); + } + int x0 = x; + assertThrows(ArithmeticException.class, () -> unsignedPowExact(x0, nmax + 1)); + } + } + + /* signed long */ + + @Test + void testPositiveLongPowExact() { + for (long x = 2; x <= LONG_XMAX; x += 5) { + /* An estimate for the max n such that x^n does not overflow. */ + int nmax = (int) ceil(LONG_F / log(x)); + for (int n = 0; n <= nmax; ++n) { + assertEquals(actual(x, n), expected(x, n)); + } + long x0 = x; + assertThrows(ArithmeticException.class, () -> powExact(x0, nmax + 1)); + } + } + + @Test + void testNegativeLongPowExact() { + for (long x = 2; x <= LONG_XMAX; x += 5) { + /* An estimate for the max n such that (-x)^n does not overflow. */ + int nmax = (int) ceil(LONG_F / log(x)); + for (int n = 0; n <= nmax; ++n) { + assertEquals(actual(-x, n), expected(-x, n)); + } + long x0 = x; + assertThrows(ArithmeticException.class, () -> powExact(-x0, nmax + 1)); + } + } + + /* unsigned long */ + + @Test + void testSmallUnsignedLongPowExact() { + for (long x = 2; x <= LONG_XMAX; x += 5) { + /* An estimate for the max n such that x^n does not overflow. */ + int nmax = (int) ceil(LONG_F / log(x)); + for (int n = 0; n <= nmax; ++n) { + assertEquals(actualUnsigned(x, n), expectedUnsigned(x, n)); + } + long x0 = x; + assertThrows(ArithmeticException.class, () -> unsignedPowExact(x0, nmax + 1)); + } + } + +}