Revision: 7002 Author: [email protected] Date: Wed Nov 18 13:48:28 2009 Log: Improve NumberFormat.format to avoid weird rounding issues
Review by: jat http://code.google.com/p/google-web-toolkit/source/detail?r=7002 Modified: /trunk/user/src/com/google/gwt/i18n/client/NumberFormat.java /trunk/user/test/com/google/gwt/i18n/client/NumberFormat_en_Test.java ======================================= --- /trunk/user/src/com/google/gwt/i18n/client/NumberFormat.java Sun Oct 18 11:53:33 2009 +++ /trunk/user/src/com/google/gwt/i18n/client/NumberFormat.java Wed Nov 18 13:48:28 2009 @@ -580,6 +580,10 @@ return "\u00A0"; } + private static native String toFixed(double d, int digits) /*-{ + return d.toFixed(digits); + }-*/; + // The currency code. private final String currencyCode; @@ -1308,15 +1312,38 @@ */ private void subformatFixed(double number, StringBuffer result, int minIntDigits) { - // Round the number. double power = Math.pow(10, maximumFractionDigits); - double intValue = Math.floor(number); - // we don't want to use Math.round, 'cause that returns a long which JS - // then has to emulate... Math.floor(x + 0.5d) is defined to be equivalent - double fracValue = Math.floor((number - intValue) * power + 0.5d); - if (fracValue >= power) { - intValue += 1.0; - fracValue -= power; + // 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++; + } + } } boolean fractionPresent = (minimumFractionDigits > 0) || (fracValue > 0); ======================================= --- /trunk/user/test/com/google/gwt/i18n/client/NumberFormat_en_Test.java Sun Oct 18 11:53:33 2009 +++ /trunk/user/test/com/google/gwt/i18n/client/NumberFormat_en_Test.java Wed Nov 18 13:48:28 2009 @@ -357,4 +357,62 @@ str = NumberFormat.getFormat("#").format(0); assertEquals("0", str); } -} + + public void testRounding() { + String str; + + str = NumberFormat.getFormat("#0.##").format(0.555); + assertEquals("0.56", str); + + str = NumberFormat.getFormat("#.##").format(30.555); + assertEquals("30.56", str); + + str = NumberFormat.getFormat("#.00").format(0.997); + assertEquals("1.00", str); + + str = NumberFormat.getFormat("#.00").format(-0.997); + assertEquals("-1.00", str); + + str = NumberFormat.getFormat("#.00").format(27.997); + assertEquals("28.00", str); + + str = NumberFormat.getFormat("#.00").format(-27.997); + assertEquals("-28.00", str); + + str = NumberFormat.getFormat("#0.00000").format(1.23456789E-03); + assertEquals("0.00123", str); + + str = NumberFormat.getFormat("#0.0000000").format(1.23456789E-03); + assertEquals("0.0012346", str); + + str = NumberFormat.getFormat("#0.0000").format(1.2E-03); + assertEquals("0.0012", str); + + str = NumberFormat.getFormat("#0.000").format(1.2E-03); + assertEquals("0.001", str); + + str = NumberFormat.getFormat("#0.00").format(1.2E-03); + assertEquals("0.00", str); + + str = NumberFormat.getFormat("#0.0").format(1.2E-03); + assertEquals("0.0", str); + + str = NumberFormat.getFormat("#0.00").format(11.2E-03); + assertEquals("0.01", str); + + str = NumberFormat.getFormat("#0.00").format(111.2E-03); + assertEquals("0.11", str); + + str = NumberFormat.getFormat("#0.00").format(1111.2E-03); + assertEquals("1.11", str); + + str = NumberFormat.getFormat("#0.00000").format(1.23456789E-05); + assertEquals("0.00001", str); + + str = NumberFormat.getFormat("#0.0000000").format(1.23456789E-05); + assertEquals("0.0000123", str); + + str = NumberFormat.getFormat("#0.0000000").format(1.23756789E-05); + assertEquals("0.0000124", str); + } +} -- http://groups.google.com/group/Google-Web-Toolkit-Contributors
