Skip to content

8355992: Add unsignedMultiplyExact and *powExact methods to Math and StrictMath #25003

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 225 additions & 0 deletions src/java.base/share/classes/java/lang/Math.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (x == 0 || x == 1) {
if (x == 0 || x == 1 || n == 1) {

return x;
}
if (x == -1) {
return (n & 0b1) == 0 ? 1 : -1;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (x == 2) {
if (n >= Integer.SIZE - 1)
throw new ArithmeticException("integer overflow");
return 1 << n;
}
if (x == -2) {
if (n >= Integer.SIZE)
throw new ArithmeticException("integer overflow");
// if n == Integer.SIZE - 1, result is correct
return (n & 0b1) == 0 ? 1 << n : -(1 << n);
}
if ((java.math.BigInteger.bitLengthForInt(Math.abs(x)) - 1L) * n + 1L > Integer.SIZE) {
throw new ArithmeticException("integer overflow");
}

With also a check for the condition java.math.BigInteger.bitLengthForInt(Math.abs(x)) * n < Integer.SIZE, when it is true the path could be led to a loop that skips the checks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return (n & 0b1) == 0 ? 1 << n : -(1 << n);

Equivalent to

return ((1 << n) ^ -(n & 1)) + (n & 1);

Without branches it should be faster

Copy link
Contributor

@fabioromano1 fabioromano1 May 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return ((1 << n) ^ -(n & 1)) + (n & 1);

It should have a comment that explains that this does the two's complement if n is odd, and it does nothing otherwise. Anyway, probably the optimization for x == -2 will not be included.

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (x == 0 || x == 1) {
if (x == 0 || x == 1 || n == 1) {

return x;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (x == 2) {
if (n >= Integer.SIZE)
throw new ArithmeticException("unsigned integer overflow");
return 1 << n;
}
if ((java.math.BigInteger.bitLengthForInt(x) - 1L) * n + 1L > Integer.SIZE) {
throw new ArithmeticException("unsigned integer overflow");
}

With also a check for the condition java.math.BigInteger.bitLengthForInt(x) * n <= Integer.SIZE, when it is true the path could be led to a loop that skips the checks.

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (x == 0 || x == 1) {
if (x == 0 || x == 1 || n == 1) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fabioromano1 Unless there's evidence that these cases are very very common, there's no point in adding fast paths.
See this comment in unsignedPowExact(long,int)

        /*
         * 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.
         */

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rgiulietti I would keep at least n == 1 and (bitLength(x) - 1L) * n + 1L > SIZE cases

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I don't think that n == 1 is a frequent case which would make any practical difference.

As for the bitLength check, the product might overflow.
Further, bitLength might not be that cheap.
Finally, the test would just help to fail faster at the expense of making the successful runs slightly slower.

Copy link
Contributor

@fabioromano1 fabioromano1 May 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for the bitLength check, the product might overflow. Further, bitLength might not be that cheap.

The current implementations of bitLength() call numberOfLeadingZeros(), which are:

public static int numberOfLeadingZeros(long i) {
        int x = (int)(i >>> 32);
        return x == 0 ? 32 + Integer.numberOfLeadingZeros((int)i)
                : Integer.numberOfLeadingZeros(x);
    }

public static int numberOfLeadingZeros(int i) {
        // HD, Count leading 0's
        if (i <= 0)
            return i == 0 ? 32 : 0;
        int n = 31;
        if (i >= 1 << 16) { n -= 16; i >>>= 16; }
        if (i >= 1 <<  8) { n -=  8; i >>>=  8; }
        if (i >= 1 <<  4) { n -=  4; i >>>=  4; }
        if (i >= 1 <<  2) { n -=  2; i >>>=  2; }
        return n - (i >>> 1);
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm familiar with both this Java code and the intrinsic code.

Compare this with the much simpler proposed code.
The checked multiplication unsignedMultiplyExact apparently performs two 64x64->64 multiplications, but on some architectures it might end up in a single 64x64->128 multiplication and one check.
So the proposed code performs 6 such multiplications if the method returns + 5 ordinary multiplications in the worst case.

As a general rule, the simpler the code, the better the outcome of the optimizing compiler.

Again, to me there's no point in failing fast at the expense of the successful case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm familiar with both this Java code and the intrinsic code.

Compare this with the much simpler proposed code. The checked multiplication unsignedMultiplyExact apparently performs two 64x64->64 multiplications, but on some architectures it might end up in a single 64x64->128 multiplication and one check. So the proposed code performs 6 such multiplications if the method returns + 5 ordinary multiplications in the worst case.

As a general rule, the simpler the code, the better the outcome of the optimizing compiler.

Again, to me there's no point in failing fast at the expense of the successful case.

Yes; we can always try to make simpler code faster if the need or interest arises.

return x;
}
if (x == -1) {
return (n & 0b1) != 0 ? -1 : 1;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (x == 2) {
if (n >= Long.SIZE - 1)
throw new ArithmeticException("long overflow");
return 1L << n;
}
if (x == -2) {
if (n >= Long.SIZE)
throw new ArithmeticException("long overflow");
// if n == Long.SIZE - 1, result is correct
return (n & 0b1) == 0 ? 1L << n : -(1L << n);
}
if ((java.math.BigInteger.bitLengthForLong(Math.abs(x)) - 1L) * n + 1L > Long.SIZE) {
throw new ArithmeticException("long overflow");
}

With also a check for the condition java.math.BigInteger.bitLengthForLong(Math.abs(x)) * n < Long.SIZE, when it is true the path could be led to a loop that skips the checks.

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (x == 0 || x == 1) {
if (x == 0 || x == 1 || n == 1) {

return x;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (x == 2) {
if (n >= Long.SIZE)
throw new ArithmeticException("unsigned long overflow");
return 1L << n;
}
if ((java.math.BigInteger.bitLengthForLong(x) - 1L) * n + 1L > Long.SIZE) {
throw new ArithmeticException("unsigned long overflow");
}

With also a check for the condition java.math.BigInteger.bitLengthForLong(x) * n <= Long.SIZE, when it is true the path could be led to a loop that skips the checks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's a quick, precise pre-check that would ensure that the loop can just use simple, unchecked * multiplications.

Consider unsignedPowExact(3L, 40), which does not overflow, versus unsignedPowExact(3L, 41), which does.
How would you pre-check these two cases using integer arithmetic?

IMO, you still need checked multiplications in the loop.

(Besides, the product in your checks can overflow, so you would have to add a guard.)

Copy link
Contributor

@fabioromano1 fabioromano1 May 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's a quick, precise pre-check that would ensure that the loop can just use simple, unchecked * multiplications.

Consider unsignedPowExact(3L, 40), which does not overflow, versus unsignedPowExact(3L, 41), which does. How would you pre-check these two cases using integer arithmetic?

IMO, you still need checked multiplications in the loop.

(Besides, the product in your checks can overflow, so you would have to add a guard.)

@rgiulietti

  1. BigInteger.bitLengthForLong(x) * n <= Long.SIZE is a sufficient condition to ensure no overflow;

  2. (BigInteger.bitLengthForLong(x) - 1L) * n + 1L > Long.SIZE is a sufficient condition to ensure the overflow.

Thus, there remain only the cases when Long.SIZE < BigInteger.bitLengthForLong(x) * n && (BigInteger.bitLengthForLong(x) - 1L) * n + 1L <= Long.SIZE, in this cases checked multiplications in the loop are needed.

Moreover, BigInteger.bitLengthForLong(x) - 1L is a long, so the product does not overflow, so the condition at point 1 never overflows if it is evaluated only if the condition at point 2 is false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fabioromano1 Well, there are two checks. In one the product can overflow, you'd need to convert one of the operands to long.

Anyway, since the pre-checks are not precise, that would lead to an implementation with a loop with checked, and another one with unchecked multiplications. I don't think this buys you anything.

Copy link
Contributor

@fabioromano1 fabioromano1 May 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, since the pre-checks are not precise, that would lead to an implementation with a loop with checked, and another one with unchecked multiplications. I don't think this buys you anything.

It serves to skip the checks in the loop if in the common cases the length of the results are way more little with respect to Long.SIZE and to fail fast if in the common cases the length of the results are way bigger than Long.SIZE.

@fabioromano1 Well, there are two checks. In one the product can overflow, you'd need to convert one of the operands to long.

If the condition at point 1 is evaluated only if the condition at point 2 is false, then it can never overflow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that check would be a couple of instructions or so, then I could agree.

True, there are no overflows in the checks.

Copy link
Contributor

@fabioromano1 fabioromano1 May 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rgiulietti If you think that these checks might be useful, the choice is yours.

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

}
107 changes: 107 additions & 0 deletions src/java.base/share/classes/java/lang/StrictMath.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

}
Loading