]> granicus.if.org Git - icu/commitdiff
ICU-21714 Use FixedDecimal instead of double for plural samples in tests
authorElango Cheran <elango@unicode.org>
Wed, 22 Sep 2021 05:21:58 +0000 (05:21 +0000)
committerElango <elango@unicode.org>
Thu, 23 Sep 2021 01:28:15 +0000 (18:28 -0700)
See #1875

icu4c/source/i18n/plurrule.cpp
icu4c/source/i18n/plurrule_impl.h
icu4c/source/test/intltest/plurults.cpp
icu4c/source/test/intltest/plurults.h
icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java

index ad950e1176e3ed3da262608f622012ac9e011551..d1918c4698138b96fe7adae7f1f921d49cfe1f36 100644 (file)
@@ -1881,8 +1881,8 @@ void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) {
 
 double FixedDecimal::getPluralOperand(PluralOperand operand) const {
     switch(operand) {
-        case PLURAL_OPERAND_N: return source;
-        case PLURAL_OPERAND_I: return static_cast<double>(intValue);
+        case PLURAL_OPERAND_N: return (exponent == 0 ? source : source * pow(10, exponent));
+        case PLURAL_OPERAND_I: return (double) longValue();
         case PLURAL_OPERAND_F: return static_cast<double>(decimalDigits);
         case PLURAL_OPERAND_T: return static_cast<double>(decimalDigitsWithoutTrailingZeros);
         case PLURAL_OPERAND_V: return visibleDecimalDigitCount;
@@ -1931,6 +1931,18 @@ UnicodeString FixedDecimal::toString() const {
     return UnicodeString(buffer, -1, US_INV);
 }
 
+double FixedDecimal::doubleValue() const {
+    return (isNegative ? -source : source) * pow(10, exponent);
+}
+
+int64_t FixedDecimal::longValue() const {
+    if (exponent == 0) {
+        return intValue;
+    } else {
+        return (long) (pow(10, exponent) * intValue);
+    }
+}
+
 
 PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) {
     fOpenStatus = status;
index 1a30096f2f49172f8e83dd9f34766ade85253b62..7274da58f0652c4b51686cc6bd50b8153b285a50 100644 (file)
@@ -329,6 +329,9 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject {
 
     UnicodeString toString() const;
 
+    double doubleValue() const;
+    int64_t longValue() const;
+
     double      source;
     int32_t     visibleDecimalDigitCount;
     int64_t     decimalDigits;
index df37bd20b70fc76fb92b79a0f493441075b01d32..9131d370317f4670e16c9fadab71b07991139269 100644 (file)
@@ -57,6 +57,8 @@ void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &na
     TESTCASE_AUTO(testGetAllKeywordValues);
     TESTCASE_AUTO(testScientificPluralKeyword);
     TESTCASE_AUTO(testCompactDecimalPluralKeyword);
+    TESTCASE_AUTO(testDoubleValue);
+    TESTCASE_AUTO(testLongValue);
     TESTCASE_AUTO(testOrdinal);
     TESTCASE_AUTO(testSelect);
     TESTCASE_AUTO(testSelectRange);
@@ -898,6 +900,94 @@ PluralRulesTest::testCompactDecimalPluralKeyword() {
     }
 }
 
+void
+PluralRulesTest::testDoubleValue() {
+    IcuTestErrorCode errorCode(*this, "testDoubleValue");
+
+    struct IntTestCase {
+        const int64_t inputNum;
+        const double expVal;
+    } intCases[] = {
+        {-101, -101.0},
+        {-100, -100.0},
+        {-1,   -1.0},
+        {0,     0.0},
+        {1,     1.0},
+        {100,   100.0}
+    };
+    for (const auto& cas : intCases) {
+        const int64_t inputNum = cas.inputNum;
+        const double expVal = cas.expVal;
+
+        FixedDecimal fd(inputNum);
+        UnicodeString message(u"FixedDecimal::doubleValue() for" + Int64ToUnicodeString(inputNum));
+        assertEquals(message, expVal, fd.doubleValue());
+    }
+
+    struct DoubleTestCase {
+        const double inputNum;
+        const double expVal;
+    } dblCases[] = {
+        {-0.0,     -0.0},
+        {0.1,       0.1},
+        {1.999,     1.999},
+        {2.0,       2.0},
+        {100.001, 100.001}
+    };
+    for (const auto & cas : dblCases) {
+        const double inputNum = cas.inputNum;
+        const double expVal = cas.expVal;
+
+        FixedDecimal fd(inputNum);
+        UnicodeString message(u"FixedDecimal::doubleValue() for" + DoubleToUnicodeString(inputNum));
+        assertEquals(message, expVal, fd.doubleValue());
+    }
+}
+
+void
+PluralRulesTest::testLongValue() {
+    IcuTestErrorCode errorCode(*this, "testLongValue");
+
+    struct IntTestCase {
+        const int64_t inputNum;
+        const int64_t expVal;
+    } intCases[] = {
+        {-101,  101},
+        {-100,  100},
+        {-1,    1},
+        {0,     0},
+        {1,     1},
+        {100,   100}
+    };
+    for (const auto& cas : intCases) {
+        const int64_t inputNum = cas.inputNum;
+        const int64_t expVal = cas.expVal;
+
+        FixedDecimal fd(inputNum);
+        UnicodeString message(u"FixedDecimal::longValue() for" + Int64ToUnicodeString(inputNum));
+        assertEquals(message, expVal, fd.longValue());
+    }
+
+    struct DoubleTestCase {
+        const double inputNum;
+        const int64_t expVal;
+    } dblCases[] = {
+        {-0.0,      0},
+        {0.1,       0},
+        {1.999,     1},
+        {2.0,       2},
+        {100.001,   100}
+    };
+    for (const auto & cas : dblCases) {
+        const double inputNum = cas.inputNum;
+        const int64_t expVal = cas.expVal;
+
+        FixedDecimal fd(inputNum);
+        UnicodeString message(u"FixedDecimal::longValue() for" + DoubleToUnicodeString(inputNum));
+        assertEquals(message, expVal, fd.longValue());
+    }
+}
+
 UnicodeString PluralRulesTest::getPluralKeyword(const LocalPointer<PluralRules> &rules, Locale locale, double number, const char16_t* skeleton) {
     IcuTestErrorCode errorCode(*this, "getPluralKeyword");
     UnlocalizedNumberFormatter ulnf = NumberFormatter::forSkeleton(skeleton, errorCode);
index 6a92225aec2d0bc4436e4245cf7402551cac35f4..76ecb6bf191bc758f48eb0986c39916233c0c74a 100644 (file)
@@ -36,6 +36,8 @@ private:
     void testWithin();
     void testGetAllKeywordValues();
     void testCompactDecimalPluralKeyword();
+    void testDoubleValue();
+    void testLongValue();
     void testScientificPluralKeyword();
     void testOrdinal();
     void testSelect();
index ba715bd225e06a1f6421cf4e98b1bde0d1bf5756..c9a2936c908b2dcbd58538751ac82828740f3110 100644 (file)
@@ -688,9 +688,7 @@ public class PluralRules implements Serializable {
             source = isNegative ? -n : n;
             visibleDecimalDigitCount = v;
             decimalDigits = f;
-            integerValue = n > MAX
-                    ? MAX
-                            : (long)n;
+            integerValue = n > MAX ? MAX : (long) source;
             int initExpVal = e;
             if (initExpVal == 0) {
                 initExpVal = c;
@@ -922,15 +920,15 @@ public class PluralRules implements Serializable {
         @Deprecated
         public double getPluralOperand(Operand operand) {
             switch(operand) {
-            case n: return source;
-            case i: return integerValue;
+            case n: return (exponent == 0 ? source : source * Math.pow(10, exponent));
+            case i: return intValue();
             case f: return decimalDigits;
             case t: return decimalDigitsWithoutTrailingZeros;
             case v: return visibleDecimalDigitCount;
             case w: return visibleDecimalDigitCountWithoutTrailingZeros;
             case e: return exponent;
             case c: return exponent;
-            default: return source;
+            default: return doubleValue();
             }
         }
 
@@ -1078,7 +1076,7 @@ public class PluralRules implements Serializable {
         @Deprecated
         public long getShiftedValue() {
             if (exponent != 0 && visibleDecimalDigitCount == 0 && decimalDigits == 0) {
-                // Need to taxe exponent into account if we have it
+                // Need to take exponent into account if we have it
                 return (long)(source * Math.pow(10, exponent));
             }
             return integerValue * baseFactor + decimalDigits;
index d4cb9bbfc0212534b20dd25a07b1d43d03454cf6..6aae8c27ed2e42cc69789d19b43a2e3ca4e5dabf 100644 (file)
@@ -465,21 +465,56 @@ public class PluralRulesTest extends TestFmwk {
     }
 
     public void checkValue(String title1, PluralRules rules, String expected, String value) {
-        double number = Double.parseDouble(value);
-        int decimalPos = value.indexOf('.') + 1;
-        int countVisibleFractionDigits;
-        int fractionaldigits;
-        if (decimalPos == 0) {
-            countVisibleFractionDigits = fractionaldigits = 0;
-        } else {
-            countVisibleFractionDigits = value.length() - decimalPos;
-            fractionaldigits = Integer.parseInt(value.substring(decimalPos));
-        }
-        String result = rules.select(number, countVisibleFractionDigits, fractionaldigits);
+        FixedDecimal fdNum = new FixedDecimal(value);
+
+        String result = rules.select(fdNum);
         ULocale locale = null;
         assertEquals(getAssertMessage(title1, locale, rules, expected) + "; value: " + value, expected, result);
     }
 
+    /**
+     * Check the testing helper method checkValue(), which parses a plural
+     * rule's sample string as a {@link FormattedNumber} in order to call
+     * {@code PluralRules.select(FormattedNumber)}, which in turn can support
+     * the exponent in plural sample numbers like 1e6 and 2.8c3.
+     */
+    @Test
+    public void testCheckValue() {
+        String ruleString =
+            "many: e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5"
+            + " @integer 1000000, 1e6, 2e6, 3e6, 4e6, 5e6, 6e6, …"
+            + " @decimal 1.0000001e6, 1.1e6, 2.0000001e6, 2.1e6, 3.0000001e6, 3.1e6, …;  "
+            + "one: i = 1 and v = 0"
+            + " @integer 1;  "
+            + "other: "
+            + " @integer 0, 2~16, 100, 1000, 10000, 100000, 1e3, 2e3, 3e3, 4e3, 5e3, 6e3, …"
+            + " @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 1.0001e3, 1.1e3, 2.0001e3, 2.1e3, 3.0001e3, 3.1e3, …";
+        PluralRules rules = PluralRules.createRules(ruleString);
+
+        Object[][] casesData = {
+                // expected category, value string
+                {"many",  "1000000"},
+                {"many",  "1e6"},
+                {"many",  "1.1e6"},
+                {"one",   "1"},
+                {"other", "0"},
+                {"other", "1e5"},
+                {"other", "100000"},
+                {"other", "0.0"},
+                {"other", "100000.0"},
+                {"other", "1000000.0"}
+        };
+
+        for (Object[] caseDatum : casesData) {
+            String expCategory = (String) caseDatum[0];
+            String inputValueStr = (String) caseDatum[1];
+
+            String msg = "checkValue(" + inputValueStr + ")";
+
+            checkValue(msg, rules, expCategory, inputValueStr);
+        }
+    }
+
     private static String[][] equalityTestData = {
             // once we add fractions, we had to retract the "test all possibilities" for equality,
             // so we only have a limited set of equality tests now.
@@ -1110,6 +1145,78 @@ public class PluralRulesTest extends TestFmwk {
         return pluralKeyword;
     }
 
+    @Test
+    public void testDoubleValue() {
+        Object[][] intCasesData = {
+                // source number, expected double value
+                {-101, -101.0},
+                {-100, -100.0},
+                {-1,   -1.0},
+                {0,     0.0},
+                {1,     1.0},
+                {100,   100.0}
+        };
+
+        for (Object[] caseDatum : intCasesData) {
+            double inputNum = (int) caseDatum[0];
+            double expVal = (double) caseDatum[1];
+            FixedDecimal fd = new FixedDecimal(inputNum);
+            assertEquals("FixedDecimal.doubleValue() for " + inputNum, expVal, fd.doubleValue());
+        }
+
+        Object[][] doubleCasesData = {
+                // source number, expected double value
+                {-0.0,     -0.0},
+                {0.1,       0.1},
+                {1.999,     1.999},
+                {2.0,       2.0},
+                {100.001, 100.001}
+        };
+
+        for (Object[] caseDatum : doubleCasesData) {
+            double inputNum = (double) caseDatum[0];
+            double expVal = (double) caseDatum[1];
+            FixedDecimal fd = new FixedDecimal(inputNum);
+            assertEquals("FixedDecimal.doubleValue() for " + inputNum, expVal, fd.doubleValue());
+        }
+    }
+
+    @Test
+    public void testLongValue() {
+        Object[][] intCasesData = {
+                // source number, expected double value
+                {-101,  101},
+                {-100,  100},
+                {-1,    1},
+                {0,     0},
+                {1,     1},
+                {100,   100}
+        };
+
+        for (Object[] caseDatum : intCasesData) {
+            long inputNum = (int) caseDatum[0];
+            long expVal = (int) caseDatum[1];
+            FixedDecimal fd = new FixedDecimal(inputNum);
+            assertEquals("FixedDecimal.longValue() for " + inputNum, expVal, fd.longValue());
+        }
+
+        Object[][] doubleCasesData = {
+                // source number, expected double value
+                {-0.0,      0},
+                {0.1,       0},
+                {1.999,     1},
+                {2.0,       2},
+                {100.001,   100}
+        };
+
+        for (Object[] caseDatum : doubleCasesData) {
+            double inputNum = (double) caseDatum[0];
+            long expVal = (int) caseDatum[1];
+            FixedDecimal fd = new FixedDecimal(inputNum);
+            assertEquals("FixedDecimal.longValue() for " + inputNum, expVal, fd.longValue());
+        }
+    }
+
     enum StandardPluralCategories {
         zero, one, two, few, many, other;
         /**