]> granicus.if.org Git - icu/commitdiff
ICU-13513 The data-driven test is green :)
authorShane Carr <shane@unicode.org>
Sat, 16 Dec 2017 04:13:43 +0000 (04:13 +0000)
committerShane Carr <shane@unicode.org>
Sat, 16 Dec 2017 04:13:43 +0000 (04:13 +0000)
X-SVN-Rev: 40740

icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/DecimalMatcher.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/NanMatcher.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/NumberParserImpl.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/ParsedNumber.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/PercentMatcher.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/PermilleMatcher.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/ScientificMatcher.java
icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberParserTest.java

index efbefdca8fc1f780b570226d515228b2473c2bfd..c08f24b26322278fce0bd560349cf8bc4bff5bdd 100644 (file)
@@ -204,8 +204,9 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
   @Override
   public void adjustMagnitude(int delta) {
     if (precision != 0) {
-      scale += delta;
-      origDelta += delta;
+      // TODO: Math.addExact is not in 1.6 or 1.7
+      scale = Math.addExact(scale, delta);
+      origDelta = Math.addExact(origDelta, delta);
     }
   }
 
index 129519fa0288571c467bf11e5c659b62fc75caf6..b395ca7f273a25dc71e69c933dc13a3868a22909 100644 (file)
@@ -13,7 +13,6 @@ import com.ibm.icu.text.UnicodeSet;
  */
 public class DecimalMatcher implements NumberParseMatcher {
 
-
     public boolean requireGroupingMatch = false;
     public boolean decimalEnabled = true;
     public boolean groupingEnabled = true;
@@ -44,6 +43,10 @@ public class DecimalMatcher implements NumberParseMatcher {
 
     @Override
     public boolean match(StringSegment segment, ParsedNumber result) {
+        return match(segment, result, false);
+    }
+
+    public boolean match(StringSegment segment, ParsedNumber result, boolean negativeExponent) {
         assert frozen;
         if (result.seenNumber() && !isScientific) {
             // A number has already been consumed.
@@ -87,7 +90,13 @@ public class DecimalMatcher implements NumberParseMatcher {
             // If found, save it in the DecimalQuantity or scientific adjustment.
             if (digit >= 0) {
                 if (isScientific) {
-                    exponent = digit + exponent * 10;
+                    int nextExponent = digit + exponent * 10;
+                    if (nextExponent < exponent) {
+                        // Overflow
+                        exponent = Integer.MAX_VALUE;
+                    } else {
+                        exponent = nextExponent;
+                    }
                 } else {
                     if (result.quantity == null) {
                         result.quantity = new DecimalQuantity_DualStorageBCD();
@@ -132,7 +141,24 @@ public class DecimalMatcher implements NumberParseMatcher {
         }
 
         if (isScientific) {
-            result.quantity.adjustMagnitude(exponent);
+            boolean overflow = (exponent == Integer.MAX_VALUE);
+            if (!overflow) {
+                try {
+                    result.quantity.adjustMagnitude(negativeExponent ? -exponent : exponent);
+                } catch (ArithmeticException e) {
+                    overflow = true;
+                }
+            }
+            if (overflow) {
+                if (negativeExponent) {
+                    // Set to zero
+                    result.quantity.clear();
+                } else {
+                    // Set to infinity
+                    result.quantity = null;
+                    result.flags |= ParsedNumber.FLAG_INFINITY;
+                }
+            }
         } else if (result.quantity == null) {
             // No-op: strings that start with a separator without any other digits
         } else if (seenBothSeparators || (separator != -1 && decimalUniSet.contains(separator))) {
index 795b72996870675def5a748b6bd3817aca0fc149..61bca78769ab6be611336ad11ae1bc6d4aa8768d 100644 (file)
@@ -36,4 +36,9 @@ public class NanMatcher implements NumberParseMatcher {
         // No-op
     }
 
+    @Override
+    public String toString() {
+        return "<NanMatcher>";
+    }
+
 }
index 96d4dc6a2b025d288999998b2f2a3839c161bafa..36abbbc6aad1af789769463878dce52f5643f85e 100644 (file)
@@ -64,7 +64,7 @@ public class NumberParserImpl {
         parser.parse(input, true, result);
         ppos.setIndex(result.charsConsumed);
         if (result.charsConsumed > 0) {
-            return result.getDouble();
+            return result.getNumber();
         } else {
             return null;
         }
@@ -88,7 +88,7 @@ public class NumberParserImpl {
                 assert 0 != (result.flags & ParsedNumber.FLAG_HAS_DEFAULT_CURRENCY);
                 currency = CustomSymbolCurrency.resolve(properties.getCurrency(), symbols.getULocale(), symbols);
             }
-            return new CurrencyAmount(result.getDouble(), currency);
+            return new CurrencyAmount(result.getNumber(), currency);
         } else {
             return null;
         }
@@ -120,8 +120,7 @@ public class NumberParserImpl {
         /// CURRENCY MATCHER ///
         ////////////////////////
 
-        parseCurrency = parseCurrency || patternInfo.hasCurrencySign();
-        if (parseCurrency) {
+        if (parseCurrency || patternInfo.hasCurrencySign()) {
             parser.addMatcher(new CurrencyMatcher(locale));
         }
 
@@ -137,6 +136,8 @@ public class NumberParserImpl {
         }
         parser.addMatcher(new MinusSignMatcher());
         parser.addMatcher(new NanMatcher(symbols));
+        parser.addMatcher(new PercentMatcher());
+        parser.addMatcher(new PermilleMatcher());
         DecimalMatcher decimalMatcher = new DecimalMatcher();
         decimalMatcher.requireGroupingMatch = isStrict;
         decimalMatcher.groupingEnabled = properties.getGroupingSize() > 0;
index 02555f0778d14c5a42ad16925505389d5615fcbb..1d02c7c1af38fcea1a451e6cc5e7196c5331ed35 100644 (file)
@@ -2,6 +2,7 @@
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.impl.number.parse;
 
+import java.math.BigDecimal;
 import java.util.Comparator;
 
 import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
@@ -48,6 +49,7 @@ public class ParsedNumber {
     public static final int FLAG_HAS_DEFAULT_CURRENCY = 0x0010;
     public static final int FLAG_HAS_DECIMAL_SEPARATOR = 0x0020;
     public static final int FLAG_NAN = 0x0040;
+    public static final int FLAG_INFINITY = 0x0080;
 
     /** A Comparator that favors ParsedNumbers with the most chars consumed. */
     public static final Comparator<ParsedNumber> COMPARATOR = new Comparator<ParsedNumber>() {
@@ -87,16 +89,32 @@ public class ParsedNumber {
     }
 
     public boolean seenNumber() {
-        return quantity != null || 0 != (flags & FLAG_NAN);
+        return quantity != null || 0 != (flags & FLAG_NAN) || 0 != (flags & FLAG_INFINITY);
     }
 
-    public double getDouble() {
-        if (0 != (flags & FLAG_NAN)) {
-            return Double.NaN;
+    public Number getNumber() {
+        boolean sawNegative = 0 != (flags & FLAG_NEGATIVE);
+        boolean sawNaN = 0 != (flags & FLAG_NAN);
+        boolean sawInfinity = 0 != (flags & FLAG_INFINITY);
+
+        // Check for NaN, infinity, and -0.0
+        if (sawNaN) {
+          return Double.NaN;
+        }
+        if (sawInfinity) {
+          if (sawNegative) {
+            return Double.NEGATIVE_INFINITY;
+          } else {
+            return Double.POSITIVE_INFINITY;
+          }
         }
-        double d = quantity.toDouble();
+        if (quantity.isZero() && sawNegative) {
+          return -0.0;
+        }
+
+        BigDecimal d = quantity.toBigDecimal();
         if (0 != (flags & FLAG_NEGATIVE)) {
-            d = -d;
+            d = d.negate();
         }
         return d;
     }
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/PercentMatcher.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/PercentMatcher.java
new file mode 100644 (file)
index 0000000..e10ebb0
--- /dev/null
@@ -0,0 +1,40 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number.parse;
+
+import com.ibm.icu.text.UnicodeSet;
+
+/**
+ * @author sffc
+ *
+ */
+public class PercentMatcher extends SymbolMatcher {
+
+    public PercentMatcher() {
+        // FIXME
+        super("%", new UnicodeSet("[%]"));
+    }
+
+    @Override
+    protected boolean isDisabled(ParsedNumber result) {
+        return 0 != (result.flags & ParsedNumber.FLAG_PERCENT);
+    }
+
+    @Override
+    protected void accept(ParsedNumber result) {
+        result.flags |= ParsedNumber.FLAG_PERCENT;
+    }
+
+    @Override
+    public void postProcess(ParsedNumber result) {
+        super.postProcess(result);
+        if (0 != (result.flags & ParsedNumber.FLAG_PERCENT) && result.quantity != null) {
+            result.quantity.adjustMagnitude(-2);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "<PercentMatcher>";
+    }
+}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/PermilleMatcher.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/PermilleMatcher.java
new file mode 100644 (file)
index 0000000..c3b4d10
--- /dev/null
@@ -0,0 +1,40 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number.parse;
+
+import com.ibm.icu.text.UnicodeSet;
+
+/**
+ * @author sffc
+ *
+ */
+public class PermilleMatcher extends SymbolMatcher {
+
+    public PermilleMatcher() {
+        // FIXME
+        super("‰", new UnicodeSet("[‰]"));
+    }
+
+    @Override
+    protected boolean isDisabled(ParsedNumber result) {
+        return 0 != (result.flags & ParsedNumber.FLAG_PERMILLE);
+    }
+
+    @Override
+    protected void accept(ParsedNumber result) {
+        result.flags |= ParsedNumber.FLAG_PERMILLE;
+    }
+
+    @Override
+    public void postProcess(ParsedNumber result) {
+        super.postProcess(result);
+        if (0 != (result.flags & ParsedNumber.FLAG_PERMILLE) && result.quantity != null) {
+            result.quantity.adjustMagnitude(-3);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "<PermilleMatcher>";
+    }
+}
index 48032a59df18f6d766f54aedaeaa383d702c75cc..90b9b9a46c448bbb9e03633cc56b7c45997c8b7d 100644 (file)
@@ -47,18 +47,10 @@ public class ScientificMatcher implements NumberParseMatcher {
             }
 
             int digitsOffset = segment.getOffset();
-            int oldMagnitude = result.quantity.getMagnitude();
-            boolean digitsReturnValue = exponentMatcher.match(segment, result);
+            boolean digitsReturnValue = exponentMatcher.match(segment, result, minusSign);
             if (segment.getOffset() != digitsOffset) {
                 // At least one exponent digit was matched.
                 result.flags |= ParsedNumber.FLAG_HAS_EXPONENT;
-                if (minusSign) {
-                    // Switch the magnitude adjustment from positive to negative. Extracting the exponent from the
-                    // change in the quantity's magnitude feels a bit like a hack; better would be for the
-                    // DecimalMatcher to somehow return the exponent directly.
-                    int exponent = result.quantity.getMagnitude() - oldMagnitude;
-                    result.quantity.adjustMagnitude(-2 * exponent);
-                }
             } else {
                 // No exponent digits were matched; un-match the exponent separator.
                 segment.adjustOffset(-overlap1);
index ae7294b55f7d79129fad3868fe6ae7f73828da9e..3137c3d6107deec083d440c049a7414f4fea4cdf 100644 (file)
@@ -1425,8 +1425,8 @@ NaN       NaN     K
 1E2147483646   1E2147483646
 1E-2147483649  0
 1E-2147483648  0
-// S returns zero here
-1E-2147483647  1E-2147483647   S
+// S and P return zero here
+1E-2147483647  1E-2147483647   SP
 1E-2147483646  1E-2147483646
 
 test format push limits
@@ -1441,7 +1441,7 @@ maxFractionDigits format  output  breaks
 100    9999999999999.9950000000001     9999999999999.9950000000001     C
 2      9999999999999.9950000000001     10000000000000.00       C
 2      9999999.99499999        9999999.99
-// K doesn't support halfDowm rounding mode?
+// K doesn't support halfDown rounding mode?
 2      9999999.995     9999999.99      K
 2      9999999.99500001        10000000.00
 100    56565656565656565656565656565656565656565656565656565656565656  56565656565656565656565656565656565656565656565656565656565656.00       C
@@ -1455,26 +1455,27 @@ set locale en
 set pattern #,##0
 begin
 parse  output  breaks
-// K and J return null; S and C return 99
- 9 9   9       CJKS
+// K and J return null; S, C, and P return 99
+ 9 9   9       CJKSP
 // K returns null
  9 999 9999    K
 
 test parse ignorables
 set locale ar
 // Note: Prefixes contain RLMs, as do some of the test cases.
+// P does not work on most of these right now.
 set pattern x a‎b0c df 
 set negativePrefix y g‎h
 set negativeSuffix i jk 
 begin
 parse  output  breaks
-x a‎b56c df  56
-x  a‎b56c df         56      K
-x ab56c df     56      K
-x ab56c df     56      JK
-x ab56c df     56      K
-x ab56 56      JK
-x a b56        56      JK
+x a‎b56c df  56      P
+x  a‎b56c df         56      KP
+x ab56c df     56      KP
+x ab56c df     56      JKP
+x ab56c df     56      KP
+x ab56 56      JKP
+x a b56        56      JKP
 56cdf  56      JK
 56c df 56      JK
 56cd f 56      JK
@@ -1482,24 +1483,24 @@ x a b56 56      JK
 56cdf  56      JK
 56c d‎f      56      JK
 56‎c df      56      JK
-y g‎h56i jk  -56
-y  g‎h56i jk         -56     K
-y gh56i jk     -56     K
-y gh56i jk     -56     JK
-y gh56i jk     -56     K
-y gh56 -56     JK
-y g h56        -56     JK
+y g‎h56i jk  -56     P
+y  g‎h56i jk         -56     KP
+y gh56i jk     -56     KP
+y gh56i jk     -56     JKP
+y gh56i jk     -56     KP
+y gh56 -56     JKP
+y g h56        -56     JKP
 // S stops parsing after the 'i' for these and returns -56
 // C stops before the 'i' and gets 56
 56ijk  -56     CJK
-56i jk -56     CJK
-56ij k -56     CJK
-56i‎j‎k    -56     CJK
+56i jk -56     CJKP
+56ij k -56     CJKP
+56i‎j‎k    -56     CJKP
 56ijk  -56     CJK
-56i j‎k      -56     CJK
-56‎i jk      -56     CJK
+56i j‎k      -56     CJKP
+56‎i jk      -56     CJKP
 // S and C get 56 (accepts ' ' gs grouping); J and K get null
-5 6    fail    CS
+5 6    fail    CSP
 5‎6  5       JK
 
 test parse spaces in grouping
@@ -1509,9 +1510,9 @@ set locale en
 set pattern #,##0
 begin
 parse  output  breaks
-// C, J and S get "12" here
-1 2    1       CJS
-1 23   1       CJS
+// C, J, S, and P get "12" here
+1 2    1       CJSP
+1 23   1       CJSP
 // K gets 1 here; doesn't pick up the grouping separator
 1 234  1234    K
 
@@ -1545,7 +1546,8 @@ begin
 parse  output  breaks
 55%    0.55
 // J and K get null
-55     0.55    JK
+// P requires the symbol to be present and gets 55
+55     0.55    JKP
 
 test trailing grouping separators in pattern
 // This test is for #13115
index ee7a73cbc9b6321cbc9d9d7705da8ddf74b56138..cf32cb58be7aac9bf401c2c7971607d0a1a905bd 100644 (file)
@@ -85,7 +85,7 @@ public class NumberParserTest {
                 ParsedNumber resultObject = new ParsedNumber();
                 parser.parse(input, true, resultObject);
                 assertNotNull(message, resultObject.quantity);
-                assertEquals(message, resultDouble, resultObject.getDouble(), 0.0);
+                assertEquals(message, resultDouble, resultObject.getNumber().doubleValue(), 0.0);
                 assertEquals(message, expectedCharsConsumed, resultObject.charsConsumed);
             }
 
@@ -94,7 +94,7 @@ public class NumberParserTest {
                 ParsedNumber resultObject = new ParsedNumber();
                 parser.parse(input, false, resultObject);
                 assertNotNull(message, resultObject.quantity);
-                assertEquals(message, resultDouble, resultObject.getDouble(), 0.0);
+                assertEquals(message, resultDouble, resultObject.getNumber().doubleValue(), 0.0);
                 assertEquals(message, expectedCharsConsumed, resultObject.charsConsumed);
             }
 
@@ -104,7 +104,7 @@ public class NumberParserTest {
                 ParsedNumber resultObject = new ParsedNumber();
                 parser.parse(input, true, resultObject);
                 assertNotNull(message, resultObject.quantity);
-                assertEquals(message, resultDouble, resultObject.getDouble(), 0.0);
+                assertEquals(message, resultDouble, resultObject.getNumber().doubleValue(), 0.0);
                 assertEquals(message, expectedCharsConsumed, resultObject.charsConsumed);
             }
         }