Better implementation of Long.divideUnsigned and Long.remainderUnsigned
John Platts
john_platts at hotmail.com
Thu Jul 7 15:11:27 UTC 2016
In the implementations of Long.divideUnsigned and Long.remainderUnsigned, there is a better way to handle the case where dividend > Long.MAX_VALUE and divisor <= Long.MAX_VALUE than doing it through a BigInteger.
Here is the current implementation of Long.divideUnsigned and Long.remainderUnsigned from OpenJDK 9:
/**
* Returns the unsigned quotient of dividing the first argument by
* the second where each argument and the result is interpreted as
* an unsigned value.
*
* <p>Note that in two's complement arithmetic, the three other
* basic arithmetic operations of add, subtract, and multiply are
* bit-wise identical if the two operands are regarded as both
* being signed or both being unsigned. Therefore separate {@code
* addUnsigned}, etc. methods are not provided.
*
* @param dividend the value to be divided
* @param divisor the value doing the dividing
* @return the unsigned quotient of the first argument divided by
* the second argument
* @see #remainderUnsigned
* @since 1.8
*/
public static long divideUnsigned(long dividend, long divisor) {
if (divisor < 0L) { // signed comparison
// Answer must be 0 or 1 depending on relative magnitude
// of dividend and divisor.
return (compareUnsigned(dividend, divisor)) < 0 ? 0L :1L;
}
if (dividend > 0) // Both inputs non-negative
return dividend/divisor;
else {
/*
* For simple code, leveraging BigInteger. Longer and faster
* code written directly in terms of operations on longs is
* possible; see "Hacker's Delight" for divide and remainder
* algorithms.
*/
return toUnsignedBigInteger(dividend).
divide(toUnsignedBigInteger(divisor)).longValue();
}
}
/**
* Returns the unsigned remainder from dividing the first argument
* by the second where each argument and the result is interpreted
* as an unsigned value.
*
* @param dividend the value to be divided
* @param divisor the value doing the dividing
* @return the unsigned remainder of the first argument divided by
* the second argument
* @see #divideUnsigned
* @since 1.8
*/
public static long remainderUnsigned(long dividend, long divisor) {
if (dividend > 0 && divisor > 0) { // signed comparisons
return dividend % divisor;
} else {
if (compareUnsigned(dividend, divisor) < 0) // Avoid explicit check for 0 divisor
return dividend;
else
return toUnsignedBigInteger(dividend).
remainder(toUnsignedBigInteger(divisor)).longValue();
}
}
Here is an better implementation that does not require conversion to BigInteger:
/**
* Returns the unsigned quotient of dividing the first argument by
* the second where each argument and the result is interpreted as
* an unsigned value.
*
* <p>Note that in two's complement arithmetic, the three other
* basic arithmetic operations of add, subtract, and multiply are
* bit-wise identical if the two operands are regarded as both
* being signed or both being unsigned. Therefore separate {@code
* addUnsigned}, etc. methods are not provided.
*
* @param dividend the value to be divided
* @param divisor the value doing the dividing
* @return the unsigned quotient of the first argument divided by
* the second argument
* @see #remainderUnsigned
* @since 1.8
*/
public static long divideUnsigned(long dividend, long divisor) {
if (divisor < 0L) { // signed comparison
// Answer must be 0 or 1 depending on relative magnitude
// of dividend and divisor.
return (compareUnsigned(dividend, divisor)) < 0 ? 0L :1L;
}
if (dividend > 0 || divisor <= 1) {
// One of the following is true:
// 1. Both inputs are positive, in which the result is equal to dividing
// dividend by divisor.
// 2. The divisor is equal to zero, which will result in a DivideByZeroException
// being thrown.
// 3. The divisor is equal to one, in which the result is equal to dividend.
return dividend/divisor;
} else {
long rMax = divisor - 1;
if(divisor != 0 && (divisor & rMax) == 0) { // divisor is power of 2
return dividend >>> Long.numberOfTrailingZeros(divisor);
}
// First, divide Long.MAX_VALUE + 1 by divisor.
// Division of Long.MAX_VALUE + 1 by divisor is done by the following:
// 1. Dividing Long.MAX_VALUE by divisor
// 2. Computing the remainder of Long.MAX_VALUE by divisor
// 3. Adding 1 to the remainder computed in step #2.
// 4. If the remainder is equal to divisor, add 1 to the quotient and set
// the remainder to zero.
long q = Long.MAX_VALUE / divisor;
long r = (Long.MAX_VALUE % divisor) + 1;
if(r == divisor) {
q++;
r = 0;
}
// Let x2 be equal to dividend - (Long.MAX_VALUE + 1).
// Compute the actual quotient and remainder of dividing dividend by divisor
// by doing the following:
// 1. Increment q by the quotient of dividing x2 by divisor.
// 2. Increment r by the remainder of dividing x2 by divisor.
// 3. If r >= divisor is true, then increment q by 1 and decrement r by divisor.
long x2 = dividend & Long.MAX_VALUE;
q += x2 / divisor;
r += x2 % divisor;
if(r >= divisor) {
q++;
r -= divisor;
}
// Return q, which is now the actual remainder of dividing dividend by divisor.
return q;
}
}
/**
* Returns the unsigned remainder from dividing the first argument
* by the second where each argument and the result is interpreted
* as an unsigned value.
*
* @param dividend the value to be divided
* @param divisor the value doing the dividing
* @return the unsigned remainder of the first argument divided by
* the second argument
* @see #divideUnsigned
* @since 1.8
*/
public static long remainderUnsigned(long dividend, long divisor) {
if (dividend > 0 && divisor > 0) { // signed comparisons
return dividend % divisor;
} else {
if (compareUnsigned(dividend, divisor) < 0) { // Avoid explicit check for 0 divisor
return dividend;
} else {
long rMax = divisor - 1;
if(divisor != 0 && (divisor & rMax) == 0) { // divisor is power of 2
return dividend & rMax;
}
// First, compute the remainder of dividing Long.MAX_VALUE + 1
// This is done by doing the following:
// 1. Computing the remainder of dividing Long.MAX_VALUE by divisor.
// 2. Adding 1 to the remainder computed in step #1.
// 3. If the remainder is equal to divisor, set the remainder to 0.
long r = (Long.MAX_VALUE % divisor) + 1;
if(r == divisor) {
r = 0;
}
// Let x2 be equal to dividend - (Long.MAX_VALUE + 1).
// Compute the actual remainder of dividing dividend by divisor
// by doing the following:
// 1. Increment r by the remainder of dividing x2 by divisor.
// 2. If r >= divisor is true, then decrement r by divisor.
long x2 = dividend & Long.MAX_VALUE;
r += x2 % divisor;
if(r >= divisor) {
r -= divisor;
}
// Return r, which is now the actual remainder of dividing dividend by divisor.
return r;
}
}
}
The updated implementations of divideUnsigned and remainderUnsigned avoid the memory allocations that are involved in doing division through BigInteger. In addition, the algorithm used to do division without going through a BigInteger is simpler than the actual implementation in the BigInteger class.
More information about the core-libs-dev
mailing list