Revision: 7328 Author: [email protected] Date: Thu Dec 17 14:28:24 2009 Log: Fix NumberFormat to work with BigInteger/BigDecimal (using code/ideas from Dan's implementation), fix bug present in Harmony, more tests.
http://code.google.com/p/google-web-toolkit/source/detail?r=7328 Modified: /changes/jat/bigdecimal/user/src/com/google/gwt/i18n/client/NumberFormat.java /changes/jat/bigdecimal/user/super/com/google/gwt/emul/java/math/BigDecimal.java /changes/jat/bigdecimal/user/super/com/google/gwt/emul/java/math/BigInteger.java /changes/jat/bigdecimal/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java /changes/jat/bigdecimal/user/test/com/google/gwt/emultest/java/math/BigIntegerConvertTest.java /changes/jat/bigdecimal/user/test/com/google/gwt/i18n/client/NumberFormat_en_Test.java ======================================= --- /changes/jat/bigdecimal/user/src/com/google/gwt/i18n/client/NumberFormat.java Sun Dec 13 22:19:25 2009 +++ /changes/jat/bigdecimal/user/src/com/google/gwt/i18n/client/NumberFormat.java Thu Dec 17 14:28:24 2009 @@ -15,7 +15,6 @@ */ package com.google.gwt.i18n.client; -import com.google.gwt.core.client.GWT; import com.google.gwt.i18n.client.constants.NumberConstants; import java.math.BigDecimal; @@ -610,7 +609,48 @@ } return "\u00A0"; } - + + /** + * Appends a scaled string representation to a buffer, returning the scale + * (which is the number of places to the right of the end of the string the + * decimal point should be moved -- i.e., 3.5 would be added to the buffer + * as "35" and a returned scale of -1). + * + * @param buf + * @param val + * @return scale to apply to the result + */ + // @VisibleForTesting + static int toScaledString(StringBuilder buf, double val) { + int startLen = buf.length(); + buf.append(toPrecision(val, 20)); + int scale = 0; + + // remove exponent if present, adjusting scale + int expIdx = buf.indexOf("e", startLen); + if (expIdx < 0) { + expIdx = buf.indexOf("E", startLen); + } + if (expIdx >= 0) { + int expDigits = expIdx + 1; + if (expDigits < buf.length() && buf.charAt(expDigits) == '+') { + ++expDigits; + } + if (expDigits < buf.length()) { + scale = Integer.parseInt(buf.substring(expDigits)); + } + buf.delete(expIdx, buf.length()); + } + + // remove decimal point if present, adjusting scale + int dot = buf.indexOf(".", startLen); + if (dot >= 0) { + buf.deleteCharAt(dot); + scale -= buf.length() - dot; + } + return scale; + } + /** * Lookup a currency code. * @@ -628,31 +668,48 @@ return currencyData; } - private static native String toFixed(double d, int digits) /*-{ - return d.toFixed(digits); + /** + * Convert a double to a string with {...@code digits} precision. The resulting + * string may still be in exponential notation. + * + * @param d double value + * @param digits number of digits of precision to include + * @return non-localized string representation of {...@code d} + */ + private static native String toPrecision(double d, int digits) /*-{ + return d.toPrecision(digits); }-*/; - // The currency code. + /** + * The currency code. + */ private final String currencyCode; - // Currency setting. + /** + * Currency symbol to use. + */ private final String currencySymbol; - // Forces the decimal separator to always appear in a formatted number. + /** + * Forces the decimal separator to always appear in a formatted number. + */ private boolean decimalSeparatorAlwaysShown = false; - // The number of digits between grouping separators in the integer - // portion of a number. + /** + * The number of digits between grouping separators in the integer portion of + * a number. + */ private int groupingSize = 3; - + private boolean isCurrencyFormat = false; private int maximumFractionDigits = 3; // invariant, >= minFractionDigits. + private int maximumIntegerDigits = 40; private int minExponentDigits; private int minimumFractionDigits = 0; private int minimumIntegerDigits = 1; - + // The multiplier for use in percent, per mille, etc. private int multiplier = 1; @@ -673,6 +730,24 @@ // True to force the use of exponential (i.e. scientific) notation. private boolean useExponentialNotation = false; + /** + * Holds the current exponent during one call to + * {...@link #format(boolean, StringBuilder, int)}. + */ + private transient int exponent; + + /** + * Holds the current decimal position during one call to + * {...@link #format(boolean, StringBuilder, int)}. + */ + private transient int decimalPosition; + + /** + * Holds the current digits length during one call to + * {...@link #format(boolean, StringBuilder, int)}. + */ + private transient int digitsLength; + /** * Constructs a format object based on the specified settings. * @@ -722,33 +797,35 @@ * @return the formatted number string */ public String format(double number) { - StringBuffer result = new StringBuffer(); - if (Double.isNaN(number)) { - result.append(numberConstants.notANumber()); - return result.toString(); - } - + return numberConstants.notANumber(); + } boolean isNegative = ((number < 0.0) || (number == 0.0 && 1 / number < 0.0)); - - result.append(isNegative ? negativePrefix : positivePrefix); + if (isNegative) { + number = -number; + } + StringBuilder buf = new StringBuilder(); if (Double.isInfinite(number)) { - result.append(numberConstants.infinity()); - } else { - if (isNegative) { - number = -number; - } - number *= multiplier; - if (useExponentialNotation) { - subformatExponential(number, result); - } else { - subformatFixed(number, result, minimumIntegerDigits); - } - } - - result.append(isNegative ? negativeSuffix : positiveSuffix); - - return result.toString(); + buf.append(isNegative ? negativePrefix : positivePrefix); + buf.append(numberConstants.infinity()); + buf.append(isNegative ? negativeSuffix : positiveSuffix); + return buf.toString(); + } + number *= multiplier; + int scale = toScaledString(buf, number); + + // pre-round value to deal with .15 being represented as .149999... etc + // check at 3 more digits than will be required in the output + int preRound = buf.length() + scale + maximumFractionDigits + 3; + if (preRound > 0 && preRound < buf.length() + && buf.charAt(preRound) == '9') { + propagateCarry(buf, preRound - 1); + scale += buf.length() - preRound; + buf.delete(preRound, buf.length()); + } + + format(isNegative, buf, scale); + return buf.toString(); } /** @@ -758,18 +835,41 @@ * @return the formatted number string */ public String format(Number number) { - int scale = 0; - BigInteger bigInt = null; if (number instanceof BigDecimal) { BigDecimal bigDec = (BigDecimal) number; - bigInt = bigDec.unscaledValue(); - scale = bigDec.scale(); + boolean isNegative = bigDec.signum() < 0; + if (isNegative) { + bigDec = bigDec.negate(); + } + bigDec = bigDec.multiply(BigDecimal.valueOf(multiplier)); + StringBuilder buf = new StringBuilder(); + buf.append(bigDec.unscaledValue().toString()); + format(isNegative, buf, -bigDec.scale()); + return buf.toString(); } else if (number instanceof BigInteger) { - bigInt = (BigInteger) number; + BigInteger bigInt = (BigInteger) number; + boolean isNegative = bigInt.signum() < 0; + if (isNegative) { + bigInt = bigInt.negate(); + } + bigInt = bigInt.multiply(BigInteger.valueOf(multiplier)); + StringBuilder buf = new StringBuilder(); + buf.append(bigInt.toString()); + format(isNegative, buf, 0); + return buf.toString(); + } else if (number instanceof Long) { + long longVal = number.longValue(); + boolean isNegative = longVal < 0; + if (isNegative) { + longVal = -longVal; + } + StringBuilder buf = new StringBuilder(); + buf.append(String.valueOf(longVal)); + format(isNegative, buf, 0); + return buf.toString(); } else { return format(number.doubleValue()); } - return formatBig(bigInt, scale); } /** @@ -898,10 +998,10 @@ * @throws NumberFormatException if the entire text could not be converted * into a number */ - public Number parseBig(String text) throws NumberFormatException { - // TODO(jat): implement - return Double.valueOf(parse(text)); - } +// public Number parseBig(String text) throws NumberFormatException { +// // TODO(jat): implement +// return Double.valueOf(parse(text)); +// } /** * Parses text to produce a numeric value. @@ -922,11 +1022,11 @@ * BigDecimal, or {...@code Double(0.0)} if the parse fails. * @throws NumberFormatException if the text segment could not be converted into a number */ - public Number parseBig(String text, int[] inOutPos) - throws NumberFormatException { - // TODO(jat): implement - return Double.valueOf(parse(text, inOutPos)); - } +// public Number parseBig(String text, int[] inOutPos) +// throws NumberFormatException { +// // TODO(jat): implement +// return Double.valueOf(parse(text, inOutPos)); +// } protected int getGroupingSize() { return groupingSize; @@ -957,41 +1057,178 @@ } /** - * This method formats the exponent part of a double. + * Add exponent suffix. * - * @param exponent exponential value - * @param result formatted exponential part will be append to it + * @param digits */ - private void addExponentPart(int exponent, StringBuffer result) { - result.append(numberConstants.exponentialSymbol()); - + private void addExponent(StringBuilder digits) { + digits.append(numberConstants.exponentialSymbol()); if (exponent < 0) { exponent = -exponent; - result.append(numberConstants.minusSign()); - } - + digits.append(numberConstants.minusSign()); + } String exponentDigits = String.valueOf(exponent); - int len = exponentDigits.length(); - for (int i = len; i < minExponentDigits; ++i) { - result.append(numberConstants.zeroDigit()); - } - int zeroDelta = numberConstants.zeroDigit().charAt(0) - '0'; - for (int i = 0; i < len; ++i) { - result.append((char) (exponentDigits.charAt(i) + zeroDelta)); + for (int i = exponentDigits.length(); i < minExponentDigits; ++i) { + digits.append('0'); + } + digits.append(exponentDigits); + } + + /** + * @param digits + * @param decimalSeparator + */ + private void addZeroAndDecimal(StringBuilder digits, char decimalSeparator) { + // add zero and decimal point if required + if (digitsLength == 0) { + digits.insert(0, '0'); + ++decimalPosition; + } + if (decimalPosition < digitsLength || decimalSeparatorAlwaysShown) { + digits.insert(decimalPosition, decimalSeparator); } } /** - * Format a scaled BigInteger. + * Adjust the fraction digits, adding trailing zeroes if necessary or removing + * excess trailing zeroes. * - * @param bigInt value to format - * @param scale number of places to move the decimal point to the left; - * i.e., multiply by 10^-scale for the value to format. - * @return formatted value + * @param digits */ - private String formatBig(BigInteger bigInt, int scale) { - // TODO(jat): implement - return format(bigInt.doubleValue() * Math.pow(10, -scale)); + private void adjustFractionDigits(StringBuilder digits) { + // adjust fraction digits as required + int requiredDigits = decimalPosition + minimumFractionDigits; + if (digitsLength < requiredDigits) { + // add trailing zeros + while (digitsLength < requiredDigits) { + digits.append('0'); + ++digitsLength; + } + } else { + // remove excess trailing zeros + int toRemove = decimalPosition + maximumFractionDigits; + if (toRemove > digitsLength) { + toRemove = digitsLength; + } + while (toRemove > requiredDigits + && digits.charAt(toRemove - 1) == '0') { + --toRemove; + } + if (toRemove < digitsLength) { + digits.delete(toRemove, digitsLength); + digitsLength = toRemove; + } + } + } + + /** + * Compute the exponent to use and adjust decimal position if we are using + * exponential notation. + * + * @param digits + */ + private void computeExponent(StringBuilder digits) { + // always trim leading zeros + int strip = 0; + while (strip < digitsLength - 1 && digits.charAt(strip) == '0') { + ++strip; + } + if (strip > 0) { + digits.delete(0, strip); + digitsLength -= strip; + exponent -= strip; + } + + // decimal should wind up between minimum & maximumIntegerDigits + if (maximumIntegerDigits > minimumIntegerDigits + && maximumIntegerDigits > 0) { + // in this case, the exponent should be a multiple of + // maximumIntegerDigits and 1 <= decimal <= maximumIntegerDigits + exponent += decimalPosition - 1; + int remainder = exponent % maximumIntegerDigits; + if (remainder < 0) { + remainder += maximumIntegerDigits; + } + decimalPosition = remainder + 1; + exponent -= remainder; + } else { + exponent += decimalPosition - minimumIntegerDigits; + decimalPosition = minimumIntegerDigits; + } + + // special-case 0 to have an exponent of 0 + if (digitsLength == 1 && digits.charAt(0) == '0') { + exponent = 0; + decimalPosition = minimumIntegerDigits; + } + } + + /** + * Format a number with its significant digits already represented in string + * form. This is done so both double and BigInteger/Decimal formatting can + * share code without requiring all users to pay the code size penalty for + * BigDecimal/etc. + * <p> + * Example values passed in: + * <ul> + * <li>-13e2 + * <br>{...@code isNegative=true, digits="13", scale=2} + * <li>3.14158 + * <br>{...@code isNegative=false, digits="314158", scale=-5} + * <li>.0001 + * <br>{...@code isNegative=false, digits="1" ("0001" would be ok), scale=-4} + * </ul> + * + * @param isNegative true if the value to be formatted is negative + * @param digits a StringBuilder containing just the significant digits in + * the value to be formatted, the formatted result will be left here + * @param scale the number of places to the right the decimal point should + * be moved in the digit string -- negative means the value contains + * fractional digits + */ + private void format(boolean isNegative, StringBuilder digits, int scale) { + char decimalSeparator; + char groupingSeparator; + if (isCurrencyFormat) { + decimalSeparator = numberConstants.monetarySeparator().charAt(0); + groupingSeparator = numberConstants.monetaryGroupingSeparator().charAt(0); + } else { + decimalSeparator = numberConstants.decimalSeparator().charAt(0); + groupingSeparator = numberConstants.groupingSeparator().charAt(0); + } + + // Set these transient fields, which will be adjusted/used by the routines + // called in this method. + exponent = 0; + digitsLength = digits.length(); + decimalPosition = digitsLength + scale; + + boolean useExponent = this.useExponentialNotation; + int currentGroupingSize = this.groupingSize; + if (decimalPosition > 1024) { + // force really large numbers to be in exponential form + useExponent = true; + } + + if (useExponent) { + computeExponent(digits); + } + processLeadingZeros(digits); + roundValue(digits); + insertGroupingSeparators(digits, groupingSeparator, currentGroupingSize); + adjustFractionDigits(digits); + addZeroAndDecimal(digits, decimalSeparator); + if (useExponent) { + addExponent(digits); + } + char zeroChar = numberConstants.zeroDigit().charAt(0); + if (zeroChar != '0') { + localizeDigits(digits, zeroChar); + } + + // add prefix/suffix + digits.insert(0, isNegative ? negativePrefix : positivePrefix); + digits.append(isNegative ? negativeSuffix : positiveSuffix); } /** @@ -1011,47 +1248,39 @@ } /** - * This does the work of String.valueOf(long), but given a double as input - * and avoiding our emulated longs. Contrasted with String.valueOf(double), - * it ensures (a) there will be no trailing .0, and (b) unwinds E-notation. - * - * @param number the integral value to convert - * @return the string representing that integer + * Insert grouping separators if needed. + * + * @param digits + * @param groupingSeparator + * @param g */ - private String makeIntString(double number) { - String intPart = String.valueOf(number); - if (GWT.isScript()) { - return intPart; // JavaScript does the right thing for integral doubles - } - // ...but bytecode (hosted mode) does not... String.valueOf(double) will - // either end in .0 (non internationalized) which we don't want but is - // easy, or, for large numbers, it will be E-notation, which is annoying. - int digitLen = intPart.length(); - - if (intPart.charAt(digitLen - 2) == '.') { - return intPart.substring(0, digitLen - 2); - } - - // if we have E notation, (1) the exponent will be positive (else - // intValue is 0, which doesn't need E notation), and (2) there will - // be a radix dot (String.valueOf() isn't interationalized) - int radix = intPart.indexOf('.'); - int exp = intPart.indexOf('E'); - int digits = 0; - for (int i = exp + 1; i < intPart.length(); i++) { - digits = digits * 10 + (intPart.charAt(i) - '0'); - } - digits++; // exp of zero is one int digit... - StringBuffer newIntPart = new StringBuffer(); - newIntPart.append(intPart.substring(0, radix)); - newIntPart.append(intPart.substring(radix + 1, exp)); - while (newIntPart.length() < digits) { - newIntPart.append('0'); - } - newIntPart.setLength(digits); - return newIntPart.toString(); - } - + private void insertGroupingSeparators(StringBuilder digits, + char groupingSeparator, int g) { + if (g > 0) { + for (int i = g; i < decimalPosition; i += g + 1) { + digits.insert(decimalPosition - i, groupingSeparator); + ++decimalPosition; + ++digitsLength; + } + } + } + + /** + * Replace locale-independent digits with locale-specific ones. + * + * @param digits StringBuilder containing formatted number + * @param zero locale-specific zero character -- the rest of the digits must + * be consecutive + */ + private void localizeDigits(StringBuilder digits, char zero) { + for (int i = 0; i < digitsLength; ++i) { + char ch = digits.charAt(i); + if (ch >= '0' && ch <= '9') { + digits.setCharAt(i, (char) (ch - '0' + zero)); + } + } + } + /** * This method parses affix part of pattern. * @@ -1212,7 +1441,7 @@ ret = ret / scale; return ret; } - + /** * Method parses provided pattern, result is stored in member variables. * @@ -1379,141 +1608,88 @@ return pos - start; } - + /** - * This method formats a <code>double</code> in exponential format. + * Remove excess leading zeros or add some if we don't have enough. * - * @param number value need to be formated - * @param result where the formatted string goes + * @param digits + * @param zero */ - private void subformatExponential(double number, StringBuffer result) { - if (number == 0.0) { - subformatFixed(number, result, minimumIntegerDigits); - addExponentPart(0, result); - return; + private void processLeadingZeros(StringBuilder digits) { + // make sure we have enough trailing zeros + if (decimalPosition > digitsLength) { + while (digitsLength < decimalPosition) { + digits.append('0'); + ++digitsLength; + } } - int exponent = (int) Math.floor(Math.log(number) / Math.log(10)); - number /= Math.pow(10, exponent); - - int minIntDigits = minimumIntegerDigits; - if (maximumIntegerDigits > 1 && maximumIntegerDigits > minimumIntegerDigits) { - // A repeating range is defined; adjust to it as follows. - // If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3; - // -3,-4,-5=>-6, etc. This takes into account that the - // exponent we have here is off by one from what we expect; - // it is for the format 0.MMMMMx10^n. - while ((exponent % maximumIntegerDigits) != 0) { - number *= 10; - exponent--; - } - minIntDigits = 1; - } else { - // No repeating range is defined; use minimum integer digits. - if (minimumIntegerDigits < 1) { - exponent++; - number /= 10; - } else { - for (int i = 1; i < minimumIntegerDigits; i++) { - exponent--; - number *= 10; + if (!useExponentialNotation) { + // make sure we have the right number of leading zeros + if (decimalPosition < minimumIntegerDigits) { + // add leading zeros + StringBuilder prefix = new StringBuilder(); + while (decimalPosition < minimumIntegerDigits) { + prefix.append('0'); + ++decimalPosition; + ++digitsLength; + } + digits.insert(0, prefix); + } else if (decimalPosition > minimumIntegerDigits) { + // trim excess leading zeros + int strip = decimalPosition - minimumIntegerDigits; + for (int i = 0; i < strip; ++i) { + if (digits.charAt(i) != '0') { + strip = i; + break; + } + } + if (strip > 0) { + digits.delete(0, strip); + digitsLength -= strip; + decimalPosition -= strip; } } } - - subformatFixed(number, result, minIntDigits); - addExponentPart(exponent, result); } /** - * This method formats a <code>double</code> into a fractional - * representation. + * Propagate a carry from incrementing the {...@code i+1}'th digit. * - * @param number value need to be formated - * @param result result will be written here - * @param minIntDigits minimum integer digits + * @param digits + * @param i digit to start incrementing */ - private void subformatFixed(double number, StringBuffer result, - int minIntDigits) { - double power = Math.pow(10, maximumFractionDigits); - // Use 3 extra digits to allow us to do our own rounding since - // Java rounds up on .5 whereas some browsers might use 'round to even' - // or other rules. - - // There are cases where more digits would be required to get - // guaranteed results, but this at least makes such cases rarer. - String fixedString = toFixed(number, maximumFractionDigits + 3); - - double intValue = 0, fracValue = 0; - int exponentIndex = fixedString.indexOf('e'); - if (exponentIndex != -1) { - // Large numbers may be returned in exponential notation: such numbers - // are integers anyway - intValue = Math.floor(number); - } else { - int decimalIndex = fixedString.indexOf('.'); - int len = fixedString.length(); - if (decimalIndex == -1) { - decimalIndex = len; - } - if (decimalIndex > 0) { - intValue = Double.parseDouble(fixedString.substring(0, decimalIndex)); - } - if (decimalIndex < len - 1) { - fracValue = Double.parseDouble(fixedString.substring(decimalIndex + 1)); - fracValue = (((int) fracValue) + 500) / 1000; - if (fracValue >= power) { - fracValue -= power; - intValue++; - } + private void propagateCarry(StringBuilder digits, int i) { + boolean carry = true; + while (carry && i >= 0) { + char digit = digits.charAt(i); + if (digit == '9') { + // set this to zero and keep going + digits.setCharAt(i--, '0'); + } else { + digits.setCharAt(i, (char) (digit + 1)); + carry = false; } } - - boolean fractionPresent = (minimumFractionDigits > 0) || (fracValue > 0); - - String intPart = makeIntString(intValue); - String grouping = isCurrencyFormat - ? numberConstants.monetaryGroupingSeparator() - : numberConstants.groupingSeparator(); - String decimal = isCurrencyFormat ? numberConstants.monetarySeparator() - : numberConstants.decimalSeparator(); - - int zeroDelta = numberConstants.zeroDigit().charAt(0) - '0'; - int digitLen = intPart.length(); - - if (intValue > 0 || minIntDigits > 0) { - for (int i = digitLen; i < minIntDigits; i++) { - result.append(numberConstants.zeroDigit()); - } - - for (int i = 0; i < digitLen; i++) { - result.append((char) (intPart.charAt(i) + zeroDelta)); - - if (digitLen - i > 1 && groupingSize > 0 - && ((digitLen - i) % groupingSize == 1)) { - result.append(grouping); - } - } - } else if (!fractionPresent) { - // If there is no fraction present, and we haven't printed any - // integer digits, then print a zero. - result.append(numberConstants.zeroDigit()); - } - - // Output the decimal separator if we always do so. - if (decimalSeparatorAlwaysShown || fractionPresent) { - result.append(decimal); - } - - // To make sure it lead zero will be kept. - String fracPart = makeIntString(Math.floor(fracValue + power + 0.5d)); - int fracLen = fracPart.length(); - while (fracPart.charAt(fracLen - 1) == '0' && fracLen > minimumFractionDigits + 1) { - fracLen--; - } - - for (int i = 1; i < fracLen; i++) { - result.append((char) (fracPart.charAt(i) + zeroDelta)); + if (carry) { + // ran off the front, prepend a 1 + digits.insert(0, '1'); + ++decimalPosition; + ++digitsLength; + } + } + + /** + * Round the value at the requested place, propagating any carry backward. + * + * @param digits + */ + private void roundValue(StringBuilder digits) { + // TODO(jat): other rounding modes? + if (digitsLength > decimalPosition + maximumFractionDigits + && digits.charAt(decimalPosition + maximumFractionDigits) >= '5') { + int i = decimalPosition + maximumFractionDigits - 1; + propagateCarry(digits, i); } } } ======================================= --- /changes/jat/bigdecimal/user/super/com/google/gwt/emul/java/math/BigDecimal.java Fri Dec 11 21:30:01 2009 +++ /changes/jat/bigdecimal/user/super/com/google/gwt/emul/java/math/BigDecimal.java Thu Dec 17 14:28:24 2009 @@ -962,17 +962,17 @@ return add(augend).round(mc); } // Cases where there is room for optimizations - if (this.aproxPrecision() < diffScale - 1) { + if (this.approxPrecision() < diffScale - 1) { larger = augend; smaller = this; - } else if (augend.aproxPrecision() < -diffScale - 1) { + } else if (augend.approxPrecision() < -diffScale - 1) { larger = this; smaller = augend; } else { // No optimization is done return add(augend).round(mc); } - if (mc.getPrecision() >= larger.aproxPrecision()) { + if (mc.getPrecision() >= larger.approxPrecision()) { // No optimization is done return add(augend).round(mc); } @@ -1030,7 +1030,7 @@ : (smallValue > val.smallValue) ? 1 : 0; } double diffScale = this.scale - val.scale; - double diffPrecision = this.aproxPrecision() - val.aproxPrecision(); + double diffPrecision = this.approxPrecision() - val.approxPrecision(); if (diffPrecision > diffScale + 1) { return thisSign; } else if (diffPrecision < diffScale - 1) { @@ -1258,8 +1258,8 @@ * Calculating how many zeros must be append to 'dividend' to obtain a * quotient with at least 'mc.precision()' digits */ - double traillingZeros = mc.getPrecision() + 2L + divisor.aproxPrecision() - - aproxPrecision(); + double traillingZeros = mc.getPrecision() + 2L + divisor.approxPrecision() + - approxPrecision(); double diffScale = scale - divisor.scale; double newScale = diffScale; // scale of the final quotient int compRem; // to compare the remainder @@ -1401,7 +1401,7 @@ // math.04=Division by zero throw new ArithmeticException("Division by zero"); //$NON-NLS-1$ } - if ((divisor.aproxPrecision() + newScale > this.aproxPrecision() + 1L) + if ((divisor.approxPrecision() + newScale > this.approxPrecision() + 1L) || (this.isZero())) { /* * If the divisor's integer part is greater than this's integer part, the @@ -1780,7 +1780,7 @@ * If scale <= -32 there are at least 32 trailing bits zero in 10^(-scale). * If the scale is positive and very large the long value could be zero. */ - return ((scale <= -32) || (scale > aproxPrecision()) ? 0 + return ((scale <= -32) || (scale > approxPrecision()) ? 0 : toBigInteger().intValue()); } @@ -1810,7 +1810,7 @@ * If scale <= -64 there are at least 64 trailing bits zero in 10^(-scale). * If the scale is positive and very large the long value could be zero. */ - return ((scale <= -64) || (scale > aproxPrecision()) ? 0L + return ((scale <= -64) || (scale > approxPrecision()) ? 0L : toBigInteger().longValue()); } @@ -2427,10 +2427,10 @@ return subtract(subtrahend).round(mc); } // Now: this != 0 and subtrahend != 0 - if (subtrahend.aproxPrecision() < diffScale - 1) { + if (subtrahend.approxPrecision() < diffScale - 1) { // Cases where it is unnecessary to subtract two numbers with very // different scales - if (mc.getPrecision() < this.aproxPrecision()) { + if (mc.getPrecision() < this.approxPrecision()) { thisSignum = this.signum(); if (thisSignum != subtrahend.signum()) { tempBI = Multiplication.multiplyByPositiveInt( @@ -2484,7 +2484,7 @@ // (scale > 0) BigInteger[] integerAndFraction; // An optimization before do a heavy division - if ((scale > aproxPrecision()) + if ((scale > approxPrecision()) || (scale > getUnscaledValue().getLowestSetBit())) { // math.08=Rounding necessary throw new ArithmeticException("Rounding necessary"); //$NON-NLS-1$ @@ -2717,9 +2717,9 @@ * * @return an approximation of {...@code precision()} value */ - private double aproxPrecision() { - return Math.floor(((precision > 0) ? precision - : ((this.bitLength - 1) * LOG10_2)) + 1); + private double approxPrecision() { + return (precision > 0) ? precision + : Math.floor((this.bitLength - 1) * LOG10_2) + 1; } private BigInteger getUnscaledValue() { @@ -2826,7 +2826,7 @@ */ private void inplaceRound(MathContext mc) { int mcPrecision = mc.getPrecision(); - if (aproxPrecision() - mcPrecision <= 0 || mcPrecision == 0) { + if (approxPrecision() - mcPrecision <= 0 || mcPrecision == 0) { return; } int discardedPrecision = precision() - mcPrecision; ======================================= --- /changes/jat/bigdecimal/user/super/com/google/gwt/emul/java/math/BigInteger.java Fri Dec 11 22:46:18 2009 +++ /changes/jat/bigdecimal/user/super/com/google/gwt/emul/java/math/BigInteger.java Thu Dec 17 14:28:24 2009 @@ -703,6 +703,7 @@ * * @return this {...@code BigInteger} as a double value */ + @Override public double doubleValue() { return Double.parseDouble(this.toString()); } @@ -1334,10 +1335,10 @@ /** * Returns a string containing a string representation of this {...@code - * BigInteger} with base radix. If {...@code radix < Character.MIN_RADIX} or - * {...@code radix > Character.MAX_RADIX} then a decimal representation is - * returned. The characters of the string representation are generated with - * method {...@code Character.forDigit}. + * BigInteger} with base radix. If {...@code radix} is less than + * {...@link Character#MIN_RADIX} or greater than {...@link Character#MAX_RADIX} + * then a decimal representation is returned. The characters of the string + * representation are generated with method {...@link Character#forDigit}. * * @param radix base to be used for the string representation. * @return a string representation of this with radix 10. ======================================= --- /changes/jat/bigdecimal/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java Tue Dec 15 23:07:53 2009 +++ /changes/jat/bigdecimal/user/test/com/google/gwt/emultest/java/math/BigDecimalArithmeticTest.java Thu Dec 17 14:28:24 2009 @@ -210,9 +210,21 @@ sum = val.add(BigDecimal.ZERO); assertEquals(val, sum); } + /** - * divideAndRemainder(BigDecimal) + * Test the the approximate scale is computed correctly. + * + * @see https://issues.apache.org/jira/browse/HARMONY-6406 + */ + public void testApproxScale() { + BigDecimal decVal = BigDecimal.TEN.multiply(new BigDecimal("0.1")); + int compare = decVal.compareTo(new BigDecimal("1.00")); + assertEquals(0, compare); + } + + /** + * divideAndRemainder(BigDecimal). */ public void testDivideAndRemainder1() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -233,7 +245,7 @@ } /** - * divideAndRemainder(BigDecimal) + * divideAndRemainder(BigDecimal). */ public void testDivideAndRemainder2() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -255,7 +267,7 @@ } /** - * divideAndRemainder(BigDecimal, MathContext) + * divideAndRemainder(BigDecimal, MathContext). */ public void testDivideAndRemainderMathContextDOWN() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -279,7 +291,7 @@ } /** - * divideAndRemainder(BigDecimal, MathContext) + * divideAndRemainder(BigDecimal, MathContext). */ public void testDivideAndRemainderMathContextUP() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -303,7 +315,7 @@ } /** - * Divide to BigDecimal + * Divide to BigDecimal. */ public void testDivideBigDecimal1() { String a = "-37361671119238118911893939591735"; @@ -320,7 +332,7 @@ } /** - * Divide to BigDecimal + * Divide to BigDecimal. */ public void testDivideBigDecimal2() { String a = "-37361671119238118911893939591735"; @@ -337,7 +349,7 @@ } /** - * divide(BigDecimal, MathContext) + * divide(BigDecimal, MathContext). */ public void testDivideBigDecimalScaleMathContextCEILING() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -357,7 +369,7 @@ } /** - * divide(BigDecimal, MathContext) + * divide(BigDecimal, MathContext). */ public void testDivideBigDecimalScaleMathContextDOWN() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -377,7 +389,7 @@ } /** - * divide(BigDecimal, MathContext) + * divide(BigDecimal, MathContext). */ public void testDivideBigDecimalScaleMathContextFLOOR() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -397,7 +409,7 @@ } /** - * divide(BigDecimal, MathContext) + * divide(BigDecimal, MathContext). */ public void testDivideBigDecimalScaleMathContextHALF_DOWN() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -417,7 +429,7 @@ } /** - * divide(BigDecimal, MathContext) + * divide(BigDecimal, MathContext). */ public void testDivideBigDecimalScaleMathContextHALF_EVEN() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -437,7 +449,7 @@ } /** - * divide(BigDecimal, MathContext) + * divide(BigDecimal, MathContext). */ public void testDivideBigDecimalScaleMathContextHALF_UP() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -457,7 +469,7 @@ } /** - * divide(BigDecimal, MathContext) + * divide(BigDecimal, MathContext). */ public void testDivideBigDecimalScaleMathContextUP() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -477,7 +489,7 @@ } /** - * divide(BigDecimal, scale, RoundingMode) + * divide(BigDecimal, scale, RoundingMode). */ public void testDivideBigDecimalScaleRoundingModeCEILING() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -495,7 +507,7 @@ } /** - * divide(BigDecimal, scale, RoundingMode) + * divide(BigDecimal, scale, RoundingMode). */ public void testDivideBigDecimalScaleRoundingModeDOWN() { String a = "-37361671119238118911893939591735"; @@ -513,7 +525,7 @@ } /** - * divide(BigDecimal, scale, RoundingMode) + * divide(BigDecimal, scale, RoundingMode). */ public void testDivideBigDecimalScaleRoundingModeFLOOR() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -531,7 +543,7 @@ } /** - * divide(BigDecimal, scale, RoundingMode) + * divide(BigDecimal, scale, RoundingMode). */ public void testDivideBigDecimalScaleRoundingModeHALF_DOWN() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -549,7 +561,7 @@ } /** - * divide(BigDecimal, scale, RoundingMode) + * divide(BigDecimal, scale, RoundingMode). */ public void testDivideBigDecimalScaleRoundingModeHALF_EVEN() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -567,7 +579,7 @@ } /** - * divide(BigDecimal, scale, RoundingMode) + * divide(BigDecimal, scale, RoundingMode). */ public void testDivideBigDecimalScaleRoundingModeHALF_UP() { String a = "3736186567876876578956958765675671119238118911893939591735"; @@ -587,7 +599,7 @@ } /** - * divide(BigDecimal, scale, RoundingMode) + * divide(BigDecimal, scale, RoundingMode). */ public void testDivideBigDecimalScaleRoundingModeUP() { String a = "-37361671119238118911893939591735"; @@ -605,7 +617,7 @@ } /** - * Divide by zero + * Divide by zero. */ public void testDivideByZero() { String a = "1231212478987482988429808779810457634781384756794987"; @@ -622,7 +634,7 @@ } /** - * Divide with invalid rounding mode + * Divide with invalid rounding mode. */ public void testDivideExceptionInvalidRM() { String a = "1231212478987482988429808779810457634781384756794987"; @@ -641,7 +653,7 @@ } /** - * Divide with ROUND_UNNECESSARY + * Divide with ROUND_UNNECESSARY. */ public void testDivideExceptionRM() { String a = "1231212478987482988429808779810457634781384756794987"; @@ -660,7 +672,7 @@ } /** - * Divide: local variable exponent is equal to zero + * Divide: local variable exponent is equal to zero. */ public void testDivideExpEqualsZero() { String a = "1231212478987482988429808779810457634781384756794987"; @@ -678,7 +690,7 @@ } /** - * Divide: local variable exponent is greater than zero + * Divide: local variable exponent is greater than zero. */ public void testDivideExpGreaterZero() { String a = "1231212478987482988429808779810457634781384756794987"; @@ -696,7 +708,7 @@ } /** - * Divide: local variable exponent is less than zero + * Divide: local variable exponent is less than zero. */ public void testDivideExpLessZero() { String a = "1231212478987482988429808779810457634781384756794987"; @@ -714,7 +726,7 @@ } /** - * BigDecimal.divide with a scale that's too large + * BigDecimal.divide with a scale that's too large. * * Regression test for HARMONY-6271 */ @@ -730,7 +742,7 @@ } /** - * Divide: remainder is zero + * Divide: remainder is zero. */ public void testDivideRemainderIsZero() { String a = "8311389578904553209874735431110"; @@ -748,7 +760,7 @@ } /** - * Divide: rounding mode is ROUND_CEILING, result is negative + * Divide: rounding mode is ROUND_CEILING, result is negative. */ public void testDivideRoundCeilingNeg() { String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; @@ -766,7 +778,7 @@ } /** - * Divide: rounding mode is ROUND_CEILING, result is positive + * Divide: rounding mode is ROUND_CEILING, result is positive. */ public void testDivideRoundCeilingPos() { String a = "92948782094488478231212478987482988429808779810457634781384756794987"; @@ -784,7 +796,7 @@ } /** - * Divide: rounding mode is ROUND_DOWN, result is negative + * Divide: rounding mode is ROUND_DOWN, result is negative. */ public void testDivideRoundDownNeg() { String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; @@ -801,7 +813,7 @@ } /** - * Divide: rounding mode is ROUND_DOWN, result is positive + * Divide: rounding mode is ROUND_DOWN, result is positive. */ public void testDivideRoundDownPos() { String a = "92948782094488478231212478987482988429808779810457634781384756794987"; @@ -818,7 +830,7 @@ } /** - * Divide: rounding mode is ROUND_FLOOR, result is negative + * Divide: rounding mode is ROUND_FLOOR, result is negative. */ public void testDivideRoundFloorNeg() { String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; @@ -836,7 +848,7 @@ } /** - * Divide: rounding mode is ROUND_FLOOR, result is positive + * Divide: rounding mode is ROUND_FLOOR, result is positive. */ public void testDivideRoundFloorPos() { String a = "92948782094488478231212478987482988429808779810457634781384756794987"; @@ -854,7 +866,8 @@ } /** - * Divide: rounding mode is ROUND_HALF_DOWN, result is negative; distance = -1 + * Divide: rounding mode is ROUND_HALF_DOWN, result is negative; + * distance = -1. */ public void testDivideRoundHalfDownNeg() { String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; @@ -872,7 +885,7 @@ } /** - * Divide: rounding mode is ROUND_HALF_DOWN, result is negative; distance = 1 + * Divide: rounding mode is ROUND_HALF_DOWN, result is negative; distance = 1. */ public void testDivideRoundHalfDownNeg1() { String a = "-92948782094488478231212478987482988798104576347813847567949855464535634534563456"; @@ -890,7 +903,7 @@ } /** - * Divide: rounding mode is ROUND_HALF_UP, result is negative; equidistant + * Divide: rounding mode is ROUND_HALF_UP, result is negative; equidistant. */ public void testDivideRoundHalfDownNeg2() { String a = "-37361671119238118911893939591735"; @@ -908,7 +921,8 @@ } /** - * Divide: rounding mode is ROUND_HALF_DOWN, result is positive; distance = -1 + * Divide: rounding mode is ROUND_HALF_DOWN, result is positive; + * distance = -1. */ public void testDivideRoundHalfDownPos() { String a = "92948782094488478231212478987482988429808779810457634781384756794987"; @@ -926,7 +940,7 @@ } /** - * Divide: rounding mode is ROUND_HALF_DOWN, result is positive; distance = 1 + * Divide: rounding mode is ROUND_HALF_DOWN, result is positive; distance = 1. */ public void testDivideRoundHalfDownPos1() { String a = "92948782094488478231212478987482988798104576347813847567949855464535634534563456"; @@ -944,7 +958,8 @@ } /** - * Divide: rounding mode is ROUND_HALF_EVEN, result is negative; distance = -1 + * Divide: rounding mode is ROUND_HALF_EVEN, result is negative; + * distance = -1. */ public void testDivideRoundHalfEvenNeg() { String a = "-92948782094488478231212478987482988429808779810457634781384756794987"; ======================================= --- /changes/jat/bigdecimal/user/test/com/google/gwt/emultest/java/math/BigIntegerConvertTest.java Fri Dec 11 22:46:18 2009 +++ /changes/jat/bigdecimal/user/test/com/google/gwt/emultest/java/math/BigIntegerConvertTest.java Thu Dec 17 14:28:24 2009 @@ -309,6 +309,34 @@ float aNumber = new BigInteger(a).floatValue(); assertEquals("incorrect value", aNumber, result, 1); } + + /** + * Convert a negative number to a float value. The value is near + * Float.MAX_VALUE, but since all calculation in JS is done in double + * precision, it can't be completely accurate about passing that value + * through. + */ + public void testFloatValueNearNegMaxValue() { + byte[] a = { + 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int aSign = -1; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertEquals(-3.4028235e38, aNumber, 1e31); + } + + /** + * Convert a positive number to a float value. The value is near + * Float.MAX_VALUE, but since all calculation in JS is done in double + * precision, it can't be completely accurate about passing that value + * through. + */ + public void testFloatValueNearPosMaxValue() { + byte[] a = { + 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int aSign = 1; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertEquals(3.4028235e38, aNumber, 1e31); + } /** * Convert a negative number to a float value. The number's bit length is less @@ -365,18 +393,6 @@ float aNumber = new BigInteger(aSign, a).floatValue(); assertEquals(Float.NEGATIVE_INFINITY, aNumber); } - - /** - * Convert a negative number to a float value. The exponent is 1023 and the - * mantissa is all 1s. The rounding bit is 0. The result is -Float.MAX_VALUE. - */ - public void testFloatValueNegMaxValue() { - byte[] a = { - 0, -1, -1, -1, 0, -1, -1, -8, -1, -1, -1, -1, -1, -1, -1, -1, -1}; - int aSign = -1; - float aNumber = new BigInteger(aSign, a).floatValue(); - assertEquals(-Float.MAX_VALUE, aNumber, 1e30); - } /** * Convert a positive number to a float value. Rounding is NOT needed. @@ -412,6 +428,30 @@ float aNumber = new BigInteger(aSign, a).floatValue(); assertEquals(result, aNumber, 1e19); } + + /** + * Convert a negative number beyond Float.MAX_VALUE, ensuring that it is + * converted to Float.NEGATIVE_INFINITY. + */ + public void testFloatValuePastNegMaxValue() { + byte[] a = { + 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int aSign = -1; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertEquals(Float.NEGATIVE_INFINITY, aNumber); + } + + /** + * Convert a positive number beyond Float.MAX_VALUE, ensuring that it is + * converted to Float.POSITIVE_INFINITY. + */ + public void testFloatValuePastPosMaxValue() { + byte[] a = { + 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int aSign = 1; + float aNumber = new BigInteger(aSign, a).floatValue(); + assertEquals(Float.POSITIVE_INFINITY, aNumber); + } /** * Convert a positive number to a float value. The number's length is less @@ -473,18 +513,6 @@ float aNumber = new BigInteger(aSign, a).floatValue(); assertEquals(result, aNumber, 1e31); } - - /** - * Convert a positive number to a float value. The exponent is 1023 and the - * mantissa is all 1s. The rounding bit is 0. The result is Float.MAX_VALUE. - */ - public void testFloatValuePosMaxValue() { - byte[] a = { - 0, -1, -1, -1, 0, -1, -1, -8, -1, -1, -1, -1, -1, -1, -1, -1, -1}; - int aSign = 1; - float aNumber = new BigInteger(aSign, a).floatValue(); - assertEquals(Float.MAX_VALUE, aNumber, 1e30); - } /** * Convert a positive number to a float value. Rounding is NOT needed. ======================================= --- /changes/jat/bigdecimal/user/test/com/google/gwt/i18n/client/NumberFormat_en_Test.java Mon Dec 14 08:34:56 2009 +++ /changes/jat/bigdecimal/user/test/com/google/gwt/i18n/client/NumberFormat_en_Test.java Thu Dec 17 14:28:24 2009 @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package com.google.gwt.i18n.client; import com.google.gwt.junit.client.GWTTestCase; @@ -22,7 +21,7 @@ import java.math.BigInteger; /** - * GWT JUnit tests must extend GWTTestCase. + * Test {...@link NumberFormat} in the {...@code en} locale. */ public class NumberFormat_en_Test extends GWTTestCase { @@ -55,9 +54,6 @@ assertEquals("R$123,456.79", str); } - /** - * Add as many tests as you like. - */ public void testBasicFormat() { String str = NumberFormat.getFormat("0.0000").format(123.45789179565757f); assertEquals("123.4579", str); @@ -72,21 +68,23 @@ String str = NumberFormat.getFormat("0.000").format(decVal); assertEquals("1000000000000000000000000.000", str); - // TODO(jat): enable when actually supported -// decVal = decVal.add(new BigDecimal(".1")); -// str = NumberFormat.getFormat("#,##0.000").format(decVal); -// assertEquals("1,000,000,000,000,000,000,000,000.100", str); - } + decVal = decVal.add(new BigDecimal(".1")); + str = NumberFormat.getFormat("#,##0.000").format(decVal); + assertEquals("1,000,000,000,000,000,000,000,000.100", str); + + decVal = new BigDecimal(".1499999999999999999999"); + str = NumberFormat.getFormat(".0").format(decVal); + assertEquals(".1", str); +} public void testBigInteger() { BigInteger intVal = new BigInteger("1000000000000000000000000"); String str = NumberFormat.getFormat("#,##0").format(intVal); assertEquals("1,000,000,000,000,000,000,000,000", str); - // TODO(jat): enable when actually supported -// intVal = intVal.add(BigInteger.ONE); -// str = NumberFormat.getFormat("#,##0").format(intVal); -// assertEquals("1,000,000,000,000,000,000,000,001", str); + intVal = intVal.add(BigInteger.ONE); + str = NumberFormat.getFormat("#,##0").format(intVal); + assertEquals("1,000,000,000,000,000,000,000,001", str); } public void testCurrency() { @@ -262,18 +260,6 @@ value = NumberFormat.getFormat("0E0").parse("1.2345E+4"); assertTrue(value == 12345.0); } - - public void testGrouping() { - String str; - - str = NumberFormat.getFormat("#,###").format(1234567890); - assertEquals("1,234,567,890", str); - str = NumberFormat.getFormat("#,####").format(1234567890); - assertEquals("12,3456,7890", str); - - str = NumberFormat.getFormat("#").format(1234567890); - assertEquals("1234567890", str); - } public void testForceLatin() { assertFalse(NumberFormat.forcedLatinDigits()); @@ -289,6 +275,47 @@ assertEquals("3.14", unforced.format(3.14)); } + public void testGrouping() { + String str; + + str = NumberFormat.getFormat("#,###").format(1234567890); + assertEquals("1,234,567,890", str); + str = NumberFormat.getFormat("#,####").format(1234567890); + assertEquals("12,3456,7890", str); + + str = NumberFormat.getFormat("#").format(1234567890); + assertEquals("1234567890", str); + } + + // See external issue 3140 + public void testLeadingZeros() { + String str; + + str = NumberFormat.getFormat("0,000,000,000.#").format(123456789.489123); + assertEquals("0,123,456,789.5", str); + + str = NumberFormat.getFormat("#,###.####").format(0.414014); + assertEquals("0.414", str); // why leading 0? + + str = NumberFormat.getFormat("#.####").format(0.414014); + assertEquals("0.414", str); // why leading 0? + + str = NumberFormat.getFormat("#.0###").format(0.414014); + assertEquals(".414", str); + + str = NumberFormat.getFormat("0.0###").format(0.414014); + assertEquals("0.414", str); + + str = NumberFormat.getFormat("0.####").format(0.414014); + assertEquals("0.414", str); + + str = NumberFormat.getFormat("0.0000").format(0.414014); + assertEquals("0.4140", str); + + str = NumberFormat.getFormat("#.0000").format(0.414014); + assertEquals(".4140", str); + } + public void testNegative() { String str; @@ -347,41 +374,6 @@ str = NumberFormat.getFormat("a''b#").format(123); assertEquals("a'b123", str); } - - public void testStandardFormat() { - String str; - - str = NumberFormat.getCurrencyFormat().format(1234.579); - assertEquals("$1,234.58", str); - str = NumberFormat.getDecimalFormat().format(1234.579); - assertEquals("1,234.579", str); - str = NumberFormat.getPercentFormat().format(1234.579); - assertEquals("123,458%", str); - str = NumberFormat.getScientificFormat().format(1234.579); - assertEquals("1E3", str); - } - - public void testZeros() { - String str; - - str = NumberFormat.getFormat("#.#").format(0); - assertEquals("0", str); - str = NumberFormat.getFormat("#.").format(0); - assertEquals("0.", str); - str = NumberFormat.getFormat(".#").format(0); - assertEquals(".0", str); - str = NumberFormat.getFormat("#").format(0); - assertEquals("0", str); - - str = NumberFormat.getFormat("#0.#").format(0); - assertEquals("0", str); - str = NumberFormat.getFormat("#0.").format(0); - assertEquals("0.", str); - str = NumberFormat.getFormat("#.0").format(0); - assertEquals(".0", str); - str = NumberFormat.getFormat("#").format(0); - assertEquals("0", str); - } public void testRounding() { String str; @@ -439,5 +431,69 @@ str = NumberFormat.getFormat("#0.0000000").format(1.23756789E-05); assertEquals("0.0000124", str); + + str = NumberFormat.getFormat("#,##,###,##0.00000000000").format(111.18); + assertEquals("111.18000000000", str); + } + + public void testStandardFormat() { + String str; + + str = NumberFormat.getCurrencyFormat().format(1234.579); + assertEquals("$1,234.58", str); + str = NumberFormat.getDecimalFormat().format(1234.579); + assertEquals("1,234.579", str); + str = NumberFormat.getPercentFormat().format(1234.579); + assertEquals("123,458%", str); + str = NumberFormat.getScientificFormat().format(1234.579); + assertEquals("1E3", str); + } + + public void testToScaledString() { + StringBuilder buf = new StringBuilder(); + int scale = NumberFormat.toScaledString(buf, .1); + String str = buf.toString(); + assertStartsWith("100", str.substring(str.length() + scale)); + assertAllZeros(str, str.length() + scale); + buf = new StringBuilder(); + scale = NumberFormat.toScaledString(buf, 12345e38); + str = buf.toString(); + assertStartsWith("12345", str); + assertEquals(43, scale + str.length()); + } + + public void testZeros() { + String str; + + str = NumberFormat.getFormat("#.#").format(0); + assertEquals("0", str); + str = NumberFormat.getFormat("#.").format(0); + assertEquals("0.", str); + str = NumberFormat.getFormat(".#").format(0); + assertEquals(".0", str); + str = NumberFormat.getFormat("#").format(0); + assertEquals("0", str); + + str = NumberFormat.getFormat("#0.#").format(0); + assertEquals("0", str); + str = NumberFormat.getFormat("#0.").format(0); + assertEquals("0.", str); + str = NumberFormat.getFormat("#.0").format(0); + assertEquals(".0", str); + str = NumberFormat.getFormat("#").format(0); + assertEquals("0", str); + } + + private void assertAllZeros(String str, int prefixLen) { + if (prefixLen > str.length()) { + prefixLen = str.length(); + } + for (int i = 0; i < prefixLen; ++i) { + assertEquals('0', str.charAt(i)); + } + } + + private void assertStartsWith(String prefix, String str) { + assertTrue(str + " does not start with " + prefix, str.startsWith(prefix)); } } -- http://groups.google.com/group/Google-Web-Toolkit-Contributors
