]> granicus.if.org Git - icu/commitdiff
ICU-21675 Allow NaN and Infinity to appear in DecNum strings
authorShane F. Carr <shane@unicode.org>
Tue, 21 Sep 2021 20:09:45 +0000 (20:09 +0000)
committerShane F. Carr <shane@unicode.org>
Wed, 22 Sep 2021 17:34:20 +0000 (12:34 -0500)
See #1871

icu4c/source/i18n/number_decimalquantity.cpp
icu4c/source/i18n/number_decnum.h
icu4c/source/i18n/number_rounding.cpp
icu4c/source/i18n/number_skeletons.cpp
icu4c/source/i18n/number_utils.cpp
icu4c/source/test/intltest/numbertest_range.cpp
icu4c/source/test/intltest/numbertest_skeletons.cpp
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java

index 308c0e30e815db6ba76bde88890a8c1f85b22a35..6a2847b1c18f191f441a6bba58784670fb61f687 100644 (file)
@@ -539,7 +539,11 @@ void DecimalQuantity::_setToDecNum(const DecNum& decnum, UErrorCode& status) {
     if (decnum.isNegative()) {
         flags |= NEGATIVE_FLAG;
     }
-    if (!decnum.isZero()) {
+    if (decnum.isNaN()) {
+        flags |= NAN_FLAG;
+    } else if (decnum.isInfinity()) {
+        flags |= INFINITY_FLAG;
+    } else if (!decnum.isZero()) {
         readDecNumberToBcd(decnum);
         compact();
     }
index 3bb8d107807967b52e330a8e66993b47f4a028f5..94a0b31bcb5dd7c5df7b13b8ecbcb25f56ba888e 100644 (file)
@@ -56,6 +56,13 @@ class U_I18N_API DecNum : public UMemory {
 
     bool isZero() const;
 
+    /** Is infinity or NaN */
+    bool isSpecial() const;
+
+    bool isInfinity() const;
+
+    bool isNaN() const;
+
     void toString(ByteSink& output, UErrorCode& status) const;
 
     inline CharString toCharString(UErrorCode& status) const {
index 7059a2ecb94cb4fc6ed247954e01bc153d69fea6..877df63c8f68efebdde6982fa19d32b159e13cca 100644 (file)
@@ -13,6 +13,7 @@
 #include "double-conversion.h"
 #include "number_roundingutils.h"
 #include "number_skeletons.h"
+#include "number_decnum.h"
 #include "putilimp.h"
 #include "string_segment.h"
 
@@ -35,8 +36,10 @@ void number::impl::parseIncrementOption(const StringSegment &segment,
     // Utilize DecimalQuantity/decNumber to parse this for us.
     DecimalQuantity dq;
     UErrorCode localStatus = U_ZERO_ERROR;
-    dq.setToDecNumber({buffer.data(), buffer.length()}, localStatus);
-    if (U_FAILURE(localStatus)) {
+    DecNum decnum;
+    decnum.setTo({buffer.data(), buffer.length()}, localStatus);
+    dq.setToDecNum(decnum, localStatus);
+    if (U_FAILURE(localStatus) || decnum.isSpecial()) {
         // throw new SkeletonSyntaxException("Invalid rounding increment", segment, e);
         status = U_NUMBER_SKELETON_SYNTAX_ERROR;
         return;
index 9a2993fda9fc6a04ade85a23073238c886c11fb2..ba9eee6b5d023e4d61586d6f1bdfa71f16e53828 100644 (file)
@@ -1493,7 +1493,7 @@ void blueprint_helpers::parseScaleOption(const StringSegment& segment, MacroProp
     LocalPointer<DecNum> decnum(new DecNum(), status);
     if (U_FAILURE(status)) { return; }
     decnum->setTo({buffer.data(), buffer.length()}, status);
-    if (U_FAILURE(status)) {
+    if (U_FAILURE(status) || decnum->isSpecial()) {
         // This is a skeleton syntax error; don't let the low-level decnum error bubble up
         status = U_NUMBER_SKELETON_SYNTAX_ERROR;
         return;
index b93d3d0ea4b7a5a49ac17275dde4b78d13c752e9..ad7053214077b0d108f8bc0e0642fba6eb4f2780 100644 (file)
@@ -180,12 +180,6 @@ void DecNum::_setTo(const char* str, int32_t maxDigits, UErrorCode& status) {
         status = U_UNSUPPORTED_ERROR;
         return;
     }
-
-    // For consistency with Java BigDecimal, no support for DecNum that is NaN or Infinity!
-    if (decNumberIsSpecial(fData.getAlias())) {
-        status = U_UNSUPPORTED_ERROR;
-        return;
-    }
 }
 
 void
@@ -252,6 +246,18 @@ bool DecNum::isZero() const {
     return decNumberIsZero(fData.getAlias());
 }
 
+bool DecNum::isSpecial() const {
+    return decNumberIsSpecial(fData.getAlias());
+}
+
+bool DecNum::isInfinity() const {
+    return decNumberIsInfinite(fData.getAlias());
+}
+
+bool DecNum::isNaN() const {
+    return decNumberIsNaN(fData.getAlias());
+}
+
 void DecNum::toString(ByteSink& output, UErrorCode& status) const {
     if (U_FAILURE(status)) {
         return;
index a6bf83141434527a383be03dad1c918c82e30ad4..2f59f43ab6c67487879c9e2d5a304bab3faed9d4 100644 (file)
@@ -677,6 +677,8 @@ void NumberRangeFormatterTest::testNaNInfinity() {
     auto result4 = lnf.formatFormattableRange(uprv_getNaN(), 0, status);
     auto result5 = lnf.formatFormattableRange(0, uprv_getNaN(), status);
     auto result6 = lnf.formatFormattableRange(uprv_getNaN(), uprv_getNaN(), status);
+    auto result7 = lnf.formatFormattableRange({"1000", status}, {"Infinity", status}, status);
+    auto result8 = lnf.formatFormattableRange({"-Infinity", status}, {"NaN", status}, status);
 
     assertEquals("0 - inf", u"-∞ – 0", result1.toTempString(status));
     assertEquals("-inf - 0", u"0–∞", result2.toTempString(status));
@@ -684,6 +686,8 @@ void NumberRangeFormatterTest::testNaNInfinity() {
     assertEquals("NaN - 0", u"NaN–0", result4.toTempString(status));
     assertEquals("0 - NaN", u"0–NaN", result5.toTempString(status));
     assertEquals("NaN - NaN", u"~NaN", result6.toTempString(status));
+    assertEquals("1000 - inf", u"1,000–∞", result7.toTempString(status));
+    assertEquals("-inf - NaN", u"-∞ – NaN", result8.toTempString(status));
 }
 
 void NumberRangeFormatterTest::testPlurals() {
index 7be57f044adf9f03617d0ac6e38e050696429276..f09fb60c8c6f0fac8ba43cd5ddff9e023f65e809 100644 (file)
@@ -184,9 +184,11 @@ void NumberSkeletonTest::invalidTokens() {
             u"scientific/ee",
             u"precision-increment/xxx",
             u"precision-increment/NaN",
+            u"precision-increment/Infinity",
             u"precision-increment/0.1.2",
             u"scale/xxx",
             u"scale/NaN",
+            u"scale/Infinity",
             u"scale/0.1.2",
             u"scale/français", // non-invariant characters for C++
             u"currency/dummy",
index e563a9fbfb57364e263edcecea416156aaff53ef..6b99583a1c83d542f05014bbf8c2e51eb747c3db 100644 (file)
@@ -167,9 +167,11 @@ public class NumberSkeletonTest {
                 "scientific/ee",
                 "precision-increment/xxx",
                 "precision-increment/NaN",
+                "precision-increment/Infinity",
                 "precision-increment/0.1.2",
                 "scale/xxx",
                 "scale/NaN",
+                "scale/Infinity",
                 "scale/0.1.2",
                 "scale/français", // non-invariant characters for C++
                 "currency/dummy",