Fix a couple of bugs in BigDecimal that were corrected in the
gwt-java-math project.
Public review at: http://gwt-code-reviews.appspot.com/153814/show
Patch by: jat, richard@zschech.net
Review by: rice
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7644 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/super/com/google/gwt/emul/java/math/BigDecimal.java b/user/super/com/google/gwt/emul/java/math/BigDecimal.java
index 569918e..8420da2 100644
--- a/user/super/com/google/gwt/emul/java/math/BigDecimal.java
+++ b/user/super/com/google/gwt/emul/java/math/BigDecimal.java
@@ -48,6 +48,11 @@
Serializable {
/**
+ * One more than the number of bits which can be stored in {@link #smallValue}.
+ */
+ private static final int SMALL_VALUE_BITS = 54;
+
+ /**
* The constant one as a {@code BigDecimal}.
*/
public static final BigDecimal ONE = new BigDecimal(1, 0);
@@ -158,7 +163,8 @@
private static final double[] DOUBLE_TEN_POW = new double[] {
1D, 10D, 100D, 1000D, 10000D, 100000D, 1000000D, 10000000D, 100000000D,
1000000000D, 10000000000D, 100000000000D, 1000000000000D,
- 10000000000000D, 100000000000000D,};
+ 10000000000000D, 100000000000000D, 1000000000000000D,
+ 10000000000000000D,};
private static final int[] DOUBLE_TEN_POW_BIT_LENGTH = new int[DOUBLE_TEN_POW.length];
@@ -279,7 +285,8 @@
BigDecimal augend, double diffScale) {
if (diffScale < DOUBLE_TEN_POW.length
&& Math.max(thisValue.bitLength, augend.bitLength
- + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) + 1 < 54) {
+ + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) + 1
+ < SMALL_VALUE_BITS) {
return valueOf(thisValue.smallValue + augend.smallValue
* DOUBLE_TEN_POW[(int) diffScale], thisValue.scale);
}
@@ -317,7 +324,7 @@
}
int sign = scaledDividend.signum() * scaledDivisor.signum();
int compRem; // 'compare to remainder'
- if (scaledDivisor.bitLength() < 54) {
+ if (scaledDivisor.bitLength() < SMALL_VALUE_BITS) {
long rem = remainder.longValue();
long divisor = scaledDivisor.longValue();
compRem = longCompareTo(Math.abs(rem) << 1, Math.abs(divisor));
@@ -332,7 +339,7 @@
* (5 + compRem), roundingMode);
}
if (compRem != 0) {
- if (quotient.bitLength() < 54) {
+ if (quotient.bitLength() < SMALL_VALUE_BITS) {
return valueOf(quotient.longValue() + compRem, scale);
}
quotient = quotient.add(BigInteger.valueOf(compRem));
@@ -598,6 +605,10 @@
private double scale;
+ /**
+ * The unscaled integer value (stored in a double) if the number of bits is
+ * less than {@link #SMALL_VALUE_BITS}.
+ */
private transient double smallValue;
/**
@@ -884,7 +895,7 @@
private BigDecimal(long smallValue, int scale) {
this.scale = scale;
this.bitLength = bitLength(smallValue);
- if (bitLength < 54) {
+ if (bitLength < SMALL_VALUE_BITS) {
this.smallValue = smallValue;
} else {
this.intVal = BigInteger.valueOf(smallValue);
@@ -939,7 +950,7 @@
// Let be: this = [u1,s1] and augend = [u2,s2]
if (diffScale == 0) {
// case s1 == s2: [u1 + u2 , s1]
- if (Math.max(this.bitLength, augend.bitLength) + 1 < 54) {
+ if (Math.max(this.bitLength, augend.bitLength) + 1 < SMALL_VALUE_BITS) {
return valueOf(this.smallValue + augend.smallValue, this.scale);
}
return new BigDecimal(this.getUnscaledValue().add(
@@ -1037,7 +1048,8 @@
int valueSign = val.signum();
if (thisSign == valueSign) {
- if (this.scale == val.scale && this.bitLength < 54 && val.bitLength < 54) {
+ if (this.scale == val.scale && this.bitLength < SMALL_VALUE_BITS
+ && val.bitLength < SMALL_VALUE_BITS) {
return (smallValue < val.smallValue) ? -1
: (smallValue > val.smallValue) ? 1 : 0;
}
@@ -1214,20 +1226,23 @@
}
double diffScale = this.scale - divisor.scale - scale;
- if (this.bitLength < 54 && divisor.bitLength < 54) {
+ if (this.bitLength < SMALL_VALUE_BITS
+ && divisor.bitLength < SMALL_VALUE_BITS) {
if (diffScale == 0) {
return dividePrimitiveLongs((long) this.smallValue,
(long) divisor.smallValue, scale, roundingMode);
} else if (diffScale > 0) {
if (diffScale < DOUBLE_TEN_POW.length
- && divisor.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale] < 54) {
+ && divisor.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[
+ (int) diffScale] < SMALL_VALUE_BITS) {
return dividePrimitiveLongs((long) this.smallValue,
(long) (divisor.smallValue * DOUBLE_TEN_POW[(int) diffScale]),
scale, roundingMode);
}
} else { // diffScale < 0
if (-diffScale < DOUBLE_TEN_POW.length
- && this.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) -diffScale] < 54) {
+ && this.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) -diffScale]
+ < SMALL_VALUE_BITS) {
return dividePrimitiveLongs(
(long) (this.smallValue * DOUBLE_TEN_POW[(int) -diffScale]),
(long) divisor.smallValue, scale, roundingMode);
@@ -1611,7 +1626,7 @@
if (x instanceof BigDecimal) {
BigDecimal x1 = (BigDecimal) x;
return x1.scale == scale
- && (bitLength < 54 ? (x1.smallValue == smallValue)
+ && (bitLength < SMALL_VALUE_BITS ? (x1.smallValue == smallValue)
: intVal.equals(x1.intVal));
}
return false;
@@ -1766,7 +1781,7 @@
if (hashCode != 0) {
return hashCode;
}
- if (bitLength < 54) {
+ if (bitLength < SMALL_VALUE_BITS) {
long longValue = (long) smallValue;
hashCode = (int) (longValue & 0xffffffff);
hashCode = 33 * hashCode + (int) ((longValue >> 32) & 0xffffffff);
@@ -1915,7 +1930,7 @@
* Let be: this = [u1,s1] and multiplicand = [u2,s2] so: this x multiplicand
* = [ s1 * s2 , s1 + s2 ]
*/
- if (this.bitLength + multiplicand.bitLength < 54) {
+ if (this.bitLength + multiplicand.bitLength < SMALL_VALUE_BITS) {
return valueOf(this.smallValue * multiplicand.smallValue,
toIntScale(newScale));
}
@@ -1947,7 +1962,7 @@
* @return {@code -this}
*/
public BigDecimal negate() {
- if (bitLength < 54) {
+ if (bitLength < SMALL_VALUE_BITS) {
return valueOf(-smallValue, scale);
}
return new BigDecimal(getUnscaledValue().negate(), scale);
@@ -2079,17 +2094,15 @@
if (precision > 0) {
return precision;
}
- int decimalDigits = 1; // the precision to be calculated
+ double decimalDigits = 1; // the precision to be calculated
double doubleUnsc = 1; // intVal in 'double'
- if (bitLength < 1024) {
+ if (bitLength < SMALL_VALUE_BITS) {
// To calculate the precision for small numbers
- if (bitLength >= 54) {
- doubleUnsc = getUnscaledValue().doubleValue();
- } else if (bitLength >= 1) {
+ if (bitLength >= 1) {
doubleUnsc = smallValue;
}
- decimalDigits += (int) Math.log10(Math.abs(doubleUnsc));
+ decimalDigits += Math.log10(Math.abs(doubleUnsc));
} else {
// (bitLength >= 1024)
/*
@@ -2102,7 +2115,7 @@
decimalDigits++;
}
}
- precision = decimalDigits;
+ precision = (int) decimalDigits;
return precision;
}
@@ -2190,7 +2203,7 @@
*/
public BigDecimal scaleByPowerOfTen(int n) {
double newScale = scale - n;
- if (bitLength < 54) {
+ if (bitLength < SMALL_VALUE_BITS) {
// Taking care when a 0 is to be scaled
if (smallValue == 0) {
return zeroScaledBy(newScale);
@@ -2271,7 +2284,8 @@
if (diffScale > 0) {
// return [u * 10^(s2 - s), newScale]
if (diffScale < DOUBLE_TEN_POW.length
- && (this.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) < 54) {
+ && (this.bitLength + DOUBLE_TEN_POW_BIT_LENGTH[
+ (int) diffScale]) < SMALL_VALUE_BITS) {
return valueOf(this.smallValue * DOUBLE_TEN_POW[(int) diffScale],
newScale);
}
@@ -2280,7 +2294,8 @@
}
// diffScale < 0
// return [u,s] / [1,newScale] with the appropriate scale and rounding
- if (this.bitLength < 54 && -diffScale < DOUBLE_TEN_POW.length) {
+ if (this.bitLength < SMALL_VALUE_BITS
+ && -diffScale < DOUBLE_TEN_POW.length) {
return dividePrimitiveLongs((long) this.smallValue,
(long) DOUBLE_TEN_POW[(int) -diffScale], newScale, roundingMode);
}
@@ -2308,7 +2323,7 @@
* {@code 1} if {@code this > 0}.
*/
public int signum() {
- if (bitLength < 54) {
+ if (bitLength < SMALL_VALUE_BITS) {
return this.smallValue < 0 ? -1 : this.smallValue > 0 ? 1 : 0;
}
return getUnscaledValue().signum();
@@ -2385,7 +2400,8 @@
// Let be: this = [u1,s1] and subtrahend = [u2,s2] so:
if (diffScale == 0) {
// case s1 = s2 : [u1 - u2 , s1]
- if (Math.max(this.bitLength, subtrahend.bitLength) + 1 < 54) {
+ if (Math.max(this.bitLength, subtrahend.bitLength) + 1
+ < SMALL_VALUE_BITS) {
return valueOf(this.smallValue - subtrahend.smallValue, this.scale);
}
return new BigDecimal(this.getUnscaledValue().subtract(
@@ -2394,7 +2410,8 @@
// case s1 > s2 : [ u1 - u2 * 10 ^ (s1 - s2) , s1 ]
if (diffScale < DOUBLE_TEN_POW.length
&& Math.max(this.bitLength, subtrahend.bitLength
- + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) + 1 < 54) {
+ + DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale]) + 1
+ < SMALL_VALUE_BITS) {
return valueOf(this.smallValue - subtrahend.smallValue
* DOUBLE_TEN_POW[(int) diffScale], this.scale);
}
@@ -2407,7 +2424,7 @@
if (diffScale < DOUBLE_TEN_POW.length
&& Math.max(this.bitLength
+ DOUBLE_TEN_POW_BIT_LENGTH[(int) diffScale],
- subtrahend.bitLength) + 1 < 54) {
+ subtrahend.bitLength) + 1 < SMALL_VALUE_BITS) {
return valueOf(this.smallValue * DOUBLE_TEN_POW[(int) diffScale]
- subtrahend.smallValue, subtrahend.scale);
}
@@ -2746,9 +2763,6 @@
String scaleString = null; // buffer for scale
StringBuilder unscaledBuffer; // buffer for unscaled value
- if (val == null) {
- throw new NullPointerException();
- }
unscaledBuffer = new StringBuilder(val.length());
// To skip a possible '+' symbol
if ((offset <= last) && (val.charAt(offset) == '+')) {
@@ -2822,8 +2836,13 @@
setUnscaledValue(new BigInteger(unscaled));
}
precision = unscaledBuffer.length() - counter;
- if (unscaledBuffer.charAt(0) == '-') {
- precision--;
+ // Don't count leading zeros in the precision
+ for (int i = 0; i < unscaledBuffer.length(); ++i) {
+ char ch = unscaledBuffer.charAt(i);
+ if (ch != '-' && ch != '0') {
+ break;
+ }
+ --precision;
}
}
@@ -2836,7 +2855,7 @@
*/
private void inplaceRound(MathContext mc) {
int mcPrecision = mc.getPrecision();
- if (approxPrecision() - mcPrecision <= 0 || mcPrecision == 0) {
+ if (approxPrecision() - mcPrecision < 0 || mcPrecision == 0) {
return;
}
int discardedPrecision = precision() - mcPrecision;
@@ -2845,7 +2864,7 @@
return;
}
// When the number is small perform an efficient rounding
- if (this.bitLength < 54) {
+ if (this.bitLength < SMALL_VALUE_BITS) {
smallRound(mc, discardedPrecision);
return;
}
@@ -2893,13 +2912,14 @@
* movePointRight(-n) since -Integer.MIN_VALUE == Integer.MIN_VALUE
*/
if (newScale >= 0) {
- if (bitLength < 54) {
+ if (bitLength < SMALL_VALUE_BITS) {
return valueOf(smallValue, toIntScale(newScale));
}
return new BigDecimal(getUnscaledValue(), toIntScale(newScale));
}
if (-newScale < DOUBLE_TEN_POW.length
- && bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) -newScale] < 54) {
+ && bitLength + DOUBLE_TEN_POW_BIT_LENGTH[(int) -newScale]
+ < SMALL_VALUE_BITS) {
return valueOf(smallValue * DOUBLE_TEN_POW[(int) -newScale], 0);
}
return new BigDecimal(Multiplication.multiplyByTenPow(getUnscaledValue(),
@@ -2909,7 +2929,7 @@
private void setUnscaledValue(BigInteger unscaledValue) {
this.intVal = unscaledValue;
this.bitLength = unscaledValue.bitLength();
- if (this.bitLength < 54) {
+ if (this.bitLength < SMALL_VALUE_BITS) {
this.smallValue = unscaledValue.longValue();
}
}
diff --git a/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java b/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java
index 99ae798..a0a7fde 100644
--- a/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java
+++ b/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java
@@ -1513,6 +1513,32 @@
}
/**
+ * Test gwt-java-math issues 4 and 5.
+ */
+ public void testRoundMathContextCEILING() {
+ BigDecimal val = BigDecimal.valueOf(1.5);
+ BigDecimal result = val.round(new MathContext(1, RoundingMode.CEILING));
+ assertEquals("2", result.toString());
+ assertEquals(0, result.scale());
+ assertEquals(1, result.precision());
+
+ // 1 digit left of dp, 14 scale + 1
+ val = BigDecimal.valueOf(5.43445663479765);
+ val.setScale(val.scale() + 1, RoundingMode.CEILING).round(new
+ MathContext(1, RoundingMode.CEILING));
+
+ // 1 digit left of dp, 13 scale + 2
+ val = BigDecimal.valueOf(5.4344566347976);
+ val.setScale(val.scale() + 2, RoundingMode.CEILING).round(new
+ MathContext(1, RoundingMode.CEILING));
+
+ // 2 digits left of dp, 13 scale + 2
+ BigDecimal test = BigDecimal.valueOf(12.4344566347976);
+ test.setScale(test.scale() + 1, RoundingMode.CEILING).round(new
+ MathContext(1, RoundingMode.CEILING));
+ }
+
+ /**
* round(BigDecimal, MathContext)
*/
public void testRoundMathContextHALF_DOWN() {
diff --git a/user/test/com/google/gwt/emultest/java/math/BigDecimalConstructorsTest.java b/user/test/com/google/gwt/emultest/java/math/BigDecimalConstructorsTest.java
index 66fe6ba..dea49e7 100644
--- a/user/test/com/google/gwt/emultest/java/math/BigDecimalConstructorsTest.java
+++ b/user/test/com/google/gwt/emultest/java/math/BigDecimalConstructorsTest.java
@@ -666,6 +666,14 @@
}
/**
+ * Test second failing example in gwt-java-math issue 4.
+ */
+ public void testConstrStringWithLeadingZeros() {
+ BigDecimal value = new BigDecimal("-000.1");
+ assertEquals("bad precision", 1, value.precision());
+ }
+
+ /**
* new BigDecimal(String value); value does not contain exponent.
*/
public void testConstrStringWithoutExpNeg() {