6622432: RFE: Performance improvements to java.math.BigDecimal

Reviewed-by: darcy
This commit is contained in:
Xiaobin Lu 2009-05-24 16:29:57 -07:00
parent f625a6d545
commit 9f9d70b270
8 changed files with 1462 additions and 1094 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -110,13 +110,11 @@ class BitSieve {
int convertedStep = (step *2) + 1;
// Construct the large sieve at an even offset specified by base
MutableBigInteger r = new MutableBigInteger();
MutableBigInteger b = new MutableBigInteger(base);
MutableBigInteger q = new MutableBigInteger();
do {
// Calculate base mod convertedStep
r.copyValue(base.mag);
r.divideOneWord(convertedStep, q);
start = r.value[r.offset];
start = b.divideOneWord(convertedStep, q);
// Take each multiple of step out of sieve
start = convertedStep - start;

View File

@ -126,19 +126,6 @@ public final class MathContext implements Serializable {
*/
final RoundingMode roundingMode;
/**
* Lookaside for the rounding points (the numbers which determine
* whether the coefficient of a number will require rounding).
* These will be present if {@code precision > 0} and
* {@code precision <= MAX_LOOKASIDE}. In this case they will share the
* {@code BigInteger int[]} array. Note that the transients
* cannot be {@code final} because they are reconstructed on
* deserialization.
*/
transient BigInteger roundingMax = null;
transient BigInteger roundingMin = null;
private static final int MAX_LOOKASIDE = 1000;
/* ----- Constructors ----- */
/**
@ -173,11 +160,6 @@ public final class MathContext implements Serializable {
throw new NullPointerException("null RoundingMode");
precision = setPrecision;
if (precision > 0 && precision <= MAX_LOOKASIDE) {
roundingMax = BigInteger.TEN.pow(precision);
roundingMin = roundingMax.negate();
}
roundingMode = setRoundingMode;
return;
}
@ -221,10 +203,6 @@ public final class MathContext implements Serializable {
throw new IllegalArgumentException("Digits < 0");
// the other parameters cannot be invalid if we got here
precision = setPrecision;
if (precision > 0 && precision <= MAX_LOOKASIDE) {
roundingMax = BigInteger.TEN.pow(precision);
roundingMin = roundingMax.negate();
}
}
/**
@ -343,11 +321,6 @@ public final class MathContext implements Serializable {
String message = "MathContext: null roundingMode in stream";
throw new java.io.StreamCorruptedException(message);
}
// Set the lookaside, if applicable
if (precision <= MAX_LOOKASIDE) {
roundingMax = BigInteger.TEN.pow(precision);
roundingMin = roundingMax.negate();
}
}
}

View File

@ -41,6 +41,11 @@ package java.math;
* @since 1.3
*/
import java.util.Arrays;
import static java.math.BigInteger.LONG_MASK;
import static java.math.BigDecimal.INFLATED;
class MutableBigInteger {
/**
* Holds the magnitude of this MutableBigInteger in big endian order.
@ -62,10 +67,13 @@ class MutableBigInteger {
*/
int offset = 0;
// Constants
/**
* This mask is used to obtain the value of an int as if it were unsigned.
* MutableBigInteger with one element value array with the value 1. Used by
* BigDecimal divideAndRound to increment the quotient. Use this constant
* only when the method is not going to modify this object.
*/
private final static long LONG_MASK = 0xffffffffL;
static final MutableBigInteger ONE = new MutableBigInteger(1);
// Constructors
@ -88,15 +96,6 @@ class MutableBigInteger {
value[0] = val;
}
/**
* Construct a new MutableBigInteger with the specified value array
* up to the specified length.
*/
MutableBigInteger(int[] val, int len) {
value = val;
intLen = len;
}
/**
* Construct a new MutableBigInteger with the specified value array
* up to the length of the array supplied.
@ -111,8 +110,8 @@ class MutableBigInteger {
* specified BigInteger.
*/
MutableBigInteger(BigInteger b) {
value = b.mag.clone();
intLen = value.length;
intLen = b.mag.length;
value = Arrays.copyOf(b.mag, intLen);
}
/**
@ -121,10 +120,58 @@ class MutableBigInteger {
*/
MutableBigInteger(MutableBigInteger val) {
intLen = val.intLen;
value = new int[intLen];
value = Arrays.copyOfRange(val.value, val.offset, val.offset + intLen);
}
for(int i=0; i<intLen; i++)
value[i] = val.value[val.offset+i];
/**
* Internal helper method to return the magnitude array. The caller is not
* supposed to modify the returned array.
*/
private int[] getMagnitudeArray() {
if (offset > 0 || value.length != intLen)
return Arrays.copyOfRange(value, offset, offset + intLen);
return value;
}
/**
* Convert this MutableBigInteger to a long value. The caller has to make
* sure this MutableBigInteger can be fit into long.
*/
private long toLong() {
assert (intLen <= 2) : "this MutableBigInteger exceeds the range of long";
if (intLen == 0)
return 0;
long d = value[offset] & LONG_MASK;
return (intLen == 2) ? d << 32 | (value[offset + 1] & LONG_MASK) : d;
}
/**
* Convert this MutableBigInteger to a BigInteger object.
*/
BigInteger toBigInteger(int sign) {
if (intLen == 0 || sign == 0)
return BigInteger.ZERO;
return new BigInteger(getMagnitudeArray(), sign);
}
/**
* Convert this MutableBigInteger to BigDecimal object with the specified sign
* and scale.
*/
BigDecimal toBigDecimal(int sign, int scale) {
if (intLen == 0 || sign == 0)
return BigDecimal.valueOf(0, scale);
int[] mag = getMagnitudeArray();
int len = mag.length;
int d = mag[0];
// If this MutableBigInteger can't be fit into long, we need to
// make a BigInteger object for the resultant BigDecimal object.
if (len > 2 || (d < 0 && len == 2))
return new BigDecimal(new BigInteger(mag, sign), INFLATED, scale, 0);
long v = (len == 2) ?
((mag[1] & LONG_MASK) | (d & LONG_MASK) << 32) :
d & LONG_MASK;
return new BigDecimal(null, sign == -1 ? -v : v, scale, 0);
}
/**
@ -146,17 +193,21 @@ class MutableBigInteger {
/**
* Compare the magnitude of two MutableBigIntegers. Returns -1, 0 or 1
* as this MutableBigInteger is numerically less than, equal to, or
* greater than {@code b}.
* greater than <tt>b</tt>.
*/
final int compare(MutableBigInteger b) {
if (intLen < b.intLen)
int blen = b.intLen;
if (intLen < blen)
return -1;
if (intLen > b.intLen)
return 1;
if (intLen > blen)
return 1;
for (int i=0; i<intLen; i++) {
int b1 = value[offset+i] + 0x80000000;
int b2 = b.value[b.offset+i] + 0x80000000;
// Add Integer.MIN_VALUE to make the comparison act as unsigned integer
// comparison.
int[] bval = b.value;
for (int i = offset, j = b.offset; i < intLen + offset; i++, j++) {
int b1 = value[i] + 0x80000000;
int b2 = bval[j] + 0x80000000;
if (b1 < b2)
return -1;
if (b1 > b2)
@ -165,6 +216,46 @@ class MutableBigInteger {
return 0;
}
/**
* Compare this against half of a MutableBigInteger object (Needed for
* remainder tests).
* Assumes no leading unnecessary zeros, which holds for results
* from divide().
*/
final int compareHalf(MutableBigInteger b) {
int blen = b.intLen;
int len = intLen;
if (len <= 0)
return blen <=0 ? 0 : -1;
if (len > blen)
return 1;
if (len < blen - 1)
return -1;
int[] bval = b.value;
int bstart = 0;
int carry = 0;
// Only 2 cases left:len == blen or len == blen - 1
if (len != blen) { // len == blen - 1
if (bval[bstart] == 1) {
++bstart;
carry = 0x80000000;
} else
return -1;
}
// compare values with right-shifted values of b,
// carrying shifted-out bits across words
int[] val = value;
for (int i = offset, j = bstart; i < len + offset;) {
int bv = bval[j++];
long hb = ((bv >>> 1) + carry) & LONG_MASK;
long v = val[i++] & LONG_MASK;
if (v != hb)
return v < hb ? -1 : 1;
carry = (bv & 1) << 31; // carray will be either 0x80000000 or 0
}
return carry == 0? 0 : -1;
}
/**
* Return the index of the lowest set bit in this MutableBigInteger. If the
* magnitude of this MutableBigInteger is zero, -1 is returned.
@ -178,7 +269,7 @@ class MutableBigInteger {
b = value[j+offset];
if (b==0)
return -1;
return ((intLen-1-j)<<5) + BigInteger.trailingZeroCnt(b);
return ((intLen-1-j)<<5) + Integer.numberOfTrailingZeros(b);
}
/**
@ -270,13 +361,11 @@ class MutableBigInteger {
* Sets this MutableBigInteger's value array to a copy of the specified
* array. The intLen is set to the length of the new array.
*/
void copyValue(MutableBigInteger val) {
int len = val.intLen;
void copyValue(MutableBigInteger src) {
int len = src.intLen;
if (value.length < len)
value = new int[len];
for(int i=0; i<len; i++)
value[i] = val.value[val.offset+i];
System.arraycopy(src.value, src.offset, value, 0, len);
intLen = len;
offset = 0;
}
@ -289,8 +378,7 @@ class MutableBigInteger {
int len = val.length;
if (value.length < len)
value = new int[len];
for(int i=0; i<len; i++)
value[i] = val[i];
System.arraycopy(val, 0, value, 0, len);
intLen = len;
offset = 0;
}
@ -320,7 +408,7 @@ class MutableBigInteger {
* Returns true iff this MutableBigInteger is odd.
*/
boolean isOdd() {
return ((value[offset + intLen - 1] & 1) == 1);
return isZero() ? false : ((value[offset + intLen - 1] & 1) == 1);
}
/**
@ -340,7 +428,7 @@ class MutableBigInteger {
* Returns a String representation of this MutableBigInteger in radix 10.
*/
public String toString() {
BigInteger b = new BigInteger(this, 1);
BigInteger b = toBigInteger(1);
return b.toString();
}
@ -356,7 +444,7 @@ class MutableBigInteger {
this.intLen -= nInts;
if (nBits == 0)
return;
int bitsInHighWord = BigInteger.bitLen(value[offset]);
int bitsInHighWord = BigInteger.bitLengthForInt(value[offset]);
if (nBits >= bitsInHighWord) {
this.primitiveLeftShift(32 - nBits);
this.intLen--;
@ -379,7 +467,7 @@ class MutableBigInteger {
return;
int nInts = n >>> 5;
int nBits = n&0x1F;
int bitsInHighWord = BigInteger.bitLen(value[offset]);
int bitsInHighWord = BigInteger.bitLengthForInt(value[offset]);
// If shift can be done without moving words, do so
if (n <= (32-bitsInHighWord)) {
@ -499,34 +587,41 @@ class MutableBigInteger {
int[] result = (value.length < resultLen ? new int[resultLen] : value);
int rstart = result.length-1;
long sum = 0;
long sum;
long carry = 0;
// Add common parts of both numbers
while(x>0 && y>0) {
x--; y--;
sum = (value[x+offset] & LONG_MASK) +
(addend.value[y+addend.offset] & LONG_MASK) + (sum >>> 32);
(addend.value[y+addend.offset] & LONG_MASK) + carry;
result[rstart--] = (int)sum;
carry = sum >>> 32;
}
// Add remainder of the longer number
while(x>0) {
x--;
sum = (value[x+offset] & LONG_MASK) + (sum >>> 32);
if (carry == 0 && result == value && rstart == (x + offset))
return;
sum = (value[x+offset] & LONG_MASK) + carry;
result[rstart--] = (int)sum;
carry = sum >>> 32;
}
while(y>0) {
y--;
sum = (addend.value[y+addend.offset] & LONG_MASK) + (sum >>> 32);
sum = (addend.value[y+addend.offset] & LONG_MASK) + carry;
result[rstart--] = (int)sum;
carry = sum >>> 32;
}
if ((sum >>> 32) > 0) { // Result must grow in length
if (carry > 0) { // Result must grow in length
resultLen++;
if (result.length < resultLen) {
int temp[] = new int[resultLen];
for (int i=resultLen-1; i>0; i--)
temp[i] = result[i-1];
// Result one word longer from carry-out; copy low-order
// bits into new result.
System.arraycopy(result, 0, temp, 1, result.length);
temp[0] = 1;
result = temp;
} else {
@ -708,29 +803,26 @@ class MutableBigInteger {
z.value = zval;
}
/**
/**
* This method is used for division of an n word dividend by a one word
* divisor. The quotient is placed into quotient. The one word divisor is
* specified by divisor. The value of this MutableBigInteger is the
* dividend at invocation but is replaced by the remainder.
* specified by divisor.
*
* @return the remainder of the division is returned.
*
* NOTE: The value of this MutableBigInteger is modified by this method.
*/
void divideOneWord(int divisor, MutableBigInteger quotient) {
long divLong = divisor & LONG_MASK;
int divideOneWord(int divisor, MutableBigInteger quotient) {
long divisorLong = divisor & LONG_MASK;
// Special case of one word dividend
if (intLen == 1) {
long remValue = value[offset] & LONG_MASK;
quotient.value[0] = (int) (remValue / divLong);
quotient.intLen = (quotient.value[0] == 0) ? 0 : 1;
long dividendValue = value[offset] & LONG_MASK;
int q = (int) (dividendValue / divisorLong);
int r = (int) (dividendValue - q * divisorLong);
quotient.value[0] = q;
quotient.intLen = (q == 0) ? 0 : 1;
quotient.offset = 0;
value[0] = (int) (remValue - (quotient.value[0] * divLong));
offset = 0;
intLen = (value[0] == 0) ? 0 : 1;
return;
return r;
}
if (quotient.value.length < intLen)
@ -739,15 +831,15 @@ class MutableBigInteger {
quotient.intLen = intLen;
// Normalize the divisor
int shift = 32 - BigInteger.bitLen(divisor);
int shift = Integer.numberOfLeadingZeros(divisor);
int rem = value[offset];
long remLong = rem & LONG_MASK;
if (remLong < divLong) {
if (remLong < divisorLong) {
quotient.value[0] = 0;
} else {
quotient.value[0] = (int)(remLong/divLong);
rem = (int) (remLong - (quotient.value[0] * divLong));
quotient.value[0] = (int)(remLong / divisorLong);
rem = (int) (remLong - (quotient.value[0] * divisorLong));
remLong = rem & LONG_MASK;
}
@ -757,8 +849,8 @@ class MutableBigInteger {
long dividendEstimate = (remLong<<32) |
(value[offset + intLen - xlen] & LONG_MASK);
if (dividendEstimate >= 0) {
qWord[0] = (int) (dividendEstimate/divLong);
qWord[1] = (int) (dividendEstimate - (qWord[0] * divLong));
qWord[0] = (int) (dividendEstimate / divisorLong);
qWord[1] = (int) (dividendEstimate - qWord[0] * divisorLong);
} else {
divWord(qWord, dividendEstimate, divisor);
}
@ -767,81 +859,110 @@ class MutableBigInteger {
remLong = rem & LONG_MASK;
}
quotient.normalize();
// Unnormalize
if (shift > 0)
value[0] = rem %= divisor;
return rem % divisor;
else
value[0] = rem;
intLen = (value[0] == 0) ? 0 : 1;
quotient.normalize();
return rem;
}
/**
* Calculates the quotient and remainder of this div b and places them
* in the MutableBigInteger objects provided.
* Calculates the quotient of this div b and places the quotient in the
* provided MutableBigInteger objects and the remainder object is returned.
*
* Uses Algorithm D in Knuth section 4.3.1.
* Many optimizations to that algorithm have been adapted from the Colin
* Plumb C library.
* It special cases one word divisors for speed.
* The contents of a and b are not changed.
* It special cases one word divisors for speed. The content of b is not
* changed.
*
*/
void divide(MutableBigInteger b,
MutableBigInteger quotient, MutableBigInteger rem) {
MutableBigInteger divide(MutableBigInteger b, MutableBigInteger quotient) {
if (b.intLen == 0)
throw new ArithmeticException("BigInteger divide by zero");
// Dividend is zero
if (intLen == 0) {
quotient.intLen = quotient.offset = rem.intLen = rem.offset = 0;
return;
quotient.intLen = quotient.offset;
return new MutableBigInteger();
}
int cmp = compare(b);
// Dividend less than divisor
if (cmp < 0) {
quotient.intLen = quotient.offset = 0;
rem.copyValue(this);
return;
return new MutableBigInteger(this);
}
// Dividend equal to divisor
if (cmp == 0) {
quotient.value[0] = quotient.intLen = 1;
quotient.offset = rem.intLen = rem.offset = 0;
return;
quotient.offset = 0;
return new MutableBigInteger();
}
quotient.clear();
// Special case one word divisor
if (b.intLen == 1) {
rem.copyValue(this);
rem.divideOneWord(b.value[b.offset], quotient);
return;
int r = divideOneWord(b.value[b.offset], quotient);
if (r == 0)
return new MutableBigInteger();
return new MutableBigInteger(r);
}
// Copy divisor value to protect divisor
int[] d = new int[b.intLen];
for(int i=0; i<b.intLen; i++)
d[i] = b.value[b.offset+i];
int dlen = b.intLen;
int[] div = Arrays.copyOfRange(b.value, b.offset, b.offset + b.intLen);
return divideMagnitude(div, quotient);
}
/**
* Internally used to calculate the quotient of this div v and places the
* quotient in the provided MutableBigInteger object and the remainder is
* returned.
*
* @return the remainder of the division will be returned.
*/
long divide(long v, MutableBigInteger quotient) {
if (v == 0)
throw new ArithmeticException("BigInteger divide by zero");
// Dividend is zero
if (intLen == 0) {
quotient.intLen = quotient.offset = 0;
return 0;
}
if (v < 0)
v = -v;
int d = (int)(v >>> 32);
quotient.clear();
// Special case on word divisor
if (d == 0)
return divideOneWord((int)v, quotient) & LONG_MASK;
else {
int[] div = new int[]{ d, (int)(v & LONG_MASK) };
return divideMagnitude(div, quotient).toLong();
}
}
/**
* Divide this MutableBigInteger by the divisor represented by its magnitude
* array. The quotient will be placed into the provided quotient object &
* the remainder object is returned.
*/
private MutableBigInteger divideMagnitude(int[] divisor,
MutableBigInteger quotient) {
// Remainder starts as dividend with space for a leading zero
if (rem.value.length < intLen +1)
rem.value = new int[intLen+1];
for (int i=0; i<intLen; i++)
rem.value[i+1] = value[i+offset];
MutableBigInteger rem = new MutableBigInteger(new int[intLen + 1]);
System.arraycopy(value, offset, rem.value, 1, intLen);
rem.intLen = intLen;
rem.offset = 1;
int nlen = rem.intLen;
// Set the quotient size
int dlen = divisor.length;
int limit = nlen - dlen + 1;
if (quotient.value.length < limit) {
quotient.value = new int[limit];
@ -851,10 +972,10 @@ class MutableBigInteger {
int[] q = quotient.value;
// D1 normalize the divisor
int shift = 32 - BigInteger.bitLen(d[0]);
int shift = Integer.numberOfLeadingZeros(divisor[0]);
if (shift > 0) {
// First shift will not grow array
BigInteger.primitiveLeftShift(d, dlen, shift);
BigInteger.primitiveLeftShift(divisor, dlen, shift);
// But this one might
rem.leftShift(shift);
}
@ -866,9 +987,9 @@ class MutableBigInteger {
rem.intLen++;
}
int dh = d[0];
int dh = divisor[0];
long dhLong = dh & LONG_MASK;
int dl = d[1];
int dl = divisor[1];
int[] qWord = new int[2];
// D2 Initialize j
@ -910,7 +1031,7 @@ class MutableBigInteger {
qhat--;
qrem = (int)((qrem & LONG_MASK) + dhLong);
if ((qrem & LONG_MASK) >= dhLong) {
estProduct = (dl & LONG_MASK) * (qhat & LONG_MASK);
estProduct -= (dl & LONG_MASK);
rs = ((qrem & LONG_MASK) << 32) | nl;
if (unsignedLongCompare(estProduct, rs))
qhat--;
@ -920,12 +1041,12 @@ class MutableBigInteger {
// D4 Multiply and subtract
rem.value[j+rem.offset] = 0;
int borrow = mulsub(rem.value, d, qhat, dlen, j+rem.offset);
int borrow = mulsub(rem.value, divisor, qhat, dlen, j+rem.offset);
// D5 Test remainder
if (borrow + 0x80000000 > nh2) {
// D6 Add back
divadd(d, rem.value, j+1+rem.offset);
divadd(divisor, rem.value, j+1+rem.offset);
qhat--;
}
@ -937,8 +1058,9 @@ class MutableBigInteger {
if (shift > 0)
rem.rightShift(shift);
rem.normalize();
quotient.normalize();
rem.normalize();
return rem;
}
/**
@ -989,16 +1111,15 @@ class MutableBigInteger {
// Use Euclid's algorithm until the numbers are approximately the
// same length, then use the binary GCD algorithm to find the GCD.
MutableBigInteger a = this;
MutableBigInteger q = new MutableBigInteger(),
r = new MutableBigInteger();
MutableBigInteger q = new MutableBigInteger();
while (b.intLen != 0) {
if (Math.abs(a.intLen - b.intLen) < 2)
return a.binaryGCD(b);
a.divide(b, q, r);
MutableBigInteger swapper = a;
a = b; b = r; r = swapper;
MutableBigInteger r = a.divide(b, q);
a = b;
b = r;
}
return a;
}
@ -1069,40 +1190,21 @@ class MutableBigInteger {
if (a==0)
return b;
int x;
int aZeros = 0;
while ((x = a & 0xff) == 0) {
a >>>= 8;
aZeros += 8;
}
int y = BigInteger.trailingZeroTable[x];
aZeros += y;
a >>>= y;
int bZeros = 0;
while ((x = b & 0xff) == 0) {
b >>>= 8;
bZeros += 8;
}
y = BigInteger.trailingZeroTable[x];
bZeros += y;
b >>>= y;
// Right shift a & b till their last bits equal to 1.
int aZeros = Integer.numberOfTrailingZeros(a);
int bZeros = Integer.numberOfTrailingZeros(b);
a >>>= aZeros;
b >>>= bZeros;
int t = (aZeros < bZeros ? aZeros : bZeros);
while (a != b) {
if ((a+0x80000000) > (b+0x80000000)) { // a > b as unsigned
a -= b;
while ((x = a & 0xff) == 0)
a >>>= 8;
a >>>= BigInteger.trailingZeroTable[x];
a >>>= Integer.numberOfTrailingZeros(a);
} else {
b -= a;
while ((x = b & 0xff) == 0)
b >>>= 8;
b >>>= BigInteger.trailingZeroTable[x];
b >>>= Integer.numberOfTrailingZeros(b);
}
}
return a<<t;
@ -1152,8 +1254,7 @@ class MutableBigInteger {
temp1.multiply(y2, temp2);
result.add(temp2);
result.divide(p, temp1, temp2);
return temp2;
return result.divide(p, temp1);
}
/*
@ -1321,40 +1422,45 @@ class MutableBigInteger {
MutableBigInteger a = new MutableBigInteger(this);
MutableBigInteger q = new MutableBigInteger();
MutableBigInteger r = new MutableBigInteger();
MutableBigInteger r = b.divide(a, q);
b.divide(a, q, r);
MutableBigInteger swapper = b; b = r; r = swapper;
MutableBigInteger swapper = b;
// swap b & r
b = r;
r = swapper;
MutableBigInteger t1 = new MutableBigInteger(q);
MutableBigInteger t0 = new MutableBigInteger(1);
MutableBigInteger temp = new MutableBigInteger();
while (!b.isOne()) {
a.divide(b, q, r);
r = a.divide(b, q);
if (r.intLen == 0)
throw new ArithmeticException("BigInteger not invertible.");
swapper = r; r = a; a = swapper;
swapper = r;
a = swapper;
if (q.intLen == 1)
t1.mul(q.value[q.offset], temp);
else
q.multiply(t1, temp);
swapper = q; q = temp; temp = swapper;
swapper = q;
q = temp;
temp = swapper;
t0.add(q);
if (a.isOne())
return t0;
b.divide(a, q, r);
r = b.divide(a, q);
if (r.intLen == 0)
throw new ArithmeticException("BigInteger not invertible.");
swapper = b; b = r; r = swapper;
swapper = b;
b = r;
if (q.intLen == 1)
t0.mul(q.value[q.offset], temp);

View File

@ -129,9 +129,7 @@ class SignedMutableBigInteger extends MutableBigInteger {
* array starting at offset.
*/
public String toString() {
BigInteger b = new BigInteger(this, sign);
return
b.toString();
return this.toBigInteger(sign).toString();
}
}

View File

@ -38,6 +38,31 @@ public class AddTests {
private static Set<RoundingMode> nonExactRoundingModes =
EnumSet.complementOf(EnumSet.of(RoundingMode.UNNECESSARY));
/**
* Test for some simple additions, particularly, it will test
* the overflow case.
*/
private static int simpleTests() {
int failures = 0;
BigDecimal[] bd1 = {
new BigDecimal(new BigInteger("7812404666936930160"), 11),
new BigDecimal(new BigInteger("7812404666936930160"), 12),
new BigDecimal(new BigInteger("7812404666936930160"), 13),
};
BigDecimal bd2 = new BigDecimal(new BigInteger("2790000"), 1);
BigDecimal[] expectedResult = {
new BigDecimal("78403046.66936930160"),
new BigDecimal("8091404.666936930160"),
new BigDecimal("1060240.4666936930160"),
};
for (int i = 0; i < bd1.length; i++) {
if (!bd1[i].add(bd2).equals(expectedResult[i]))
failures++;
}
return failures;
}
/**
* Test for extreme value of scale and rounding precision that
* could cause integer overflow in right-shift-into-sticky-bit

View File

@ -281,6 +281,10 @@ public class DivideTests {
BigDecimal c = new BigDecimal("31425");
BigDecimal c_minus = c.negate();
// Ad hoc tests
BigDecimal d = new BigDecimal(new BigInteger("-37361671119238118911893939591735"), 10);
BigDecimal e = new BigDecimal(new BigInteger("74723342238476237823787879183470"), 15);
BigDecimal[][] testCases = {
{a, b, BigDecimal.valueOf(ROUND_UP, 3), new BigDecimal("3.142")},
{a_minus, b, BigDecimal.valueOf(ROUND_UP, 3), new BigDecimal("-3.142")},
@ -305,6 +309,10 @@ public class DivideTests {
{c, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("3.142")},
{c_minus, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("-3.142")},
{d, e, BigDecimal.valueOf(ROUND_HALF_UP, -5), BigDecimal.valueOf(-1, -5)},
{d, e, BigDecimal.valueOf(ROUND_HALF_DOWN, -5), BigDecimal.valueOf(0, -5)},
{d, e, BigDecimal.valueOf(ROUND_HALF_EVEN, -5), BigDecimal.valueOf(0, -5)},
};
for(BigDecimal tc[] : testCases) {