--- a/jdk/src/share/classes/java/math/BigDecimal.java Thu May 21 23:32:46 2009 -0700
+++ b/jdk/src/share/classes/java/math/BigDecimal.java Sun May 24 16:29:57 2009 -0700
@@ -29,6 +29,9 @@
package java.math;
+import java.util.Arrays;
+import static java.math.BigInteger.LONG_MASK;
+
/**
* Immutable, arbitrary-precision signed decimal numbers. A
* {@code BigDecimal} consists of an arbitrary precision integer
@@ -229,8 +232,8 @@
* @serial
* @see #scale
*/
- private int scale = 0; // Note: this may have any value, so
- // calculations must be done in longs
+ private int scale; // Note: this may have any value, so
+ // calculations must be done in longs
/**
* The number of decimal digits in this BigDecimal, or 0 if the
* number of digits are not known (lookaside information). If
@@ -240,25 +243,25 @@
*
* @since 1.5
*/
- private volatile transient int precision = 0;
+ private transient int precision;
/**
* Used to store the canonical string representation, if computed.
*/
- private volatile transient String stringCache = null;
+ private transient String stringCache;
/**
* Sentinel value for {@link #intCompact} indicating the
* significand information is only available from {@code intVal}.
*/
- private static final long INFLATED = Long.MIN_VALUE;
+ static final long INFLATED = Long.MIN_VALUE;
/**
* If the absolute value of the significand of this BigDecimal is
* less than or equal to {@code Long.MAX_VALUE}, the value can be
* compactly stored in this field and used in computations.
*/
- private transient long intCompact = INFLATED;
+ private transient long intCompact;
// All 18-digit base ten strings fit into a long; not all 19-digit
// strings will
@@ -269,19 +272,47 @@
/* Appease the serialization gods */
private static final long serialVersionUID = 6108874887143696463L;
+ private static final ThreadLocal<StringBuilderHelper>
+ threadLocalStringBuilderHelper = new ThreadLocal<StringBuilderHelper>() {
+ @Override
+ protected StringBuilderHelper initialValue() {
+ return new StringBuilderHelper();
+ }
+ };
+
// Cache of common small BigDecimal values.
private static final BigDecimal zeroThroughTen[] = {
- new BigDecimal(BigInteger.ZERO, 0, 0),
- new BigDecimal(BigInteger.ONE, 1, 0),
- new BigDecimal(BigInteger.valueOf(2), 2, 0),
- new BigDecimal(BigInteger.valueOf(3), 3, 0),
- new BigDecimal(BigInteger.valueOf(4), 4, 0),
- new BigDecimal(BigInteger.valueOf(5), 5, 0),
- new BigDecimal(BigInteger.valueOf(6), 6, 0),
- new BigDecimal(BigInteger.valueOf(7), 7, 0),
- new BigDecimal(BigInteger.valueOf(8), 8, 0),
- new BigDecimal(BigInteger.valueOf(9), 9, 0),
- new BigDecimal(BigInteger.TEN, 10, 0),
+ new BigDecimal(BigInteger.ZERO, 0, 0, 1),
+ new BigDecimal(BigInteger.ONE, 1, 0, 1),
+ new BigDecimal(BigInteger.valueOf(2), 2, 0, 1),
+ new BigDecimal(BigInteger.valueOf(3), 3, 0, 1),
+ new BigDecimal(BigInteger.valueOf(4), 4, 0, 1),
+ new BigDecimal(BigInteger.valueOf(5), 5, 0, 1),
+ new BigDecimal(BigInteger.valueOf(6), 6, 0, 1),
+ new BigDecimal(BigInteger.valueOf(7), 7, 0, 1),
+ new BigDecimal(BigInteger.valueOf(8), 8, 0, 1),
+ new BigDecimal(BigInteger.valueOf(9), 9, 0, 1),
+ new BigDecimal(BigInteger.TEN, 10, 0, 2),
+ };
+
+ // Cache of zero scaled by 0 - 15
+ private static final BigDecimal[] ZERO_SCALED_BY = {
+ zeroThroughTen[0],
+ new BigDecimal(BigInteger.ZERO, 0, 1, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 2, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 3, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 4, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 5, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 6, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 7, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 8, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 9, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 10, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 11, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 12, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 13, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 14, 1),
+ new BigDecimal(BigInteger.ZERO, 0, 15, 1),
};
// Constants
@@ -312,6 +343,18 @@
// Constructors
/**
+ * Trusted package private constructor.
+ * Trusted simply means if val is INFLATED, intVal could not be null and
+ * if intVal is null, val could not be INFLATED.
+ */
+ BigDecimal(BigInteger intVal, long val, int scale, int prec) {
+ this.scale = scale;
+ this.precision = prec;
+ this.intCompact = val;
+ this.intVal = intVal;
+ }
+
+ /**
* Translates a character array representation of a
* {@code BigDecimal} into a {@code BigDecimal}, accepting the
* same sequence of characters as the {@link #BigDecimal(String)}
@@ -331,10 +374,19 @@
* @since 1.5
*/
public BigDecimal(char[] in, int offset, int len) {
+ // protect against huge length.
+ if (offset+len > in.length || offset < 0)
+ throw new NumberFormatException();
// This is the primary string to BigDecimal constructor; all
// incoming strings end up here; it uses explicit (inline)
// parsing for speed and generates at most one intermediate
- // (temporary) object (a char[] array).
+ // (temporary) object (a char[] array) for non-compact case.
+
+ // Use locals for all fields values until completion
+ int prec = 0; // record precision value
+ int scl = 0; // record scale value
+ long rs = 0; // the compact value in long
+ BigInteger rb = null; // the inflated value in BigInteger
// use array bounds checking to handle too-long, len == 0,
// bad offset, etc.
@@ -351,27 +403,62 @@
}
// should now be at numeric part of the significand
- int dotoff = -1; // '.' offset, -1 if none
+ boolean dot = false; // true when there is a '.'
int cfirst = offset; // record start of integer
long exp = 0; // exponent
- if (len > in.length) // protect against huge length
- throw new NumberFormatException();
- char coeff[] = new char[len]; // integer significand array
- char c; // work
+ char c; // current character
+
+ boolean isCompact = (len <= MAX_COMPACT_DIGITS);
+ // integer significand array & idx is the index to it. The array
+ // is ONLY used when we can't use a compact representation.
+ char coeff[] = isCompact ? null : new char[len];
+ int idx = 0;
for (; len > 0; offset++, len--) {
c = in[offset];
+ // have digit
if ((c >= '0' && c <= '9') || Character.isDigit(c)) {
- // have digit
- coeff[precision] = c;
- precision++; // count of digits
+ // First compact case, we need not to preserve the character
+ // and we can just compute the value in place.
+ if (isCompact) {
+ int digit = Character.digit(c, 10);
+ if (digit == 0) {
+ if (prec == 0)
+ prec = 1;
+ else if (rs != 0) {
+ rs *= 10;
+ ++prec;
+ } // else digit is a redundant leading zero
+ } else {
+ if (prec != 1 || rs != 0)
+ ++prec; // prec unchanged if preceded by 0s
+ rs = rs * 10 + digit;
+ }
+ } else { // the unscaled value is likely a BigInteger object.
+ if (c == '0' || Character.digit(c, 10) == 0) {
+ if (prec == 0) {
+ coeff[idx] = c;
+ prec = 1;
+ } else if (idx != 0) {
+ coeff[idx++] = c;
+ ++prec;
+ } // else c must be a redundant leading zero
+ } else {
+ if (prec != 1 || idx != 0)
+ ++prec; // prec unchanged if preceded by 0s
+ coeff[idx++] = c;
+ }
+ }
+ if (dot)
+ ++scl;
continue;
}
+ // have dot
if (c == '.') {
// have dot
- if (dotoff >= 0) // two dots
+ if (dot) // two dots
throw new NumberFormatException();
- dotoff = offset;
+ dot = true;
continue;
}
// exponent expected
@@ -380,10 +467,9 @@
offset++;
c = in[offset];
len--;
- boolean negexp = false;
+ boolean negexp = (c == '-');
// optional sign
- if (c == '-' || c == '+') {
- negexp = (c == '-');
+ if (negexp || c == '+') {
offset++;
c = in[offset];
len--;
@@ -392,9 +478,9 @@
throw new NumberFormatException();
// skip leading zeros in the exponent
while (len > 10 && Character.digit(c, 10) == 0) {
- offset++;
- c = in[offset];
- len--;
+ offset++;
+ c = in[offset];
+ len--;
}
if (len > 10) // too many nonzero exponent digits
throw new NumberFormatException();
@@ -420,55 +506,46 @@
if ((int)exp != exp) // overflow
throw new NumberFormatException();
break; // [saves a test]
- }
+ }
// here when no characters left
- if (precision == 0) // no digits found
+ if (prec == 0) // no digits found
throw new NumberFormatException();
- if (dotoff >= 0) { // had dot; set scale
- scale = precision - (dotoff - cfirst);
- // [cannot overflow]
- }
+ // Adjust scale if exp is not zero.
if (exp != 0) { // had significant exponent
- try {
- scale = checkScale(-exp + scale); // adjust
- } catch (ArithmeticException e) {
+ // Can't call checkScale which relies on proper fields value
+ long adjustedScale = scl - exp;
+ if (adjustedScale > Integer.MAX_VALUE ||
+ adjustedScale < Integer.MIN_VALUE)
throw new NumberFormatException("Scale out of range.");
- }
+ scl = (int)adjustedScale;
}
// Remove leading zeros from precision (digits count)
- int first = 0;
- for (; (coeff[first] == '0' || Character.digit(coeff[first], 10) == 0) &&
- precision > 1;
- first++)
- precision--;
-
- // Set the significand ..
- // Copy significand to exact-sized array, with sign if
- // negative
- // Later use: BigInteger(coeff, first, precision) for
- // both cases, by allowing an extra char at the front of
- // coeff.
- char quick[];
- if (!isneg) {
- quick = new char[precision];
- System.arraycopy(coeff, first, quick, 0, precision);
+ if (isCompact) {
+ rs = isneg ? -rs : rs;
} else {
- quick = new char[precision+1];
- quick[0] = '-';
- System.arraycopy(coeff, first, quick, 1, precision);
+ char quick[];
+ if (!isneg) {
+ quick = (coeff.length != prec) ?
+ Arrays.copyOf(coeff, prec) : coeff;
+ } else {
+ quick = new char[prec + 1];
+ quick[0] = '-';
+ System.arraycopy(coeff, 0, quick, 1, prec);
+ }
+ rb = new BigInteger(quick);
+ rs = compactValFor(rb);
}
- if (precision <= MAX_COMPACT_DIGITS)
- intCompact = Long.parseLong(new String(quick));
- else
- intVal = new BigInteger(quick);
- // System.out.println(" new: " +intVal+" ["+scale+"] "+precision);
} catch (ArrayIndexOutOfBoundsException e) {
throw new NumberFormatException();
} catch (NegativeArraySizeException e) {
throw new NumberFormatException();
}
+ this.scale = scl;
+ this.precision = prec;
+ this.intCompact = rs;
+ this.intVal = (rs != INFLATED) ? null : rb;
}
/**
@@ -754,16 +831,18 @@
}
// Calculate intVal and scale
- intVal = BigInteger.valueOf(sign*significand);
+ long s = sign * significand;
+ BigInteger b;
if (exponent < 0) {
- intVal = intVal.multiply(BigInteger.valueOf(5).pow(-exponent));
+ b = BigInteger.valueOf(5).pow(-exponent).multiply(s);
scale = -exponent;
} else if (exponent > 0) {
- intVal = intVal.multiply(BigInteger.valueOf(2).pow(exponent));
+ b = BigInteger.valueOf(2).pow(exponent).multiply(s);
+ } else {
+ b = BigInteger.valueOf(s);
}
- if (intVal.bitLength() <= MAX_BIGINT_BITS) {
- intCompact = intVal.longValue();
- }
+ intCompact = compactValFor(b);
+ intVal = (intCompact != INFLATED) ? null : b;
}
/**
@@ -798,10 +877,8 @@
* {@code BigDecimal}.
*/
public BigDecimal(BigInteger val) {
- intVal = val;
- if (val.bitLength() <= MAX_BIGINT_BITS) {
- intCompact = val.longValue();
- }
+ intCompact = compactValFor(val);
+ intVal = (intCompact != INFLATED) ? null : val;
}
/**
@@ -817,7 +894,7 @@
* @since 1.5
*/
public BigDecimal(BigInteger val, MathContext mc) {
- intVal = val;
+ this(val);
if (mc.precision > 0)
roundThis(mc);
}
@@ -833,11 +910,8 @@
*/
public BigDecimal(BigInteger unscaledVal, int scale) {
// Negative scales are now allowed
- intVal = unscaledVal;
+ this(unscaledVal);
this.scale = scale;
- if (unscaledVal.bitLength() <= MAX_BIGINT_BITS) {
- intCompact = unscaledVal.longValue();
- }
}
/**
@@ -856,7 +930,7 @@
* @since 1.5
*/
public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) {
- intVal = unscaledVal;
+ this(unscaledVal);
this.scale = scale;
if (mc.precision > 0)
roundThis(mc);
@@ -899,10 +973,8 @@
* @since 1.5
*/
public BigDecimal(long val) {
- if (compactLong(val))
- intCompact = val;
- else
- intVal = BigInteger.valueOf(val);
+ this.intCompact = val;
+ this.intVal = (val == INFLATED) ? BigInteger.valueOf(val) : null;
}
/**
@@ -917,31 +989,11 @@
* @since 1.5
*/
public BigDecimal(long val, MathContext mc) {
- if (compactLong(val))
- intCompact = val;
- else
- intVal = BigInteger.valueOf(val);
+ this(val);
if (mc.precision > 0)
roundThis(mc);
}
- /**
- * Trusted internal constructor
- */
- private BigDecimal(long val, int scale) {
- this.intCompact = val;
- this.scale = scale;
- }
-
- /**
- * Trusted internal constructor
- */
- private BigDecimal(BigInteger intVal, long val, int scale) {
- this.intVal = intVal;
- this.intCompact = val;
- this.scale = scale;
- }
-
// Static Factory Methods
/**
@@ -957,12 +1009,17 @@
* <tt>(unscaledVal × 10<sup>-scale</sup>)</tt>.
*/
public static BigDecimal valueOf(long unscaledVal, int scale) {
- if (scale == 0 && unscaledVal >= 0 && unscaledVal <= 10) {
- return zeroThroughTen[(int)unscaledVal];
+ if (scale == 0)
+ return valueOf(unscaledVal);
+ else if (unscaledVal == 0) {
+ if (scale > 0 && scale < ZERO_SCALED_BY.length)
+ return ZERO_SCALED_BY[scale];
+ else
+ return new BigDecimal(BigInteger.ZERO, 0, scale, 1);
}
- if (compactLong(unscaledVal))
- return new BigDecimal(unscaledVal, scale);
- return new BigDecimal(BigInteger.valueOf(unscaledVal), scale);
+ return new BigDecimal(unscaledVal == INFLATED ?
+ BigInteger.valueOf(unscaledVal) : null,
+ unscaledVal, scale, 0);
}
/**
@@ -976,7 +1033,11 @@
* @return a {@code BigDecimal} whose value is {@code val}.
*/
public static BigDecimal valueOf(long val) {
- return valueOf(val, 0);
+ if (val >= 0 && val < zeroThroughTen.length)
+ return zeroThroughTen[(int)val];
+ else if (val != INFLATED)
+ return new BigDecimal(null, val, 0, 0);
+ return new BigDecimal(BigInteger.valueOf(val), val, 0, 0);
}
/**
@@ -1014,27 +1075,42 @@
* @return {@code this + augend}
*/
public BigDecimal add(BigDecimal augend) {
- BigDecimal arg[] = {this, augend};
- matchScale(arg);
-
- long x = arg[0].intCompact;
- long y = arg[1].intCompact;
+ long xs = this.intCompact;
+ long ys = augend.intCompact;
+ BigInteger fst = (xs != INFLATED) ? null : this.intVal;
+ BigInteger snd = (ys != INFLATED) ? null : augend.intVal;
+ int rscale = this.scale;
- // Might be able to do a more clever check incorporating the
- // inflated check into the overflow computation.
- if (x != INFLATED && y != INFLATED) {
- long sum = x + y;
- /*
- * If the sum is not an overflowed value, continue to use
- * the compact representation. if either of x or y is
- * INFLATED, the sum should also be regarded as an
- * overflow. See "Hacker's Delight" section 2-12 for
- * explanation of the overflow test.
- */
- if ( (((sum ^ x) & (sum ^ y)) >> 63) == 0L ) // not overflowed
- return BigDecimal.valueOf(sum, arg[0].scale);
+ long sdiff = (long)rscale - augend.scale;
+ if (sdiff != 0) {
+ if (sdiff < 0) {
+ int raise = checkScale(-sdiff);
+ rscale = augend.scale;
+ if (xs == INFLATED ||
+ (xs = longMultiplyPowerTen(xs, raise)) == INFLATED)
+ fst = bigMultiplyPowerTen(raise);
+ } else {
+ int raise = augend.checkScale(sdiff);
+ if (ys == INFLATED ||
+ (ys = longMultiplyPowerTen(ys, raise)) == INFLATED)
+ snd = augend.bigMultiplyPowerTen(raise);
+ }
}
- return new BigDecimal(arg[0].inflate().intVal.add(arg[1].inflate().intVal), arg[0].scale);
+ if (xs != INFLATED && ys != INFLATED) {
+ long sum = xs + ys;
+ // See "Hacker's Delight" section 2-12 for explanation of
+ // the overflow test.
+ if ( (((sum ^ xs) & (sum ^ ys))) >= 0L) // not overflowed
+ return new BigDecimal(null, sum, rscale, 0);
+ }
+ if (fst == null)
+ fst = BigInteger.valueOf(xs);
+ if (snd == null)
+ snd = BigInteger.valueOf(ys);
+ BigInteger sum = fst.add(snd);
+ return (fst.signum == snd.signum) ?
+ new BigDecimal(sum, INFLATED, rscale, 0) :
+ new BigDecimal(sum, rscale);
}
/**
@@ -1072,17 +1148,19 @@
// Could use a factory for zero instead of a new object
if (lhsIsZero && augendIsZero)
- return new BigDecimal(BigInteger.ZERO, 0, preferredScale);
+ return new BigDecimal(BigInteger.ZERO, 0, preferredScale, 0);
-
- result = lhsIsZero ? augend.doRound(mc) : lhs.doRound(mc);
+ result = lhsIsZero ? doRound(augend, mc) : doRound(lhs, mc);
if (result.scale() == preferredScale)
return result;
- else if (result.scale() > preferredScale)
- return new BigDecimal(result.intVal, result.intCompact, result.scale).
- stripZerosToMatchScale(preferredScale);
- else { // result.scale < preferredScale
+ else if (result.scale() > preferredScale) {
+ BigDecimal scaledResult =
+ new BigDecimal(result.intVal, result.intCompact,
+ result.scale, 0);
+ scaledResult.stripZerosToMatchScale(preferredScale);
+ return scaledResult;
+ } else { // result.scale < preferredScale
int precisionDiff = mc.precision - result.precision();
int scaleDiff = preferredScale - result.scale();
@@ -1102,8 +1180,9 @@
augend = arg[1];
}
- return new BigDecimal(lhs.inflate().intVal.add(augend.inflate().intVal),
- lhs.scale).doRound(mc);
+ BigDecimal d = new BigDecimal(lhs.inflate().add(augend.inflate()),
+ lhs.scale);
+ return doRound(d, mc);
}
/**
@@ -1181,28 +1260,7 @@
* @return {@code this - subtrahend}
*/
public BigDecimal subtract(BigDecimal subtrahend) {
- BigDecimal arg[] = {this, subtrahend};
- matchScale(arg);
-
- long x = arg[0].intCompact;
- long y = arg[1].intCompact;
-
- // Might be able to do a more clever check incorporating the
- // inflated check into the overflow computation.
- if (x != INFLATED && y != INFLATED) {
- long difference = x - y;
- /*
- * If the difference is not an overflowed value, continue
- * to use the compact representation. if either of x or y
- * is INFLATED, the difference should also be regarded as
- * an overflow. See "Hacker's Delight" section 2-12 for
- * explanation of the overflow test.
- */
- if ( ((x ^ y) & (difference ^ x) ) >> 63 == 0L ) // not overflowed
- return BigDecimal.valueOf(difference, arg[0].scale);
- }
- return new BigDecimal(arg[0].inflate().intVal.subtract(arg[1].inflate().intVal),
- arg[0].scale);
+ return add(subtrahend.negate());
}
/**
@@ -1220,14 +1278,11 @@
* @since 1.5
*/
public BigDecimal subtract(BigDecimal subtrahend, MathContext mc) {
+ BigDecimal nsubtrahend = subtrahend.negate();
if (mc.precision == 0)
- return subtract(subtrahend);
+ return add(nsubtrahend);
// share the special rounding code in add()
- this.inflate();
- subtrahend.inflate();
- BigDecimal rhs = new BigDecimal(subtrahend.intVal.negate(), subtrahend.scale);
- rhs.precision = subtrahend.precision;
- return add(rhs, mc);
+ return add(nsubtrahend, mc);
}
/**
@@ -1241,7 +1296,7 @@
public BigDecimal multiply(BigDecimal multiplicand) {
long x = this.intCompact;
long y = multiplicand.intCompact;
- int productScale = checkScale((long)scale+multiplicand.scale);
+ int productScale = checkScale((long)scale + multiplicand.scale);
// Might be able to do a more clever check incorporating the
// inflated check into the overflow computation.
@@ -1250,16 +1305,26 @@
* If the product is not an overflowed value, continue
* to use the compact representation. if either of x or y
* is INFLATED, the product should also be regarded as
- * an overflow. See "Hacker's Delight" section 2-12 for
- * explanation of the overflow test.
+ * an overflow. Before using the overflow test suggested in
+ * "Hacker's Delight" section 2-12, we perform quick checks
+ * using the precision information to see whether the overflow
+ * would occur since division is expensive on most CPUs.
*/
long product = x * y;
- if ( !(y != 0L && product/y != x) ) // not overflowed
- return BigDecimal.valueOf(product, productScale);
+ int prec = this.precision() + multiplicand.precision();
+ if (prec < 19 || (prec < 21 && (y == 0 || product / y == x)))
+ return new BigDecimal(null, product, productScale, 0);
+ return new BigDecimal(BigInteger.valueOf(x).multiply(y), INFLATED,
+ productScale, 0);
}
-
- BigDecimal result = new BigDecimal(this.inflate().intVal.multiply(multiplicand.inflate().intVal), productScale);
- return result;
+ BigInteger rb;
+ if (x == INFLATED && y == INFLATED)
+ rb = this.intVal.multiply(multiplicand.intVal);
+ else if (x != INFLATED)
+ rb = multiplicand.intVal.multiply(x);
+ else
+ rb = this.intVal.multiply(y);
+ return new BigDecimal(rb, INFLATED, productScale, 0);
}
/**
@@ -1276,8 +1341,7 @@
public BigDecimal multiply(BigDecimal multiplicand, MathContext mc) {
if (mc.precision == 0)
return multiply(multiplicand);
- BigDecimal lhs = this;
- return lhs.inflate().multiply(multiplicand.inflate()).doRound(mc);
+ return doRound(this.multiply(multiplicand), mc);
}
/**
@@ -1311,7 +1375,7 @@
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {
/*
* IMPLEMENTATION NOTE: This method *must* return a new object
- * since dropDigits uses divide to generate a value whose
+ * since divideAndRound uses divide to generate a value whose
* scale is then modified.
*/
if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
@@ -1321,87 +1385,103 @@
* produce correctly scaled quotient).
* Take care to detect out-of-range scales
*/
- BigDecimal dividend;
- if (checkScale((long)scale + divisor.scale) >= this.scale) {
- dividend = this.setScale(scale + divisor.scale);
- } else {
- dividend = this;
- divisor = divisor.setScale(checkScale((long)this.scale - scale));
- }
-
- boolean compact = dividend.intCompact != INFLATED && divisor.intCompact != INFLATED;
- long div = INFLATED;
- long rem = INFLATED;;
- BigInteger q=null, r=null;
+ BigDecimal dividend = this;
+ if (checkScale((long)scale + divisor.scale) > this.scale)
+ dividend = this.setScale(scale + divisor.scale, ROUND_UNNECESSARY);
+ else
+ divisor = divisor.setScale(checkScale((long)this.scale - scale),
+ ROUND_UNNECESSARY);
+ return divideAndRound(dividend.intCompact, dividend.intVal,
+ divisor.intCompact, divisor.intVal,
+ scale, roundingMode, scale);
+ }
- if (compact) {
- div = dividend.intCompact / divisor.intCompact;
- rem = dividend.intCompact % divisor.intCompact;
- } else {
- // Do the division and return result if it's exact.
- BigInteger i[] = dividend.inflate().intVal.divideAndRemainder(divisor.inflate().intVal);
- q = i[0];
- r = i[1];
- }
-
- // Check for exact result
- if (compact) {
- if (rem == 0)
- return new BigDecimal(div, scale);
+ /**
+ * Internally used for division operation. The dividend and divisor are
+ * passed both in {@code long} format and {@code BigInteger} format. The
+ * returned {@code BigDecimal} object is the quotient whose scale is set to
+ * the passed in scale. If the remainder is not zero, it will be rounded
+ * based on the passed in roundingMode. Also, if the remainder is zero and
+ * the last parameter, i.e. preferredScale is NOT equal to scale, the
+ * trailing zeros of the result is stripped to match the preferredScale.
+ */
+ private static BigDecimal divideAndRound(long ldividend, BigInteger bdividend,
+ long ldivisor, BigInteger bdivisor,
+ int scale, int roundingMode,
+ int preferredScale) {
+ boolean isRemainderZero; // record remainder is zero or not
+ int qsign; // quotient sign
+ long q = 0, r = 0; // store quotient & remainder in long
+ MutableBigInteger mq = null; // store quotient
+ MutableBigInteger mr = null; // store remainder
+ MutableBigInteger mdivisor = null;
+ boolean isLongDivision = (ldividend != INFLATED && ldivisor != INFLATED);
+ if (isLongDivision) {
+ q = ldividend / ldivisor;
+ if (roundingMode == ROUND_DOWN && scale == preferredScale)
+ return new BigDecimal(null, q, scale, 0);
+ r = ldividend % ldivisor;
+ isRemainderZero = (r == 0);
+ qsign = ((ldividend < 0) == (ldivisor < 0)) ? 1 : -1;
} else {
- if (r.signum() == 0)
- return new BigDecimal(q, scale);
+ if (bdividend == null)
+ bdividend = BigInteger.valueOf(ldividend);
+ // Descend into mutables for faster remainder checks
+ MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
+ mq = new MutableBigInteger();
+ if (ldivisor != INFLATED) {
+ r = mdividend.divide(ldivisor, mq);
+ isRemainderZero = (r == 0);
+ qsign = (ldivisor < 0) ? -bdividend.signum : bdividend.signum;
+ } else {
+ mdivisor = new MutableBigInteger(bdivisor.mag);
+ mr = mdividend.divide(mdivisor, mq);
+ isRemainderZero = mr.isZero();
+ qsign = (bdividend.signum != bdivisor.signum) ? -1 : 1;
+ }
}
-
- if (roundingMode == ROUND_UNNECESSARY) // Rounding prohibited
- throw new ArithmeticException("Rounding necessary");
-
- /* Round as appropriate */
- int signum = dividend.signum() * divisor.signum(); // Sign of result
- boolean increment;
- if (roundingMode == ROUND_UP) { // Away from zero
- increment = true;
- } else if (roundingMode == ROUND_DOWN) { // Towards zero
- increment = false;
- } else if (roundingMode == ROUND_CEILING) { // Towards +infinity
- increment = (signum > 0);
- } else if (roundingMode == ROUND_FLOOR) { // Towards -infinity
- increment = (signum < 0);
- } else { // Remaining modes based on nearest-neighbor determination
+ boolean increment = false;
+ if (!isRemainderZero) {
int cmpFracHalf;
- if (compact) {
- cmpFracHalf = longCompareTo(Math.abs(2*rem), Math.abs(divisor.intCompact));
+ /* Round as appropriate */
+ if (roundingMode == ROUND_UNNECESSARY) { // Rounding prohibited
+ throw new ArithmeticException("Rounding necessary");
+ } else if (roundingMode == ROUND_UP) { // Away from zero
+ increment = true;
+ } else if (roundingMode == ROUND_DOWN) { // Towards zero
+ increment = false;
+ } else if (roundingMode == ROUND_CEILING) { // Towards +infinity
+ increment = (qsign > 0);
+ } else if (roundingMode == ROUND_FLOOR) { // Towards -infinity
+ increment = (qsign < 0);
} else {
- // add(r) here is faster than multiply(2) or shiftLeft(1)
- cmpFracHalf= r.add(r).abs().compareTo(divisor.intVal.abs());
- }
- if (cmpFracHalf < 0) { // We're closer to higher digit
- increment = false;
- } else if (cmpFracHalf > 0) { // We're closer to lower digit
- increment = true;
- } else { // We're dead-center
- if (roundingMode == ROUND_HALF_UP)
+ if (isLongDivision || ldivisor != INFLATED)
+ cmpFracHalf = longCompareMagnitude(2 * r, ldivisor);
+ else
+ cmpFracHalf = mr.compareHalf(mdivisor);
+ if (cmpFracHalf < 0)
+ increment = false; // We're closer to higher digit
+ else if (cmpFracHalf > 0) // We're closer to lower digit
+ increment = true;
+ else if (roundingMode == ROUND_HALF_UP)
increment = true;
else if (roundingMode == ROUND_HALF_DOWN)
increment = false;
- else { // roundingMode == ROUND_HALF_EVEN
- if (compact)
- increment = (div & 1L) != 0L;
- else
- increment = q.testBit(0); // true iff q is odd
- }
+ else // roundingMode == ROUND_HALF_EVEN, true iff quotient is odd
+ increment = isLongDivision ? (q & 1L) != 0L : mq.isOdd();
}
}
-
- if (compact) {
+ BigDecimal res;
+ if (isLongDivision)
+ res = new BigDecimal(null, (increment ? q + qsign : q), scale, 0);
+ else {
if (increment)
- div += signum; // guaranteed not to overflow
- return new BigDecimal(div, scale);
- } else {
- return (increment
- ? new BigDecimal(q.add(BigInteger.valueOf(signum)), scale)
- : new BigDecimal(q, scale));
+ mq.add(MutableBigInteger.ONE);
+ res = mq.toBigDecimal(qsign, scale);
}
+ if (isRemainderZero && preferredScale != scale)
+ res.stripZerosToMatchScale(preferredScale);
+ return res;
}
/**
@@ -1499,10 +1579,12 @@
}
// Calculate preferred scale
- int preferredScale = (int)Math.max(Math.min((long)this.scale() - divisor.scale(),
- Integer.MAX_VALUE), Integer.MIN_VALUE);
+ int preferredScale = saturateLong((long)this.scale - divisor.scale);
if (this.signum() == 0) // 0/y
- return new BigDecimal(0, preferredScale);
+ return (preferredScale >= 0 &&
+ preferredScale < ZERO_SCALED_BY.length) ?
+ ZERO_SCALED_BY[preferredScale] :
+ new BigDecimal(null, 0, preferredScale, 1);
else {
this.inflate();
divisor.inflate();
@@ -1534,7 +1616,7 @@
// limit, we can add zeros too.
if (preferredScale > quotientScale)
- return quotient.setScale(preferredScale);
+ return quotient.setScale(preferredScale, ROUND_UNNECESSARY);
return quotient;
}
@@ -1554,14 +1636,12 @@
* @since 1.5
*/
public BigDecimal divide(BigDecimal divisor, MathContext mc) {
- if (mc.precision == 0)
+ int mcp = mc.precision;
+ if (mcp == 0)
return divide(divisor);
- BigDecimal lhs = this.inflate(); // left-hand-side
- BigDecimal rhs = divisor.inflate(); // right-hand-side
- BigDecimal result; // work
- long preferredScale = (long)lhs.scale() - rhs.scale();
-
+ BigDecimal dividend = this;
+ long preferredScale = (long)dividend.scale - divisor.scale;
// Now calculate the answer. We use the existing
// divide-and-round method, but as this rounds to scale we have
// to normalize the values here to achieve the desired result.
@@ -1574,54 +1654,43 @@
// the right number of digits (except in the case of a result of
// 1.000... which can arise when x=y, or when rounding overflows
// The 1.000... case will reduce properly to 1.
- if (rhs.signum() == 0) { // x/0
- if (lhs.signum() == 0) // 0/0
+ if (divisor.signum() == 0) { // x/0
+ if (dividend.signum() == 0) // 0/0
throw new ArithmeticException("Division undefined"); // NaN
throw new ArithmeticException("Division by zero");
}
- if (lhs.signum() == 0) // 0/y
- return new BigDecimal(BigInteger.ZERO,
- (int)Math.max(Math.min(preferredScale,
- Integer.MAX_VALUE),
- Integer.MIN_VALUE));
+ if (dividend.signum() == 0) // 0/y
+ return new BigDecimal(BigInteger.ZERO, 0,
+ saturateLong(preferredScale), 1);
+
+ // Normalize dividend & divisor so that both fall into [0.1, 0.999...]
+ int xscale = dividend.precision();
+ int yscale = divisor.precision();
+ dividend = new BigDecimal(dividend.intVal, dividend.intCompact,
+ xscale, xscale);
+ divisor = new BigDecimal(divisor.intVal, divisor.intCompact,
+ yscale, yscale);
+ if (dividend.compareMagnitude(divisor) > 0) // satisfy constraint (b)
+ yscale = divisor.scale -= 1; // [that is, divisor *= 10]
- BigDecimal xprime = new BigDecimal(lhs.intVal.abs(), lhs.precision());
- BigDecimal yprime = new BigDecimal(rhs.intVal.abs(), rhs.precision());
- // xprime and yprime are now both in range 0.1 through 0.999...
- if (mc.roundingMode == RoundingMode.CEILING ||
- mc.roundingMode == RoundingMode.FLOOR) {
- // The floor (round toward negative infinity) and ceil
- // (round toward positive infinity) rounding modes are not
- // invariant under a sign flip. If xprime/yprime has a
- // different sign than lhs/rhs, the rounding mode must be
- // changed.
- if ((xprime.signum() != lhs.signum()) ^
- (yprime.signum() != rhs.signum())) {
- mc = new MathContext(mc.precision,
- (mc.roundingMode==RoundingMode.CEILING)?
- RoundingMode.FLOOR:RoundingMode.CEILING);
- }
- }
+ // In order to find out whether the divide generates the exact result,
+ // we avoid calling the above divide method. 'quotient' holds the
+ // return BigDecimal object whose scale will be set to 'scl'.
+ BigDecimal quotient;
+ int scl = checkScale(preferredScale + yscale - xscale + mcp);
+ if (checkScale((long)mcp + yscale) > xscale)
+ dividend = dividend.setScale(mcp + yscale, ROUND_UNNECESSARY);
+ else
+ divisor = divisor.setScale(checkScale((long)xscale - mcp),
+ ROUND_UNNECESSARY);
+ quotient = divideAndRound(dividend.intCompact, dividend.intVal,
+ divisor.intCompact, divisor.intVal,
+ scl, mc.roundingMode.oldMode,
+ checkScale(preferredScale));
+ // doRound, here, only affects 1000000000 case.
+ quotient = doRound(quotient, mc);
- if (xprime.compareTo(yprime) > 0) // satisfy constraint (b)
- yprime.scale -= 1; // [that is, yprime *= 10]
- result = xprime.divide(yprime, mc.precision, mc.roundingMode.oldMode);
- // correct the scale of the result...
- result.scale = checkScale((long)yprime.scale - xprime.scale
- - (rhs.scale - lhs.scale) + mc.precision);
- // apply the sign
- if (lhs.signum() != rhs.signum())
- result = result.negate();
- // doRound, here, only affects 1000000000 case.
- result = result.doRound(mc);
-
- if (result.multiply(divisor).compareTo(this) == 0) {
- // Apply preferred scale rules for exact quotients
- return result.stripZerosToMatchScale(preferredScale);
- }
- else {
- return result;
- }
+ return quotient;
}
/**
@@ -1637,17 +1706,14 @@
*/
public BigDecimal divideToIntegralValue(BigDecimal divisor) {
// Calculate preferred scale
- int preferredScale = (int)Math.max(Math.min((long)this.scale() - divisor.scale(),
- Integer.MAX_VALUE), Integer.MIN_VALUE);
- this.inflate();
- divisor.inflate();
- if (this.abs().compareTo(divisor.abs()) < 0) {
+ int preferredScale = saturateLong((long)this.scale - divisor.scale);
+ if (this.compareMagnitude(divisor) < 0) {
// much faster when this << divisor
return BigDecimal.valueOf(0, preferredScale);
}
if(this.signum() == 0 && divisor.signum() != 0)
- return this.setScale(preferredScale);
+ return this.setScale(preferredScale, ROUND_UNNECESSARY);
// Perform a divide with enough digits to round to a correct
// integer value; then remove any fractional digits
@@ -1656,19 +1722,17 @@
(long)Math.ceil(10.0*divisor.precision()/3.0) +
Math.abs((long)this.scale() - divisor.scale()) + 2,
Integer.MAX_VALUE);
-
BigDecimal quotient = this.divide(divisor, new MathContext(maxDigits,
RoundingMode.DOWN));
if (quotient.scale > 0) {
- quotient = quotient.setScale(0, RoundingMode.DOWN).
- stripZerosToMatchScale(preferredScale);
+ quotient = quotient.setScale(0, RoundingMode.DOWN);
+ quotient.stripZerosToMatchScale(preferredScale);
}
if (quotient.scale < preferredScale) {
// pad with zeros if necessary
- quotient = quotient.setScale(preferredScale);
+ quotient = quotient.setScale(preferredScale, ROUND_UNNECESSARY);
}
-
return quotient;
}
@@ -1694,12 +1758,11 @@
*/
public BigDecimal divideToIntegralValue(BigDecimal divisor, MathContext mc) {
if (mc.precision == 0 || // exact result
- (this.abs().compareTo(divisor.abs()) < 0) ) // zero result
+ (this.compareMagnitude(divisor) < 0) ) // zero result
return divideToIntegralValue(divisor);
// Calculate preferred scale
- int preferredScale = (int)Math.max(Math.min((long)this.scale() - divisor.scale(),
- Integer.MAX_VALUE), Integer.MIN_VALUE);
+ int preferredScale = saturateLong((long)this.scale - divisor.scale);
/*
* Perform a normal divide to mc.precision digits. If the
@@ -1708,9 +1771,8 @@
* digits. Next, remove any fractional digits from the
* quotient and adjust the scale to the preferred value.
*/
- BigDecimal result = this.divide(divisor, new MathContext(mc.precision,
- RoundingMode.DOWN));
- int resultScale = result.scale();
+ BigDecimal result = this.
+ divide(divisor, new MathContext(mc.precision, RoundingMode.DOWN));
if (result.scale() < 0) {
/*
@@ -1721,7 +1783,7 @@
BigDecimal product = result.multiply(divisor);
// If the quotient is the full integer value,
// |dividend-product| < |divisor|.
- if (this.subtract(product).abs().compareTo(divisor.abs()) >= 0) {
+ if (this.subtract(product).compareMagnitude(divisor) >= 0) {
throw new ArithmeticException("Division impossible");
}
} else if (result.scale() > 0) {
@@ -1736,11 +1798,13 @@
int precisionDiff;
if ((preferredScale > result.scale()) &&
- (precisionDiff = mc.precision - result.precision()) > 0 ) {
+ (precisionDiff = mc.precision - result.precision()) > 0) {
return result.setScale(result.scale() +
Math.min(precisionDiff, preferredScale - result.scale) );
- } else
- return result.stripZerosToMatchScale(preferredScale);
+ } else {
+ result.stripZerosToMatchScale(preferredScale);
+ return result;
+ }
}
/**
@@ -1949,7 +2013,7 @@
int mag = Math.abs(n); // magnitude of n
if (mc.precision > 0) {
- int elength = intLength(mag); // length of n in digits
+ int elength = longDigitLength(mag); // length of n in digits
if (elength > mc.precision) // X3.274 rule
throw new ArithmeticException("Invalid operation");
workmc = new MathContext(mc.precision + elength + 1,
@@ -1974,7 +2038,7 @@
if (n<0) // [hence mc.precision>0]
acc=ONE.divide(acc, workmc);
// round to final precision and strip zeros
- return acc.doRound(mc);
+ return doRound(acc, mc);
}
/**
@@ -2068,7 +2132,7 @@
public BigDecimal plus(MathContext mc) {
if (mc.precision == 0) // no rounding please
return this;
- return this.doRound(mc);
+ return doRound(this, mc);
}
/**
@@ -2109,7 +2173,11 @@
public int precision() {
int result = precision;
if (result == 0) {
- result = digitLength();
+ long s = intCompact;
+ if (s != INFLATED)
+ result = longDigitLength(s);
+ else
+ result = bigDigitLength(inflate());
precision = result;
}
return result;
@@ -2125,7 +2193,7 @@
* @since 1.2
*/
public BigInteger unscaledValue() {
- return this.inflate().intVal;
+ return this.inflate();
}
// Rounding Modes
@@ -2302,30 +2370,34 @@
if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
throw new IllegalArgumentException("Invalid rounding mode");
- if (newScale == this.scale) // easy case
+ int oldScale = this.scale;
+ if (newScale == oldScale) // easy case
return this;
if (this.signum() == 0) // zero can have any scale
return BigDecimal.valueOf(0, newScale);
- if (newScale > this.scale) {
- // [we can use checkScale to assure multiplier is valid]
- int raise = checkScale((long)newScale - this.scale);
- if (intCompact != INFLATED) {
- long scaledResult = longTenToThe(intCompact, raise);
- if (scaledResult != INFLATED)
- return BigDecimal.valueOf(scaledResult, newScale);
- this.inflate();
- }
-
- BigDecimal result = new BigDecimal(intVal.multiply(tenToThe(raise)),
- newScale);
- if (this.precision > 0)
- result.precision = this.precision + newScale - this.scale;
- return result;
+ long rs = this.intCompact;
+ if (newScale > oldScale) {
+ int raise = checkScale((long)newScale - oldScale);
+ BigInteger rb = null;
+ if (rs == INFLATED ||
+ (rs = longMultiplyPowerTen(rs, raise)) == INFLATED)
+ rb = bigMultiplyPowerTen(raise);
+ return new BigDecimal(rb, rs, newScale,
+ (precision > 0) ? precision + raise : 0);
+ } else {
+ // newScale < oldScale -- drop some digits
+ // Can't predict the precision due to the effect of rounding.
+ int drop = checkScale((long)oldScale - newScale);
+ if (drop < LONG_TEN_POWERS_TABLE.length)
+ return divideAndRound(rs, this.intVal,
+ LONG_TEN_POWERS_TABLE[drop], null,
+ newScale, roundingMode, newScale);
+ else
+ return divideAndRound(rs, this.intVal,
+ INFLATED, bigTenToThe(drop),
+ newScale, roundingMode, newScale);
}
- // scale < this.scale
- // we cannot perfectly predict the precision after rounding
- return divide(ONE, newScale, roundingMode);
}
/**
@@ -2388,12 +2460,8 @@
public BigDecimal movePointLeft(int n) {
// Cannot use movePointRight(-n) in case of n==Integer.MIN_VALUE
int newScale = checkScale((long)scale + n);
- BigDecimal num;
- if (intCompact != INFLATED)
- num = BigDecimal.valueOf(intCompact, newScale);
- else
- num = new BigDecimal(intVal, newScale);
- return (num.scale<0 ? num.setScale(0) : num);
+ BigDecimal num = new BigDecimal(intVal, intCompact, newScale, 0);
+ return num.scale < 0 ? num.setScale(0, ROUND_UNNECESSARY) : num;
}
/**
@@ -2414,12 +2482,8 @@
public BigDecimal movePointRight(int n) {
// Cannot use movePointLeft(-n) in case of n==Integer.MIN_VALUE
int newScale = checkScale((long)scale - n);
- BigDecimal num;
- if (intCompact != INFLATED)
- num = BigDecimal.valueOf(intCompact, newScale);
- else
- num = new BigDecimal(intVal, newScale);
- return (num.scale<0 ? num.setScale(0) : num);
+ BigDecimal num = new BigDecimal(intVal, intCompact, newScale, 0);
+ return num.scale < 0 ? num.setScale(0, ROUND_UNNECESSARY) : num;
}
/**
@@ -2433,10 +2497,8 @@
* @since 1.5
*/
public BigDecimal scaleByPowerOfTen(int n) {
- this.inflate();
- BigDecimal num = new BigDecimal(intVal, checkScale((long)scale - n));
- num.precision = precision;
- return num;
+ return new BigDecimal(intVal, intCompact,
+ checkScale((long)scale - n), precision);
}
/**
@@ -2454,7 +2516,9 @@
*/
public BigDecimal stripTrailingZeros() {
this.inflate();
- return (new BigDecimal(intVal, scale)).stripZerosToMatchScale(Long.MIN_VALUE);
+ BigDecimal result = new BigDecimal(intVal, scale);
+ result.stripZerosToMatchScale(Long.MIN_VALUE);
+ return result;
}
// Comparison Operations
@@ -2477,32 +2541,67 @@
* less than, equal to, or greater than {@code val}.
*/
public int compareTo(BigDecimal val) {
- if (this.scale == val.scale &&
- this.intCompact != INFLATED &&
- val.intCompact != INFLATED)
- return longCompareTo(this.intCompact, val.intCompact);
+ // Quick path for equal scale and non-inflated case.
+ if (scale == val.scale) {
+ long xs = intCompact;
+ long ys = val.intCompact;
+ if (xs != INFLATED && ys != INFLATED)
+ return xs != ys ? ((xs > ys) ? 1 : -1) : 0;
+ }
+ int xsign = this.signum();
+ int ysign = val.signum();
+ if (xsign != ysign)
+ return (xsign > ysign) ? 1 : -1;
+ if (xsign == 0)
+ return 0;
+ int cmp = compareMagnitude(val);
+ return (xsign > 0) ? cmp : -cmp;
+ }
- // Optimization: would run fine without the next three lines
- int sigDiff = signum() - val.signum();
- if (sigDiff != 0)
- return (sigDiff > 0 ? 1 : -1);
+ /**
+ * Version of compareTo that ignores sign.
+ */
+ private int compareMagnitude(BigDecimal val) {
+ // Match scales, avoid unnecessary inflation
+ long ys = val.intCompact;
+ long xs = this.intCompact;
+ if (xs == 0)
+ return (ys == 0) ? 0 : -1;
+ if (ys == 0)
+ return 1;
- // If the (adjusted) exponents are different we do not need to
- // expensively match scales and compare the significands
- int aethis = this.precision() - this.scale; // [-1]
- int aeval = val.precision() - val.scale; // [-1]
- if (aethis < aeval)
- return -this.signum();
- else if (aethis > aeval)
- return this.signum();
-
- // Scale and compare intVals
- BigDecimal arg[] = {this, val};
- matchScale(arg);
- if (arg[0].intCompact != INFLATED &&
- arg[1].intCompact != INFLATED)
- return longCompareTo(arg[0].intCompact, arg[1].intCompact);
- return arg[0].inflate().intVal.compareTo(arg[1].inflate().intVal);
+ int sdiff = this.scale - val.scale;
+ if (sdiff != 0) {
+ // Avoid matching scales if the (adjusted) exponents differ
+ int xae = this.precision() - this.scale; // [-1]
+ int yae = val.precision() - val.scale; // [-1]
+ if (xae < yae)
+ return -1;
+ if (xae > yae)
+ return 1;
+ BigInteger rb = null;
+ if (sdiff < 0) {
+ if ( (xs == INFLATED ||
+ (xs = longMultiplyPowerTen(xs, -sdiff)) == INFLATED) &&
+ ys == INFLATED) {
+ rb = bigMultiplyPowerTen(-sdiff);
+ return rb.compareMagnitude(val.intVal);
+ }
+ } else { // sdiff > 0
+ if ( (ys == INFLATED ||
+ (ys = longMultiplyPowerTen(ys, sdiff)) == INFLATED) &&
+ xs == INFLATED) {
+ rb = val.bigMultiplyPowerTen(sdiff);
+ return this.intVal.compareMagnitude(rb);
+ }
+ }
+ }
+ if (xs != INFLATED)
+ return (ys != INFLATED) ? longCompareMagnitude(xs, ys) : -1;
+ else if (ys != INFLATED)
+ return 1;
+ else
+ return this.intVal.compareMagnitude(val.intVal);
}
/**
@@ -2521,15 +2620,25 @@
* @see #compareTo(java.math.BigDecimal)
* @see #hashCode
*/
+ @Override
public boolean equals(Object x) {
if (!(x instanceof BigDecimal))
return false;
BigDecimal xDec = (BigDecimal) x;
+ if (x == this)
+ return true;
if (scale != xDec.scale)
return false;
- if (this.intCompact != INFLATED && xDec.intCompact != INFLATED)
- return this.intCompact == xDec.intCompact;
- return this.inflate().intVal.equals(xDec.inflate().intVal);
+ long s = this.intCompact;
+ long xs = xDec.intCompact;
+ if (s != INFLATED) {
+ if (xs == INFLATED)
+ xs = compactValFor(xDec.intVal);
+ return xs == s;
+ } else if (xs != INFLATED)
+ return xs == compactValFor(this.intVal);
+
+ return this.inflate().equals(xDec.inflate());
}
/**
@@ -2572,11 +2681,12 @@
* @return hash code for this {@code BigDecimal}.
* @see #equals(Object)
*/
+ @Override
public int hashCode() {
if (intCompact != INFLATED) {
- long val2 = (intCompact < 0)?-intCompact:intCompact;
+ long val2 = (intCompact < 0)? -intCompact : intCompact;
int temp = (int)( ((int)(val2 >>> 32)) * 31 +
- (val2 & 0xffffffffL));
+ (val2 & LONG_MASK));
return 31*((intCompact < 0) ?-temp:temp) + scale;
} else
return 31*intVal.hashCode() + scale;
@@ -2683,10 +2793,12 @@
* @see Character#forDigit
* @see #BigDecimal(java.lang.String)
*/
+ @Override
public String toString() {
- if (stringCache == null)
- stringCache = layoutChars(true);
- return stringCache;
+ String sc = stringCache;
+ if (sc == null)
+ stringCache = sc = layoutChars(true);
+ return sc;
}
/**
@@ -2802,7 +2914,7 @@
*/
public BigInteger toBigInteger() {
// force to an integer, quietly
- return this.setScale(0, ROUND_DOWN).inflate().intVal;
+ return this.setScale(0, ROUND_DOWN).inflate();
}
/**
@@ -2817,7 +2929,7 @@
*/
public BigInteger toBigIntegerExact() {
// round to an integer, with Exception if decimal part non-0
- return this.setScale(0, ROUND_UNNECESSARY).inflate().intVal;
+ return this.setScale(0, ROUND_UNNECESSARY).inflate();
}
/**
@@ -2868,10 +2980,10 @@
if ((this.precision() - this.scale) <= 0)
throw new ArithmeticException("Rounding necessary");
// round to an integer, with Exception if decimal part non-0
- BigDecimal num = this.setScale(0, ROUND_UNNECESSARY).inflate();
+ BigDecimal num = this.setScale(0, ROUND_UNNECESSARY);
if (num.precision() >= 19) // need to check carefully
LongOverflow.check(num);
- return num.intVal.longValue();
+ return num.inflate().longValue();
}
private static class LongOverflow {
@@ -2882,6 +2994,7 @@
private static final BigInteger LONGMAX = BigInteger.valueOf(Long.MAX_VALUE);
public static void check(BigDecimal num) {
+ num.inflate();
if ((num.intVal.compareTo(LONGMIN) < 0) ||
(num.intVal.compareTo(LONGMAX) > 0))
throw new java.lang.ArithmeticException("Overflow");
@@ -3037,7 +3150,105 @@
return BigDecimal.valueOf(1, this.scale());
}
- // Private "Helper" Methods
+
+ // Private class to build a string representation for BigDecimal object.
+ // "StringBuilderHelper" is constructed as a thread local variable so it is
+ // thread safe. The StringBuilder field acts as a buffer to hold the temporary
+ // representation of BigDecimal. The cmpCharArray holds all the characters for
+ // the compact representation of BigDecimal (except for '-' sign' if it is
+ // negative) if its intCompact field is not INFLATED. It is shared by all
+ // calls to toString() and its variants in that particular thread.
+ static class StringBuilderHelper {
+ final StringBuilder sb; // Placeholder for BigDecimal string
+ final char[] cmpCharArray; // character array to place the intCompact
+
+ StringBuilderHelper() {
+ sb = new StringBuilder();
+ // All non negative longs can be made to fit into 19 character array.
+ cmpCharArray = new char[19];
+ }
+
+ // Accessors.
+ StringBuilder getStringBuilder() {
+ sb.setLength(0);
+ return sb;
+ }
+
+ char[] getCompactCharArray() {
+ return cmpCharArray;
+ }
+
+ /**
+ * Places characters representing the intCompact in {@code long} into
+ * cmpCharArray and returns the offset to the array where the
+ * representation starts.
+ *
+ * @param intCompact the number to put into the cmpCharArray.
+ * @return offset to the array where the representation starts.
+ * Note: intCompact must be greater or equal to zero.
+ */
+ int putIntCompact(long intCompact) {
+ assert intCompact >= 0;
+
+ long q;
+ int r;
+ // since we start from the least significant digit, charPos points to
+ // the last character in cmpCharArray.
+ int charPos = cmpCharArray.length;
+
+ // Get 2 digits/iteration using longs until quotient fits into an int
+ while (intCompact > Integer.MAX_VALUE) {
+ q = intCompact / 100;
+ r = (int)(intCompact - q * 100);
+ intCompact = q;
+ cmpCharArray[--charPos] = DIGIT_ONES[r];
+ cmpCharArray[--charPos] = DIGIT_TENS[r];
+ }
+
+ // Get 2 digits/iteration using ints when i2 >= 100
+ int q2;
+ int i2 = (int)intCompact;
+ while (i2 >= 100) {
+ q2 = i2 / 100;
+ r = i2 - q2 * 100;
+ i2 = q2;
+ cmpCharArray[--charPos] = DIGIT_ONES[r];
+ cmpCharArray[--charPos] = DIGIT_TENS[r];
+ }
+
+ cmpCharArray[--charPos] = DIGIT_ONES[i2];
+ if (i2 >= 10)
+ cmpCharArray[--charPos] = DIGIT_TENS[i2];
+
+ return charPos;
+ }
+
+ final static char[] DIGIT_TENS = {
+ '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
+ '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
+ '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
+ '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
+ '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
+ '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
+ '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
+ '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
+ '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
+ '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
+ };
+
+ final static char[] DIGIT_ONES = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ };
+ }
/**
* Lay out this {@code BigDecimal} into a {@code char[]} array.
@@ -3054,41 +3265,47 @@
Long.toString(intCompact):
intVal.toString();
+ StringBuilderHelper sbHelper = threadLocalStringBuilderHelper.get();
+ char[] coeff;
+ int offset; // offset is the starting index for coeff array
// Get the significand as an absolute value
- char coeff[];
- if (intCompact != INFLATED)
- coeff = Long.toString(Math.abs(intCompact)).toCharArray();
- else
- coeff = intVal.abs().toString().toCharArray();
+ if (intCompact != INFLATED) {
+ offset = sbHelper.putIntCompact(Math.abs(intCompact));
+ coeff = sbHelper.getCompactCharArray();
+ } else {
+ offset = 0;
+ coeff = intVal.abs().toString().toCharArray();
+ }
// Construct a buffer, with sufficient capacity for all cases.
// If E-notation is needed, length will be: +1 if negative, +1
// if '.' needed, +2 for "E+", + up to 10 for adjusted exponent.
// Otherwise it could have +1 if negative, plus leading "0.00000"
- StringBuilder buf=new StringBuilder(coeff.length+14);
+ StringBuilder buf = sbHelper.getStringBuilder();
if (signum() < 0) // prefix '-' if negative
buf.append('-');
- long adjusted = -(long)scale + (coeff.length-1);
+ int coeffLen = coeff.length - offset;
+ long adjusted = -(long)scale + (coeffLen -1);
if ((scale >= 0) && (adjusted >= -6)) { // plain number
- int pad = scale - coeff.length; // count of padding zeros
- if (pad >= 0) { // 0.xxx form
+ int pad = scale - coeffLen; // count of padding zeros
+ if (pad >= 0) { // 0.xxx form
buf.append('0');
buf.append('.');
for (; pad>0; pad--) {
buf.append('0');
}
- buf.append(coeff);
+ buf.append(coeff, offset, coeffLen);
} else { // xx.xx form
- buf.append(coeff, 0, -pad);
+ buf.append(coeff, offset, -pad);
buf.append('.');
- buf.append(coeff, -pad, scale);
+ buf.append(coeff, -pad + offset, scale);
}
} else { // E-notation is needed
if (sci) { // Scientific notation
- buf.append(coeff[0]); // first character
- if (coeff.length > 1) { // more to come
+ buf.append(coeff[offset]); // first character
+ if (coeffLen > 1) { // more to come
buf.append('.');
- buf.append(coeff, 1, coeff.length-1);
+ buf.append(coeff, offset + 1, coeffLen - 1);
}
} else { // Engineering notation
int sig = (int)(adjusted % 3);
@@ -3112,15 +3329,15 @@
default:
throw new AssertionError("Unexpected sig value " + sig);
}
- } else if (sig >= coeff.length) { // significand all in integer
- buf.append(coeff, 0, coeff.length);
+ } else if (sig >= coeffLen) { // significand all in integer
+ buf.append(coeff, offset, coeffLen);
// may need some zeros, too
- for (int i = sig - coeff.length; i > 0; i--)
+ for (int i = sig - coeffLen; i > 0; i--)
buf.append('0');
} else { // xx.xxE form
- buf.append(coeff, 0, sig);
+ buf.append(coeff, offset, sig);
buf.append('.');
- buf.append(coeff, sig, coeff.length-sig);
+ buf.append(coeff, offset + sig, coeffLen - sig);
}
}
if (adjusted != 0) { // [!sci could have made 0]
@@ -3139,9 +3356,17 @@
* @param n the power of ten to be returned (>=0)
* @return a {@code BigInteger} with the value (10<sup>n</sup>)
*/
- private static BigInteger tenToThe(int n) {
- if (n < TENPOWERS.length) // use value from constant array
- return TENPOWERS[n];
+ private static BigInteger bigTenToThe(int n) {
+ if (n < 0)
+ return BigInteger.ZERO;
+
+ if (n < BIG_TEN_POWERS_TABLE_MAX) {
+ BigInteger[] pows = BIG_TEN_POWERS_TABLE;
+ if (n < pows.length)
+ return pows[n];
+ else
+ return expandBigIntegerTenPowers(n);
+ }
// BigInteger.pow is slow, so make 10**n by constructing a
// BigInteger from a character string (still not very fast)
char tenpow[] = new char[n + 1];
@@ -3150,58 +3375,145 @@
tenpow[i] = '0';
return new BigInteger(tenpow);
}
- private static BigInteger TENPOWERS[] = {BigInteger.ONE,
+
+ /**
+ * Expand the BIG_TEN_POWERS_TABLE array to contain at least 10**n.
+ *
+ * @param n the power of ten to be returned (>=0)
+ * @return a {@code BigDecimal} with the value (10<sup>n</sup>) and
+ * in the meantime, the BIG_TEN_POWERS_TABLE array gets
+ * expanded to the size greater than n.
+ */
+ private static BigInteger expandBigIntegerTenPowers(int n) {
+ synchronized(BigDecimal.class) {
+ BigInteger[] pows = BIG_TEN_POWERS_TABLE;
+ int curLen = pows.length;
+ // The following comparison and the above synchronized statement is
+ // to prevent multiple threads from expanding the same array.
+ if (curLen <= n) {
+ int newLen = curLen << 1;
+ while (newLen <= n)
+ newLen <<= 1;
+ pows = Arrays.copyOf(pows, newLen);
+ for (int i = curLen; i < newLen; i++)
+ pows[i] = pows[i - 1].multiply(BigInteger.TEN);
+ // Based on the following facts:
+ // 1. pows is a private local varible;
+ // 2. the following store is a volatile store.
+ // the newly created array elements can be safely published.
+ BIG_TEN_POWERS_TABLE = pows;
+ }
+ return pows[n];
+ }
+ }
+
+ private static final long[] LONG_TEN_POWERS_TABLE = {
+ 1, // 0 / 10^0
+ 10, // 1 / 10^1
+ 100, // 2 / 10^2
+ 1000, // 3 / 10^3
+ 10000, // 4 / 10^4
+ 100000, // 5 / 10^5
+ 1000000, // 6 / 10^6
+ 10000000, // 7 / 10^7
+ 100000000, // 8 / 10^8
+ 1000000000, // 9 / 10^9
+ 10000000000L, // 10 / 10^10
+ 100000000000L, // 11 / 10^11
+ 1000000000000L, // 12 / 10^12
+ 10000000000000L, // 13 / 10^13
+ 100000000000000L, // 14 / 10^14
+ 1000000000000000L, // 15 / 10^15
+ 10000000000000000L, // 16 / 10^16
+ 100000000000000000L, // 17 / 10^17
+ 1000000000000000000L // 18 / 10^18
+ };
+
+ private static volatile BigInteger BIG_TEN_POWERS_TABLE[] = {BigInteger.ONE,
BigInteger.valueOf(10), BigInteger.valueOf(100),
BigInteger.valueOf(1000), BigInteger.valueOf(10000),
BigInteger.valueOf(100000), BigInteger.valueOf(1000000),
BigInteger.valueOf(10000000), BigInteger.valueOf(100000000),
- BigInteger.valueOf(1000000000)};
+ BigInteger.valueOf(1000000000),
+ BigInteger.valueOf(10000000000L),
+ BigInteger.valueOf(100000000000L),
+ BigInteger.valueOf(1000000000000L),
+ BigInteger.valueOf(10000000000000L),
+ BigInteger.valueOf(100000000000000L),
+ BigInteger.valueOf(1000000000000000L),
+ BigInteger.valueOf(10000000000000000L),
+ BigInteger.valueOf(100000000000000000L),
+ BigInteger.valueOf(1000000000000000000L)
+ };
+
+ private static final int BIG_TEN_POWERS_TABLE_INITLEN =
+ BIG_TEN_POWERS_TABLE.length;
+ private static final int BIG_TEN_POWERS_TABLE_MAX =
+ 16 * BIG_TEN_POWERS_TABLE_INITLEN;
+
+ private static final long THRESHOLDS_TABLE[] = {
+ Long.MAX_VALUE, // 0
+ Long.MAX_VALUE/10L, // 1
+ Long.MAX_VALUE/100L, // 2
+ Long.MAX_VALUE/1000L, // 3
+ Long.MAX_VALUE/10000L, // 4
+ Long.MAX_VALUE/100000L, // 5
+ Long.MAX_VALUE/1000000L, // 6
+ Long.MAX_VALUE/10000000L, // 7
+ Long.MAX_VALUE/100000000L, // 8
+ Long.MAX_VALUE/1000000000L, // 9
+ Long.MAX_VALUE/10000000000L, // 10
+ Long.MAX_VALUE/100000000000L, // 11
+ Long.MAX_VALUE/1000000000000L, // 12
+ Long.MAX_VALUE/10000000000000L, // 13
+ Long.MAX_VALUE/100000000000000L, // 14
+ Long.MAX_VALUE/1000000000000000L, // 15
+ Long.MAX_VALUE/10000000000000000L, // 16
+ Long.MAX_VALUE/100000000000000000L, // 17
+ Long.MAX_VALUE/1000000000000000000L // 18
+ };
/**
* Compute val * 10 ^ n; return this product if it is
* representable as a long, INFLATED otherwise.
*/
- private static long longTenToThe(long val, int n) {
- // System.err.print("\tval " + val + "\t power " + n + "\tresult ");
- if (n >= 0 && n < thresholds.length) {
- if (Math.abs(val) <= thresholds[n][0] ) {
- // System.err.println(val * thresholds[n][1]);
- return val * thresholds[n][1];
- }
+ private static long longMultiplyPowerTen(long val, int n) {
+ if (val == 0 || n <= 0)
+ return val;
+ long[] tab = LONG_TEN_POWERS_TABLE;
+ long[] bounds = THRESHOLDS_TABLE;
+ if (n < tab.length && n < bounds.length) {
+ long tenpower = tab[n];
+ if (val == 1)
+ return tenpower;
+ if (Math.abs(val) <= bounds[n])
+ return val * tenpower;
}
- // System.err.println(INFLATED);
return INFLATED;
}
- private static long thresholds[][] = {
- {Long.MAX_VALUE, 1L}, // 0
- {Long.MAX_VALUE/10L, 10L}, // 1
- {Long.MAX_VALUE/100L, 100L}, // 2
- {Long.MAX_VALUE/1000L, 1000L}, // 3
- {Long.MAX_VALUE/10000L, 10000L}, // 4
- {Long.MAX_VALUE/100000L, 100000L}, // 5
- {Long.MAX_VALUE/1000000L, 1000000L}, // 6
- {Long.MAX_VALUE/10000000L, 10000000L}, // 7
- {Long.MAX_VALUE/100000000L, 100000000L}, // 8
- {Long.MAX_VALUE/1000000000L, 1000000000L}, // 9
- {Long.MAX_VALUE/10000000000L, 10000000000L}, // 10
- {Long.MAX_VALUE/100000000000L, 100000000000L}, // 11
- {Long.MAX_VALUE/1000000000000L, 1000000000000L},// 12
- {Long.MAX_VALUE/100000000000000L, 10000000000000L},// 13
- };
+ /**
+ * Compute this * 10 ^ n.
+ * Needed mainly to allow special casing to trap zero value
+ */
+ private BigInteger bigMultiplyPowerTen(int n) {
+ if (n <= 0)
+ return this.inflate();
- private static boolean compactLong(long val) {
- return (val != Long.MIN_VALUE);
+ if (intCompact != INFLATED)
+ return bigTenToThe(n).multiply(intCompact);
+ else
+ return intVal.multiply(bigTenToThe(n));
}
/**
* Assign appropriate BigInteger to intVal field if intVal is
* null, i.e. the compact representation is in use.
*/
- private BigDecimal inflate() {
+ private BigInteger inflate() {
if (intVal == null)
intVal = BigInteger.valueOf(intCompact);
- return this;
+ return intVal;
}
/**
@@ -3218,10 +3530,13 @@
* {@code BigDecimal}s to be aligned.
*/
private static void matchScale(BigDecimal[] val) {
- if (val[0].scale < val[1].scale)
- val[0] = val[0].setScale(val[1].scale);
- else if (val[1].scale < val[0].scale)
- val[1] = val[1].setScale(val[0].scale);
+ if (val[0].scale == val[1].scale) {
+ return;
+ } else if (val[0].scale < val[1].scale) {
+ val[0] = val[0].setScale(val[1].scale, ROUND_UNNECESSARY);
+ } else if (val[1].scale < val[0].scale) {
+ val[1] = val[1].setScale(val[0].scale, ROUND_UNNECESSARY);
+ }
}
/**
@@ -3240,9 +3555,7 @@
throw new java.io.StreamCorruptedException(message);
// [all values of scale are now allowed]
}
- // Set intCompact to uninitialized value; could also see if the
- // intVal was small enough to fit as a compact value.
- intCompact = INFLATED;
+ intCompact = compactValFor(intVal);
}
/**
@@ -3259,83 +3572,65 @@
s.defaultWriteObject();
}
+
/**
- * Returns the length of this {@code BigDecimal}, in decimal digits.
- *
- * Notes:
- *<ul>
- * <li> This is performance-critical; most operations where a
- * context is supplied will need at least one call to this
- * method.
+ * Returns the length of the absolute value of a {@code long}, in decimal
+ * digits.
*
- * <li> This should be a method on BigInteger; the call to this
- * method in precision() can then be replaced with the
- * term: intVal.digitLength(). It could also be called
- * precision() in BigInteger.
+ * @param x the {@code long}
+ * @return the length of the unscaled value, in deciaml digits.
+ */
+ private static int longDigitLength(long x) {
+ /*
+ * As described in "Bit Twiddling Hacks" by Sean Anderson,
+ * (http://graphics.stanford.edu/~seander/bithacks.html)
+ * integer log 10 of x is within 1 of
+ * (1233/4096)* (1 + integer log 2 of x).
+ * The fraction 1233/4096 approximates log10(2). So we first
+ * do a version of log2 (a variant of Long class with
+ * pre-checks and opposite directionality) and then scale and
+ * check against powers table. This is a little simpler in
+ * present context than the version in Hacker's Delight sec
+ * 11-4. Adding one to bit length allows comparing downward
+ * from the LONG_TEN_POWERS_TABLE that we need anyway.
+ */
+ assert x != INFLATED;
+ if (x < 0)
+ x = -x;
+ if (x < 10) // must screen for 0, might as well 10
+ return 1;
+ int n = 64; // not 63, to avoid needing to add 1 later
+ int y = (int)(x >>> 32);
+ if (y == 0) { n -= 32; y = (int)x; }
+ if (y >>> 16 == 0) { n -= 16; y <<= 16; }
+ if (y >>> 24 == 0) { n -= 8; y <<= 8; }
+ if (y >>> 28 == 0) { n -= 4; y <<= 4; }
+ if (y >>> 30 == 0) { n -= 2; y <<= 2; }
+ int r = (((y >>> 31) + n) * 1233) >>> 12;
+ long[] tab = LONG_TEN_POWERS_TABLE;
+ // if r >= length, must have max possible digits for long
+ return (r >= tab.length || x < tab[r])? r : r+1;
+ }
+
+ /**
+ * Returns the length of the absolute value of a BigInteger, in
+ * decimal digits.
*
- * Better still -- the precision lookaside could be moved to
- * BigInteger, too.
- *
- * <li> This could/should use MutableBigIntegers directly for the
- * reduction loop.
- *<ul>
+ * @param b the BigInteger
* @return the length of the unscaled value, in decimal digits
*/
- private int digitLength() {
- if (intCompact != INFLATED && Math.abs(intCompact) <= Integer.MAX_VALUE)
- return intLength(Math.abs((int)intCompact));
- if (signum() == 0) // 0 is one decimal digit
+ private static int bigDigitLength(BigInteger b) {
+ /*
+ * Same idea as the long version, but we need a better
+ * approximation of log10(2). Using 646456993/2^31
+ * is accurate up to max possible reported bitLength.
+ */
+ if (b.signum == 0)
return 1;
- this.inflate();
- // we have a nonzero magnitude
- BigInteger work = intVal;
- int digits = 0; // counter
- for (;work.mag.length>1;) {
- // here when more than one integer in the magnitude; divide
- // by a billion (reduce by 9 digits) and try again
- work = work.divide(TENPOWERS[9]);
- digits += 9;
- if (work.signum() == 0) // the division was exact
- return digits; // (a power of a billion)
- }
- // down to a simple nonzero integer
- digits += intLength(work.mag[0]);
- // System.out.println("digitLength... "+this+" -> "+digits);
- return digits;
+ int r = (int)((((long)b.bitLength() + 1) * 646456993) >>> 31);
+ return b.compareMagnitude(bigTenToThe(r)) < 0? r : r+1;
}
- private static int[] ilogTable = {
- 0,
- 9,
- 99,
- 999,
- 9999,
- 99999,
- 999999,
- 9999999,
- 99999999,
- 999999999,
- Integer.MAX_VALUE};
-
- /**
- * Returns the length of an unsigned {@code int}, in decimal digits.
- * @param i the {@code int} (treated as unsigned)
- * @return the length of the unscaled value, in decimal digits
- */
- private int intLength(int x) {
- int digits;
- if (x < 0) { // 'negative' is 10 digits unsigned
- return 10;
- } else { // positive integer
- if (x <= 9)
- return 1;
- // "Hacker's Delight" section 11-4
- for(int i = -1; ; i++) {
- if (x <= ilogTable[i+1])
- return i +1;
- }
- }
- }
/**
* Remove insignificant trailing zeros from this
@@ -3352,10 +3647,9 @@
* to be closed to the preferred scale.
*/
private BigDecimal stripZerosToMatchScale(long preferredScale) {
- boolean compact = (intCompact != INFLATED);
this.inflate();
BigInteger qr[]; // quotient-remainder pair
- while ( intVal.abs().compareTo(BigInteger.TEN) >= 0 &&
+ while ( intVal.compareMagnitude(BigInteger.TEN) >= 0 &&
scale > preferredScale) {
if (intVal.testBit(0))
break; // odd number cannot end in 0
@@ -3367,17 +3661,16 @@
if (precision > 0) // adjust precision if known
precision--;
}
- if (compact)
- intCompact = intVal.longValue();
+ if (intVal != null)
+ intCompact = compactValFor(intVal);
return this;
}
/**
* Check a scale for Underflow or Overflow. If this BigDecimal is
- * uninitialized or initialized and nonzero, throw an exception if
- * the scale is out of range. If this is zero, saturate the scale
- * to the extreme value of the right sign if the scale is out of
- * range.
+ * nonzero, throw an exception if the scale is outof range. If this
+ * is zero, saturate the scale to the extreme value of the right
+ * sign if the scale is out of range.
*
* @param val The new scale.
* @throws ArithmeticException (overflow or underflow) if the new
@@ -3385,19 +3678,15 @@
* @return validated scale as an int.
*/
private int checkScale(long val) {
- if ((int)val != val) {
- if ((this.intCompact != INFLATED && this.intCompact != 0) ||
- (this.intVal != null && this.signum() != 0) ||
- (this.intVal == null && this.intCompact == INFLATED) ) {
- if (val > Integer.MAX_VALUE)
- throw new ArithmeticException("Underflow");
- if (val < Integer.MIN_VALUE)
- throw new ArithmeticException("Overflow");
- } else {
- return (val > Integer.MAX_VALUE)?Integer.MAX_VALUE:Integer.MIN_VALUE;
- }
+ int asInt = (int)val;
+ if (asInt != val) {
+ asInt = val>Integer.MAX_VALUE ? Integer.MAX_VALUE : Integer.MIN_VALUE;
+ BigInteger b;
+ if (intCompact != 0 &&
+ ((b = intVal) == null || b.signum() != 0))
+ throw new ArithmeticException(asInt>0 ? "Underflow":"Overflow");
}
- return (int)val;
+ return asInt;
}
/**
@@ -3410,7 +3699,7 @@
* rounding mode is {@code UNNECESSARY}.
*/
private BigDecimal roundOp(MathContext mc) {
- BigDecimal rounded = doRound(mc);
+ BigDecimal rounded = doRound(this, mc);
return rounded;
}
@@ -3426,7 +3715,7 @@
* {@code BigDecimal} operation would require rounding.
*/
private void roundThis(MathContext mc) {
- BigDecimal rounded = doRound(mc);
+ BigDecimal rounded = doRound(this, mc);
if (rounded == this) // wasn't rounded
return;
this.intVal = rounded.intVal;
@@ -3448,56 +3737,56 @@
* {@code RoundingMode.UNNECESSARY} and the
* result is inexact.
*/
- private BigDecimal doRound(MathContext mc) {
- this.inflate();
- if (precision == 0) {
- if (mc.roundingMax != null
- && intVal.compareTo(mc.roundingMax) < 0
- && intVal.compareTo(mc.roundingMin) > 0)
- return this; // no rounding needed
- precision(); // find it
+ private static BigDecimal doRound(BigDecimal d, MathContext mc) {
+ int mcp = mc.precision;
+ int drop;
+ // This might (rarely) iterate to cover the 999=>1000 case
+ while ((drop = d.precision() - mcp) > 0) {
+ int newScale = d.checkScale((long)d.scale - drop);
+ int mode = mc.roundingMode.oldMode;
+ if (drop < LONG_TEN_POWERS_TABLE.length)
+ d = divideAndRound(d.intCompact, d.intVal,
+ LONG_TEN_POWERS_TABLE[drop], null,
+ newScale, mode, newScale);
+ else
+ d = divideAndRound(d.intCompact, d.intVal,
+ INFLATED, bigTenToThe(drop),
+ newScale, mode, newScale);
}
- int drop = precision - mc.precision; // digits to discard
- if (drop <= 0) // we fit
- return this;
- BigDecimal rounded = dropDigits(mc, drop);
- // we need to double-check, in case of the 999=>1000 case
- return rounded.doRound(mc);
+ return d;
}
/**
- * Removes digits from the significand of a {@code BigDecimal},
- * rounding according to the MathContext settings. Does not
- * change {@code this}; a new {@code BigDecimal} is always
- * created and returned.
- *
- * <p>Actual rounding is carried out, as before, by the divide
- * method, as this minimized code changes. It might be more
- * efficient in most cases to move rounding to here, so we can do
- * a round-to-length rather than round-to-scale.
- *
- * @param mc the context to use.
- * @param drop the number of digits to drop, must be {@literal >} 0
- * @return a {@code BigDecimal} rounded according to the MathContext
- * settings. May return {@code this}, if no rounding needed.
- * @throws ArithmeticException if the rounding mode is
- * {@code RoundingMode.UNNECESSARY} and the
- * result is inexact.
+ * Returns the compact value for given {@code BigInteger}, or
+ * INFLATED if too big. Relies on internal representation of
+ * {@code BigInteger}.
*/
- private BigDecimal dropDigits(MathContext mc, int drop) {
- // here if we need to round; make the divisor = 10**drop)
- // [calculating the BigInteger here saves setScale later]
- BigDecimal divisor = new BigDecimal(tenToThe(drop), 0);
+ private static long compactValFor(BigInteger b) {
+ int[] m = b.mag;
+ int len = m.length;
+ if (len == 0)
+ return 0;
+ int d = m[0];
+ if (len > 2 || (len == 2 && d < 0))
+ return INFLATED;
- // divide to same scale to force round to length
- BigDecimal rounded = this.divide(divisor, scale,
- mc.roundingMode.oldMode);
- rounded.scale = checkScale((long)rounded.scale - drop ); // adjust the scale
- return rounded;
+ long u = (len == 2)?
+ (((long) m[1] & LONG_MASK) + (((long)d) << 32)) :
+ (((long)d) & LONG_MASK);
+ return (b.signum < 0)? -u : u;
}
- private static int longCompareTo(long x, long y) {
- return (x < y) ? -1 : (x == y) ? 0 : 1;
+ private static int longCompareMagnitude(long x, long y) {
+ if (x < 0)
+ x = -x;
+ if (y < 0)
+ y = -y;
+ return (x < y) ? -1 : ((x == y) ? 0 : 1);
+ }
+
+ private static int saturateLong(long s) {
+ int i = (int)s;
+ return (s == i) ? i : (s < 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE);
}
/*
@@ -3527,21 +3816,21 @@
*
* <li>If precision is nonzero, it must have the right value.
* </ul>
+ *
+ * Note: Since this is an audit method, we are not supposed to change the
+ * state of this BigDecimal object.
*/
private BigDecimal audit() {
- // Check precision
- if (precision > 0) {
- if (precision != digitLength()) {
- print("audit", this);
- throw new AssertionError("precision mismatch");
- }
- }
-
if (intCompact == INFLATED) {
if (intVal == null) {
print("audit", this);
throw new AssertionError("null intVal");
}
+ // Check precision
+ if (precision > 0 && precision != bigDigitLength(intVal)) {
+ print("audit", this);
+ throw new AssertionError("precision mismatch");
+ }
} else {
if (intVal != null) {
long val = intVal.longValue();
@@ -3551,6 +3840,11 @@
intCompact + "\t intVal=" + val);
}
}
+ // Check precision
+ if (precision > 0 && precision != longDigitLength(intCompact)) {
+ print("audit", this);
+ throw new AssertionError("precision mismatch");
+ }
}
return this;
}