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() {