]> granicus.if.org Git - icu/commitdiff
ICU-13129 Changing default Parse MathContext to 34 digits instead of 16 digits.
authorShane Carr <shane@unicode.org>
Thu, 22 Jun 2017 23:26:48 +0000 (23:26 +0000)
committerShane Carr <shane@unicode.org>
Thu, 22 Jun 2017 23:26:48 +0000 (23:26 +0000)
X-SVN-Rev: 40194

icu4c/source/test/testdata/numberformattestspecification.txt
icu4j/main/classes/core/src/com/ibm/icu/impl/number/Parse.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java
icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java

index 9fb934914b108de4aaa1a20c38d3f69b95c187bf..5200bc83d6f70cba33811490936351c05690f18c 100644 (file)
@@ -1588,8 +1588,7 @@ set locale en
 set pattern 0%
 begin
 parse  output  breaks
-// S uses a MathContext of 16 digits when dividing by 100.
-9223372036854775807%   92233720368547758.07    S
+9223372036854775807%   92233720368547758.07
 
 
 
index b4be09621d13c1d7d0692c4be4dd64162fc97f6b..68b1c0cbffdadeafe1ca6af908774645a75412a0 100644 (file)
@@ -583,7 +583,7 @@ public class Parse {
 
       // We need to use a math context in order to prevent non-terminating decimal expansions.
       // This is only used when dividing by the multiplier.
-      MathContext mc = RoundingUtils.getMathContextOr16Digits(properties);
+      MathContext mc = RoundingUtils.getMathContextOr34Digits(properties);
 
       // Construct the output number.
       // This is the only step during fast-mode parsing that incurs object creations.
index 3994eb257370aaf327e16f473f4820422a201d7c..80ff29e3cc7435127d4bf72a6f713e51daccb627 100644 (file)
@@ -117,13 +117,13 @@ public class RoundingUtils {
   private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED =
       new MathContext[RoundingMode.values().length];
 
-  private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_16_DIGITS =
+  private static final MathContext[] MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS =
       new MathContext[RoundingMode.values().length];
 
   static {
-    for (int i = 0; i < MATH_CONTEXT_BY_ROUNDING_MODE_16_DIGITS.length; i++) {
+    for (int i = 0; i < MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS.length; i++) {
       MATH_CONTEXT_BY_ROUNDING_MODE_UNLIMITED[i] = new MathContext(0, RoundingMode.valueOf(i));
-      MATH_CONTEXT_BY_ROUNDING_MODE_16_DIGITS[i] = new MathContext(16);
+      MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS[i] = new MathContext(34);
     }
   }
 
@@ -147,18 +147,18 @@ public class RoundingUtils {
 
   /**
    * Gets the user-specified math context out of the property bag. If there is none, falls back to a
-   * math context with 16 digits of precision (the 64-bit IEEE 754R default) and the user-specified
+   * math context with 34 digits of precision (the 128-bit IEEE 754R default) and the user-specified
    * rounding mode, which defaults to HALF_EVEN (the IEEE 754R default).
    *
    * @param properties The property bag.
    * @return A {@link MathContext}. Never null.
    */
-  public static MathContext getMathContextOr16Digits(IBasicRoundingProperties properties) {
+  public static MathContext getMathContextOr34Digits(IBasicRoundingProperties properties) {
     MathContext mathContext = properties.getMathContext();
     if (mathContext == null) {
       RoundingMode roundingMode = properties.getRoundingMode();
       if (roundingMode == null) roundingMode = RoundingMode.HALF_EVEN;
-      mathContext = MATH_CONTEXT_BY_ROUNDING_MODE_16_DIGITS[roundingMode.ordinal()];
+      mathContext = MATH_CONTEXT_BY_ROUNDING_MODE_34_DIGITS[roundingMode.ordinal()];
     }
     return mathContext;
   }
index 705afa195bc7dc20937e3cf1b1923db9c184eee5..ad3698f5f00a25cfd288babf97c5712ccc0a21fc 100644 (file)
@@ -1241,11 +1241,14 @@ public class DecimalFormat extends NumberFormat {
   /**
    * {@icu} <strong>Rounding and Digit Limits:</strong> Sets the {@link java.math.MathContext} used
    * to round numbers. A "math context" encodes both a rounding mode and a number of significant
-   * digits.
-   *
-   * <p>This method is provided for users who require their output to conform to a standard math
-   * context. <strong>Most users should call {@link #setRoundingMode} and/or {@link
-   * #setMaximumSignificantDigits} instead of this method.</strong>
+   * digits. Most users should call {@link #setRoundingMode} and/or {@link
+   * #setMaximumSignificantDigits} instead of this method.
+   *
+   * <p>When formatting, since no division is ever performed, the default MathContext is unlimited
+   * significant digits. However, when division occurs during parsing to correct for percentages and
+   * multipliers, a MathContext of 34 digits, the IEEE 754R Decimal128 standard, is used by default.
+   * If you require more than 34 digits when parsing, you can set a custom MathContext using this
+   * method.
    *
    * @param mathContext The MathContext to use when rounding numbers.
    * @see java.math.MathContext
index 9fb934914b108de4aaa1a20c38d3f69b95c187bf..5200bc83d6f70cba33811490936351c05690f18c 100644 (file)
@@ -1588,8 +1588,7 @@ set locale en
 set pattern 0%
 begin
 parse  output  breaks
-// S uses a MathContext of 16 digits when dividing by 100.
-9223372036854775807%   92233720368547758.07    S
+9223372036854775807%   92233720368547758.07
 
 
 
index b6902196383710ab1b23ca35b6fada794e3fe4a2..3031c89344d596bf8735dcdec1d71a009cc40133 100644 (file)
@@ -4796,6 +4796,53 @@ public class NumberFormatTest extends TestFmwk {
         }
     }
 
+    @Test
+    public void TestSetMathContext() throws ParseException {
+        java.math.MathContext fourDigits = new java.math.MathContext(4);
+        java.math.MathContext unlimitedCeiling = new java.math.MathContext(0, RoundingMode.CEILING);
+
+        // Test rounding
+        DecimalFormat df = new DecimalFormat();
+        assertEquals("Default format", "9,876.543", df.format(9876.5432));
+        df.setMathContext(fourDigits);
+        assertEquals("Format with fourDigits", "9,877", df.format(9876.5432));
+        df.setMathContext(unlimitedCeiling);
+        assertEquals("Format with unlimitedCeiling", "9,876.544", df.format(9876.5432));
+
+        // Test multiplication
+        df = new DecimalFormat("0.000%");
+        assertEquals("Default multiplication", "12.001%", df.format(0.120011));
+        df.setMathContext(fourDigits);
+        assertEquals("Multiplication with fourDigits", "12.000%", df.format(0.120011));
+        df.setMathContext(unlimitedCeiling);
+        assertEquals("Multiplication with unlimitedCeiling", "12.002%", df.format(0.120011));
+
+        // Test simple division
+        df = new DecimalFormat("0%");
+        assertEquals("Default division", 0.12001, df.parse("12.001%").doubleValue());
+        df.setMathContext(fourDigits);
+        assertEquals("Division with fourDigits", 0.12, df.parse("12.001%").doubleValue());
+        df.setMathContext(unlimitedCeiling);
+        assertEquals("Division with unlimitedCeiling", 0.12001, df.parse("12.001%").doubleValue());
+
+        // Test extreme division
+        df = new DecimalFormat();
+        df.setMultiplier(1000000007); // prime number
+        String hugeNumberString = "9876543212345678987654321234567898765432123456789"; // 49 digits
+        BigInteger huge34Digits = new BigInteger("9876543143209876985185182338271622000000");
+        BigInteger huge4Digits = new BigInteger("9877000000000000000000000000000000000000");
+        assertEquals("Default extreme division", huge34Digits, df.parse(hugeNumberString));
+        df.setMathContext(fourDigits);
+        assertEquals("Extreme division with fourDigits", huge4Digits, df.parse(hugeNumberString));
+        df.setMathContext(unlimitedCeiling);
+        try {
+            df.parse(hugeNumberString);
+            fail("Extreme division with unlimitedCeiling should throw ArithmeticException");
+        } catch (ArithmeticException e) {
+            // expected
+        }
+    }
+
     @Test
     public void Test10436() {
         DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH);