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

Reply via email to