ICU-21519 Add PluralOperand 'c' as alias to 'e', parse FixedDecimal strings
diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp
index 74547c1..7720955 100644
--- a/icu4c/source/i18n/number_decimalquantity.cpp
+++ b/icu4c/source/i18n/number_decimalquantity.cpp
@@ -273,6 +273,9 @@
return fractionCountWithoutTrailingZeros();
case PLURAL_OPERAND_E:
return static_cast<double>(getExponent());
+ case PLURAL_OPERAND_C:
+ // Plural operand `c` is currently an alias for `e`.
+ return static_cast<double>(getExponent());
default:
return std::abs(toDouble());
}
diff --git a/icu4c/source/i18n/plurrule.cpp b/icu4c/source/i18n/plurrule.cpp
index e1e1667..bfcb6fb 100644
--- a/icu4c/source/i18n/plurrule.cpp
+++ b/icu4c/source/i18n/plurrule.cpp
@@ -60,6 +60,7 @@
static const UChar PK_VAR_F[]={LOW_F,0};
static const UChar PK_VAR_T[]={LOW_T,0};
static const UChar PK_VAR_E[]={LOW_E,0};
+static const UChar PK_VAR_C[]={LOW_C,0};
static const UChar PK_VAR_V[]={LOW_V,0};
static const UChar PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0};
static const UChar PK_DECIMAL[]={LOW_D,LOW_E,LOW_C,LOW_I,LOW_M,LOW_A,LOW_L,0};
@@ -421,7 +422,6 @@
destFd[sampleCount++] = fixed;
}
} else {
-
FixedDecimal fixedLo(sampleRange.tempSubStringBetween(0, tildeIndex), status);
FixedDecimal fixedHi(sampleRange.tempSubStringBetween(tildeIndex+1), status);
double rangeLo = fixedLo.source;
@@ -514,6 +514,7 @@
if (rc == nullptr) {
return 0;
}
+
int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, nullptr, dest, destCapacity, status);
if (numSamples == 0) {
numSamples = getSamplesFromString(rc->fDecimalSamples, nullptr, dest, destCapacity, status);
@@ -706,6 +707,7 @@
case tVariableF:
case tVariableT:
case tVariableE:
+ case tVariableC:
case tVariableV:
U_ASSERT(curAndConstraint != nullptr);
curAndConstraint->digitsType = type;
@@ -1092,6 +1094,8 @@
s.append(LOW_T); break;
case tVariableE:
s.append(LOW_E); break;
+ case tVariableC:
+ s.append(LOW_C); break;
default:
s.append(TILDE);
}
@@ -1269,6 +1273,7 @@
case tVariableF:
case tVariableT:
case tVariableE:
+ case tVariableC:
case tVariableV:
if (type != tIs && type != tMod && type != tIn &&
type != tNot && type != tWithin && type != tEqual && type != tNotEqual) {
@@ -1286,6 +1291,7 @@
type == tVariableF ||
type == tVariableT ||
type == tVariableE ||
+ type == tVariableC ||
type == tVariableV ||
type == tAt)) {
status = U_UNEXPECTED_TOKEN;
@@ -1318,6 +1324,7 @@
type != tVariableF &&
type != tVariableT &&
type != tVariableE &&
+ type != tVariableC &&
type != tVariableV) {
status = U_UNEXPECTED_TOKEN;
}
@@ -1497,6 +1504,8 @@
keyType = tVariableT;
} else if (0 == token.compare(PK_VAR_E, 1)) {
keyType = tVariableE;
+ } else if (0 == token.compare(PK_VAR_C, 1)) {
+ keyType = tVariableC;
} else if (0 == token.compare(PK_VAR_V, 1)) {
keyType = tVariableV;
} else if (0 == token.compare(PK_IS, 2)) {
@@ -1596,11 +1605,17 @@
return PLURAL_OPERAND_T;
case tVariableE:
return PLURAL_OPERAND_E;
+ case tVariableC:
+ return PLURAL_OPERAND_E;
default:
UPRV_UNREACHABLE; // unexpected.
}
}
+FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e, int32_t c) {
+ init(n, v, f, e, c);
+}
+
FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f, int32_t e) {
init(n, v, f, e);
// check values. TODO make into unit test.
@@ -1642,16 +1657,30 @@
FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) {
CharString cs;
int32_t parsedExponent = 0;
+ int32_t parsedCompactExponent = 0;
int32_t exponentIdx = num.indexOf(u'e');
if (exponentIdx < 0) {
exponentIdx = num.indexOf(u'E');
}
+ int32_t compactExponentIdx = num.indexOf(u'c');
+ if (compactExponentIdx < 0) {
+ compactExponentIdx = num.indexOf(u'C');
+ }
+
if (exponentIdx >= 0) {
cs.appendInvariantChars(num.tempSubString(0, exponentIdx), status);
int32_t expSubstrStart = exponentIdx + 1;
parsedExponent = ICU_Utility::parseAsciiInteger(num, expSubstrStart);
}
+ else if (compactExponentIdx >= 0) {
+ cs.appendInvariantChars(num.tempSubString(0, compactExponentIdx), status);
+ int32_t expSubstrStart = compactExponentIdx + 1;
+ parsedCompactExponent = ICU_Utility::parseAsciiInteger(num, expSubstrStart);
+
+ parsedExponent = parsedCompactExponent;
+ exponentIdx = compactExponentIdx;
+ }
else {
cs.appendInvariantChars(num, status);
}
@@ -1706,13 +1735,20 @@
init(n, v, f, exponent);
}
-
void FixedDecimal::init(double n, int32_t v, int64_t f, int32_t e) {
+ // Currently, `c` is an alias for `e`
+ init(n, v, f, e, e);
+}
+
+void FixedDecimal::init(double n, int32_t v, int64_t f, int32_t e, int32_t c) {
isNegative = n < 0.0;
source = fabs(n);
_isNaN = uprv_isNaN(source);
_isInfinite = uprv_isInfinite(source);
exponent = e;
+ if (exponent == 0) {
+ exponent = c;
+ }
if (_isNaN || _isInfinite) {
v = 0;
f = 0;
@@ -1843,6 +1879,7 @@
case PLURAL_OPERAND_T: return static_cast<double>(decimalDigitsWithoutTrailingZeros);
case PLURAL_OPERAND_V: return visibleDecimalDigitCount;
case PLURAL_OPERAND_E: return exponent;
+ case PLURAL_OPERAND_C: return exponent;
default:
UPRV_UNREACHABLE; // unexpected.
}
@@ -1876,12 +1913,12 @@
UnicodeString FixedDecimal::toString() const {
char pattern[15];
char buffer[20];
- if (exponent == 0) {
- snprintf(pattern, sizeof(pattern), "%%.%df", visibleDecimalDigitCount);
- snprintf(buffer, sizeof(buffer), pattern, source);
- } else {
+ if (exponent != 0) {
snprintf(pattern, sizeof(pattern), "%%.%dfe%%d", visibleDecimalDigitCount);
snprintf(buffer, sizeof(buffer), pattern, source, exponent);
+ } else {
+ snprintf(pattern, sizeof(pattern), "%%.%df", visibleDecimalDigitCount);
+ snprintf(buffer, sizeof(buffer), pattern, source);
}
return UnicodeString(buffer, -1, US_INV);
}
diff --git a/icu4c/source/i18n/plurrule_impl.h b/icu4c/source/i18n/plurrule_impl.h
index 52af3a7..ccc4813 100644
--- a/icu4c/source/i18n/plurrule_impl.h
+++ b/icu4c/source/i18n/plurrule_impl.h
@@ -145,6 +145,7 @@
tVariableV,
tVariableT,
tVariableE,
+ tVariableC,
tDecimal,
tInteger,
tEOF
@@ -222,12 +223,21 @@
PLURAL_OPERAND_W,
/**
- * Suppressed exponent for compact notation (exponent needed in
- * scientific notation with compact notation to approximate i).
+ * Suppressed exponent for scientific notation (exponent needed in
+ * scientific notation to approximate i).
*/
PLURAL_OPERAND_E,
/**
+ * This operand is currently treated as an alias for `PLURAL_OPERAND_E`.
+ * In the future, it will represent:
+ *
+ * Suppressed exponent for compact notation (exponent needed in
+ * compact notation to approximate i).
+ */
+ PLURAL_OPERAND_C,
+
+ /**
* THIS OPERAND IS DEPRECATED AND HAS BEEN REMOVED FROM THE SPEC.
*
* <p>Returns the integer value, but will fail if the number has fraction digits.
@@ -280,8 +290,10 @@
* @param n the number, e.g. 12.345
* @param v The number of visible fraction digits, e.g. 3
* @param f The fraction digits, e.g. 345
- * @param e The exponent, e.g. 7 in 1.2e7 (for compact/scientific)
+ * @param e The exponent, e.g. 7 in 1.2e7, for scientific notation
+ * @param c Currently: an alias for param `e`.
*/
+ FixedDecimal(double n, int32_t v, int64_t f, int32_t e, int32_t c);
FixedDecimal(double n, int32_t v, int64_t f, int32_t e);
FixedDecimal(double n, int32_t v, int64_t f);
FixedDecimal(double n, int32_t);
@@ -302,6 +314,7 @@
int32_t getVisibleFractionDigitCount() const;
+ void init(double n, int32_t v, int64_t f, int32_t e, int32_t c);
void init(double n, int32_t v, int64_t f, int32_t e);
void init(double n, int32_t v, int64_t f);
void init(double n);
diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h
index 3285ff7..39e8885 100644
--- a/icu4c/source/test/intltest/numbertest.h
+++ b/icu4c/source/test/intltest/numbertest.h
@@ -201,7 +201,7 @@
void testToDouble();
void testMaxDigits();
void testNickelRounding();
- void testCompactDecimalSuppressedExponent();
+ void testScientificAndCompactSuppressedExponent();
void testSuppressedExponentUnchangedByInitialScaling();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
diff --git a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp
index 74b97a9..610df96 100644
--- a/icu4c/source/test/intltest/numbertest_decimalquantity.cpp
+++ b/icu4c/source/test/intltest/numbertest_decimalquantity.cpp
@@ -30,7 +30,7 @@
TESTCASE_AUTO(testToDouble);
TESTCASE_AUTO(testMaxDigits);
TESTCASE_AUTO(testNickelRounding);
- TESTCASE_AUTO(testCompactDecimalSuppressedExponent);
+ TESTCASE_AUTO(testScientificAndCompactSuppressedExponent);
TESTCASE_AUTO(testSuppressedExponentUnchangedByInitialScaling);
TESTCASE_AUTO_END;
}
@@ -478,8 +478,8 @@
status.expectErrorAndReset(U_FORMAT_INEXACT_ERROR);
}
-void DecimalQuantityTest::testCompactDecimalSuppressedExponent() {
- IcuTestErrorCode status(*this, "testCompactDecimalSuppressedExponent");
+void DecimalQuantityTest::testScientificAndCompactSuppressedExponent() {
+ IcuTestErrorCode status(*this, "testScientificAndCompactSuppressedExponent");
Locale ulocale("fr-FR");
struct TestCase {
@@ -489,53 +489,56 @@
int64_t expectedLong;
double expectedDouble;
const char16_t* expectedPlainString;
- int32_t expectedSuppressedExponent;
+ int32_t expectedSuppressedScientificExponent;
+ int32_t expectedSuppressedCompactExponent;
} cases[] = {
- // unlocalized formatter skeleton, input, string output, long output, double output, BigDecimal output, plain string, suppressed exponent
- {u"", 123456789, u"123 456 789", 123456789L, 123456789.0, u"123456789", 0},
- {u"compact-long", 123456789, u"123 millions", 123000000L, 123000000.0, u"123000000", 6},
- {u"compact-short", 123456789, u"123 M", 123000000L, 123000000.0, u"123000000", 6},
- {u"scientific", 123456789, u"1,234568E8", 123456800L, 123456800.0, u"123456800", 8},
+ // unlocalized formatter skeleton, input, string output, long output,
+ // double output, BigDecimal output, plain string,
+ // suppressed scientific exponent, suppressed compact exponent
+ {u"", 123456789, u"123 456 789", 123456789L, 123456789.0, u"123456789", 0, 0},
+ {u"compact-long", 123456789, u"123 millions", 123000000L, 123000000.0, u"123000000", 6, 6},
+ {u"compact-short", 123456789, u"123 M", 123000000L, 123000000.0, u"123000000", 6, 6},
+ {u"scientific", 123456789, u"1,234568E8", 123456800L, 123456800.0, u"123456800", 8, 8},
- {u"", 1234567, u"1 234 567", 1234567L, 1234567.0, u"1234567", 0},
- {u"compact-long", 1234567, u"1,2 million", 1200000L, 1200000.0, u"1200000", 6},
- {u"compact-short", 1234567, u"1,2 M", 1200000L, 1200000.0, u"1200000", 6},
- {u"scientific", 1234567, u"1,234567E6", 1234567L, 1234567.0, u"1234567", 6},
+ {u"", 1234567, u"1 234 567", 1234567L, 1234567.0, u"1234567", 0, 0},
+ {u"compact-long", 1234567, u"1,2 million", 1200000L, 1200000.0, u"1200000", 6, 6},
+ {u"compact-short", 1234567, u"1,2 M", 1200000L, 1200000.0, u"1200000", 6, 6},
+ {u"scientific", 1234567, u"1,234567E6", 1234567L, 1234567.0, u"1234567", 6, 6},
- {u"", 123456, u"123 456", 123456L, 123456.0, u"123456", 0},
- {u"compact-long", 123456, u"123 mille", 123000L, 123000.0, u"123000", 3},
- {u"compact-short", 123456, u"123 k", 123000L, 123000.0, u"123000", 3},
- {u"scientific", 123456, u"1,23456E5", 123456L, 123456.0, u"123456", 5},
+ {u"", 123456, u"123 456", 123456L, 123456.0, u"123456", 0, 0},
+ {u"compact-long", 123456, u"123 mille", 123000L, 123000.0, u"123000", 3, 3},
+ {u"compact-short", 123456, u"123 k", 123000L, 123000.0, u"123000", 3, 3},
+ {u"scientific", 123456, u"1,23456E5", 123456L, 123456.0, u"123456", 5, 5},
- {u"", 123, u"123", 123L, 123.0, u"123", 0},
- {u"compact-long", 123, u"123", 123L, 123.0, u"123", 0},
- {u"compact-short", 123, u"123", 123L, 123.0, u"123", 0},
- {u"scientific", 123, u"1,23E2", 123L, 123.0, u"123", 2},
+ {u"", 123, u"123", 123L, 123.0, u"123", 0, 0},
+ {u"compact-long", 123, u"123", 123L, 123.0, u"123", 0, 0},
+ {u"compact-short", 123, u"123", 123L, 123.0, u"123", 0, 0},
+ {u"scientific", 123, u"1,23E2", 123L, 123.0, u"123", 2, 2},
- {u"", 1.2, u"1,2", 1L, 1.2, u"1.2", 0},
- {u"compact-long", 1.2, u"1,2", 1L, 1.2, u"1.2", 0},
- {u"compact-short", 1.2, u"1,2", 1L, 1.2, u"1.2", 0},
- {u"scientific", 1.2, u"1,2E0", 1L, 1.2, u"1.2", 0},
+ {u"", 1.2, u"1,2", 1L, 1.2, u"1.2", 0, 0},
+ {u"compact-long", 1.2, u"1,2", 1L, 1.2, u"1.2", 0, 0},
+ {u"compact-short", 1.2, u"1,2", 1L, 1.2, u"1.2", 0, 0},
+ {u"scientific", 1.2, u"1,2E0", 1L, 1.2, u"1.2", 0, 0},
- {u"", 0.12, u"0,12", 0L, 0.12, u"0.12", 0},
- {u"compact-long", 0.12, u"0,12", 0L, 0.12, u"0.12", 0},
- {u"compact-short", 0.12, u"0,12", 0L, 0.12, u"0.12", 0},
- {u"scientific", 0.12, u"1,2E-1", 0L, 0.12, u"0.12", -1},
+ {u"", 0.12, u"0,12", 0L, 0.12, u"0.12", 0, 0},
+ {u"compact-long", 0.12, u"0,12", 0L, 0.12, u"0.12", 0, 0},
+ {u"compact-short", 0.12, u"0,12", 0L, 0.12, u"0.12", 0, 0},
+ {u"scientific", 0.12, u"1,2E-1", 0L, 0.12, u"0.12", -1, -1},
- {u"", 0.012, u"0,012", 0L, 0.012, u"0.012", 0},
- {u"compact-long", 0.012, u"0,012", 0L, 0.012, u"0.012", 0},
- {u"compact-short", 0.012, u"0,012", 0L, 0.012, u"0.012", 0},
- {u"scientific", 0.012, u"1,2E-2", 0L, 0.012, u"0.012", -2},
+ {u"", 0.012, u"0,012", 0L, 0.012, u"0.012", 0, 0},
+ {u"compact-long", 0.012, u"0,012", 0L, 0.012, u"0.012", 0, 0},
+ {u"compact-short", 0.012, u"0,012", 0L, 0.012, u"0.012", 0, 0},
+ {u"scientific", 0.012, u"1,2E-2", 0L, 0.012, u"0.012", -2, -2},
- {u"", 999.9, u"999,9", 999L, 999.9, u"999.9", 0},
- {u"compact-long", 999.9, u"1 millier", 1000L, 1000.0, u"1000", 3},
- {u"compact-short", 999.9, u"1 k", 1000L, 1000.0, u"1000", 3},
- {u"scientific", 999.9, u"9,999E2", 999L, 999.9, u"999.9", 2},
+ {u"", 999.9, u"999,9", 999L, 999.9, u"999.9", 0, 0},
+ {u"compact-long", 999.9, u"1 millier", 1000L, 1000.0, u"1000", 3, 3},
+ {u"compact-short", 999.9, u"1 k", 1000L, 1000.0, u"1000", 3, 3},
+ {u"scientific", 999.9, u"9,999E2", 999L, 999.9, u"999.9", 2, 2},
- {u"", 1000.0, u"1 000", 1000L, 1000.0, u"1000", 0},
- {u"compact-long", 1000.0, u"1 millier", 1000L, 1000.0, u"1000", 3},
- {u"compact-short", 1000.0, u"1 k", 1000L, 1000.0, u"1000", 3},
- {u"scientific", 1000.0, u"1E3", 1000L, 1000.0, u"1000", 3},
+ {u"", 1000.0, u"1 000", 1000L, 1000.0, u"1000", 0, 0},
+ {u"compact-long", 1000.0, u"1 millier", 1000L, 1000.0, u"1000", 3, 3},
+ {u"compact-short", 1000.0, u"1 k", 1000L, 1000.0, u"1000", 3, 3},
+ {u"scientific", 1000.0, u"1E3", 1000L, 1000.0, u"1000", 3, 3},
};
for (const auto& cas : cases) {
// test the helper methods used to compute plural operand values
@@ -550,18 +553,19 @@
int64_t actualLong = dq.toLong();
double actualDouble = dq.toDouble();
UnicodeString actualPlainString = dq.toPlainString();
- int32_t actualSuppressedExponent = dq.getExponent();
+ int32_t actualSuppressedScientificExponent = dq.getExponent();
+ int32_t actualSuppressedCompactExponent = dq.getExponent();
assertEquals(
u"formatted number " + cas.skeleton + u" toString: " + cas.input,
cas.expectedString,
actualString);
assertEquals(
- u"compact decimal " + cas.skeleton + u" toLong: " + cas.input,
+ u"formatted number " + cas.skeleton + u" toLong: " + cas.input,
cas.expectedLong,
actualLong);
assertDoubleEquals(
- u"compact decimal " + cas.skeleton + u" toDouble: " + cas.input,
+ u"formatted number " + cas.skeleton + u" toDouble: " + cas.input,
cas.expectedDouble,
actualDouble);
assertEquals(
@@ -569,36 +573,46 @@
cas.expectedPlainString,
actualPlainString);
assertEquals(
- u"compact decimal " + cas.skeleton + u" suppressed exponent: " + cas.input,
- cas.expectedSuppressedExponent,
- actualSuppressedExponent);
+ u"formatted number " + cas.skeleton + u" suppressed scientific exponent: " + cas.input,
+ cas.expectedSuppressedScientificExponent,
+ actualSuppressedScientificExponent);
+ assertEquals(
+ u"formatted number " + cas.skeleton + u" suppressed compact exponent: " + cas.input,
+ cas.expectedSuppressedCompactExponent,
+ actualSuppressedCompactExponent);
// test the actual computed values of the plural operands
double expectedNOperand = cas.expectedDouble;
double expectedIOperand = cas.expectedLong;
- double expectedEOperand = cas.expectedSuppressedExponent;
+ double expectedEOperand = cas.expectedSuppressedScientificExponent;
+ double expectedCOperand = cas.expectedSuppressedCompactExponent;
double actualNOperand = dq.getPluralOperand(PLURAL_OPERAND_N);
double actualIOperand = dq.getPluralOperand(PLURAL_OPERAND_I);
double actualEOperand = dq.getPluralOperand(PLURAL_OPERAND_E);
+ double actualCOperand = dq.getPluralOperand(PLURAL_OPERAND_C);
assertDoubleEquals(
- u"compact decimal " + cas.skeleton + u" n operand: " + cas.input,
+ u"formatted number " + cas.skeleton + u" n operand: " + cas.input,
expectedNOperand,
actualNOperand);
assertDoubleEquals(
- u"compact decimal " + cas.skeleton + u" i operand: " + cas.input,
+ u"formatted number " + cas.skeleton + u" i operand: " + cas.input,
expectedIOperand,
actualIOperand);
assertDoubleEquals(
- u"compact decimal " + cas.skeleton + " e operand: " + cas.input,
+ u"formatted number " + cas.skeleton + " e operand: " + cas.input,
expectedEOperand,
actualEOperand);
+ assertDoubleEquals(
+ u"formatted number " + cas.skeleton + " c operand: " + cas.input,
+ expectedCOperand,
+ actualCOperand);
}
}
void DecimalQuantityTest::testSuppressedExponentUnchangedByInitialScaling() {
- IcuTestErrorCode status(*this, "testCompactDecimalSuppressedExponent");
+ IcuTestErrorCode status(*this, "testSuppressedExponentUnchangedByInitialScaling");
Locale ulocale("fr-FR");
LocalizedNumberFormatter withLocale = NumberFormatter::withLocale(ulocale);
LocalizedNumberFormatter compactLong =
@@ -612,20 +626,22 @@
double expectedNOperand;
double expectedIOperand;
double expectedEOperand;
+ double expectedCOperand;
} cases[] = {
// input, compact long string output,
- // compact n operand, compact i operand, compact e operand
- {123456789, "123 millions", 123000000.0, 123000000.0, 6.0},
- {1234567, "1,2 million", 1200000.0, 1200000.0, 6.0},
- {123456, "123 mille", 123000.0, 123000.0, 3.0},
- {123, "123", 123.0, 123.0, 0.0},
+ // compact n operand, compact i operand, compact e operand,
+ // compact c operand
+ {123456789, "123 millions", 123000000.0, 123000000.0, 6.0, 6.0},
+ {1234567, "1,2 million", 1200000.0, 1200000.0, 6.0, 6.0},
+ {123456, "123 mille", 123000.0, 123000.0, 3.0, 3.0},
+ {123, "123", 123.0, 123.0, 0.0, 0.0},
};
for (const auto& cas : cases) {
FormattedNumber fnCompactScaled = compactScaled.formatInt(cas.input, status);
DecimalQuantity dqCompactScaled;
fnCompactScaled.getDecimalQuantity(dqCompactScaled, status);
- double compactScaledEOperand = dqCompactScaled.getPluralOperand(PLURAL_OPERAND_E);
+ double compactScaledCOperand = dqCompactScaled.getPluralOperand(PLURAL_OPERAND_C);
FormattedNumber fnCompact = compactLong.formatInt(cas.input, status);
DecimalQuantity dqCompact;
@@ -634,6 +650,7 @@
double compactNOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_N);
double compactIOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_I);
double compactEOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_E);
+ double compactCOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_C);
assertEquals(
u"formatted number " + Int64ToUnicodeString(cas.input) + " compactLong toString: ",
cas.expectedString,
@@ -650,14 +667,18 @@
u"compact decimal " + DoubleToUnicodeString(cas.input) + ", e operand vs. expected",
cas.expectedEOperand,
compactEOperand);
+ assertDoubleEquals(
+ u"compact decimal " + DoubleToUnicodeString(cas.input) + ", c operand vs. expected",
+ cas.expectedCOperand,
+ compactCOperand);
// By scaling by 10^3 in a locale that has words / compact notation
// based on powers of 10^3, we guarantee that the suppressed
// exponent will differ by 3.
assertDoubleEquals(
- u"decimal " + DoubleToUnicodeString(cas.input) + ", e operand for compact vs. compact scaled",
- compactEOperand + 3,
- compactScaledEOperand);
+ u"decimal " + DoubleToUnicodeString(cas.input) + ", c operand for compact vs. compact scaled",
+ compactCOperand + 3,
+ compactScaledCOperand);
}
}
diff --git a/icu4c/source/test/intltest/plurults.cpp b/icu4c/source/test/intltest/plurults.cpp
index 9610fb6..407e484 100644
--- a/icu4c/source/test/intltest/plurults.cpp
+++ b/icu4c/source/test/intltest/plurults.cpp
@@ -52,8 +52,10 @@
TESTCASE_AUTO(testGetSamples);
TESTCASE_AUTO(testGetFixedDecimalSamples);
TESTCASE_AUTO(testSamplesWithExponent);
+ TESTCASE_AUTO(testSamplesWithCompactNotation);
TESTCASE_AUTO(testWithin);
TESTCASE_AUTO(testGetAllKeywordValues);
+ TESTCASE_AUTO(testScientificPluralKeyword);
TESTCASE_AUTO(testCompactDecimalPluralKeyword);
TESTCASE_AUTO(testOrdinal);
TESTCASE_AUTO(testSelect);
@@ -404,7 +406,7 @@
double values[1000];
for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) {
if (uprv_strcmp(locales[i].getLanguage(), "fr") == 0 &&
- logKnownIssue("21299", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
+ logKnownIssue("21322", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
continue;
}
LocalPointer<PluralRules> rules(PluralRules::forLocale(locales[i], status));
@@ -465,7 +467,7 @@
FixedDecimal values[1000];
for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) {
if (uprv_strcmp(locales[i].getLanguage(), "fr") == 0 &&
- logKnownIssue("21299", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
+ logKnownIssue("21322", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
continue;
}
LocalPointer<PluralRules> rules(PluralRules::forLocale(locales[i], status));
@@ -551,6 +553,44 @@
checkNewSamples(description2, test2, u"other", u"@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …", FixedDecimal(2.0, 1));
}
+
+void PluralRulesTest::testSamplesWithCompactNotation() {
+ // integer samples
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString description(
+ u"one: i = 0,1 @integer 0, 1, 1c5 @decimal 0.0~1.5, 1.1c5; "
+ u"many: c = 0 and i != 0 and i % 1000000 = 0 and v = 0 or c != 0..5"
+ u" @integer 1000000, 2c6, 3c6, 4c6, 5c6, 6c6, 7c6, … @decimal 2.1c6, 3.1c6, 4.1c6, 5.1c6, 6.1c6, 7.1c6, …; "
+ u"other: @integer 2~17, 100, 1000, 10000, 100000, 2c5, 3c5, 4c5, 5c5, 6c5, 7c5, …"
+ u" @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1c5, 3.1c5, 4.1c5, 5.1c5, 6.1c5, 7.1c5, …"
+ );
+ LocalPointer<PluralRules> test(PluralRules::createRules(description, status));
+ if (U_FAILURE(status)) {
+ errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
+ return;
+ }
+ checkNewSamples(description, test, u"one", u"@integer 0, 1, 1c5", FixedDecimal(0));
+ checkNewSamples(description, test, u"many", u"@integer 1000000, 2c6, 3c6, 4c6, 5c6, 6c6, 7c6, …", FixedDecimal(1000000));
+ checkNewSamples(description, test, u"other", u"@integer 2~17, 100, 1000, 10000, 100000, 2c5, 3c5, 4c5, 5c5, 6c5, 7c5, …", FixedDecimal(2));
+
+ // decimal samples
+ status = U_ZERO_ERROR;
+ UnicodeString description2(
+ u"one: i = 0,1 @decimal 0.0~1.5, 1.1c5; "
+ u"many: c = 0 and i != 0 and i % 1000000 = 0 and v = 0 or c != 0..5"
+ u" @decimal 2.1c6, 3.1c6, 4.1c6, 5.1c6, 6.1c6, 7.1c6, …; "
+ u"other: @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1c5, 3.1c5, 4.1c5, 5.1c5, 6.1c5, 7.1c5, …"
+ );
+ LocalPointer<PluralRules> test2(PluralRules::createRules(description2, status));
+ if (U_FAILURE(status)) {
+ errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
+ return;
+ }
+ checkNewSamples(description2, test2, u"one", u"@decimal 0.0~1.5, 1.1c5", FixedDecimal(0, 1));
+ checkNewSamples(description2, test2, u"many", u"@decimal 2.1c6, 3.1c6, 4.1c6, 5.1c6, 6.1c6, 7.1c6, …", FixedDecimal::createWithExponent(2.1, 1, 6));
+ checkNewSamples(description2, test2, u"other", u"@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1c5, 3.1c5, 4.1c5, 5.1c5, 6.1c5, 7.1c5, …", FixedDecimal(2.0, 1));
+}
+
void PluralRulesTest::checkNewSamples(
UnicodeString description,
const LocalPointer<PluralRules> &test,
@@ -727,13 +767,77 @@
}
}
+// For the time being, the compact notation exponent operand `c` is an alias
+// for the scientific exponent operand `e` and compact notation.
+void
+PluralRulesTest::testScientificPluralKeyword() {
+ IcuTestErrorCode errorCode(*this, "testScientificPluralKeyword");
+
+ LocalPointer<PluralRules> rules(PluralRules::createRules(
+ u"one: i = 0,1 @integer 0, 1 @decimal 0.0~1.5; "
+ u"many: e = 0 and i % 1000000 = 0 and v = 0 or e != 0 .. 5; "
+ u"other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, "
+ u" @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …", errorCode));
+
+ if (U_FAILURE(errorCode)) {
+ errln("Couldn't instantiate plurals rules from string, with error = %s", u_errorName(errorCode));
+ return;
+ }
+
+ const char* localeName = "fr-FR";
+ Locale locale = Locale::createFromName(localeName);
+
+ struct TestCase {
+ const char16_t* skeleton;
+ const int input;
+ const char16_t* expectedFormattedOutput;
+ const char16_t* expectedPluralRuleKeyword;
+ } cases[] = {
+ // unlocalized formatter skeleton, input, string output, plural rule keyword
+ {u"", 0, u"0", u"one"},
+ {u"scientific", 0, u"0", u"one"},
+
+ {u"", 1, u"1", u"one"},
+ {u"scientific", 1, u"1", u"one"},
+
+ {u"", 2, u"2", u"other"},
+ {u"scientific", 2, u"2", u"other"},
+
+ {u"", 1000000, u"1 000 000", u"many"},
+ {u"scientific", 1000000, u"1 million", u"many"},
+
+ {u"", 1000001, u"1 000 001", u"other"},
+ {u"scientific", 1000001, u"1 million", u"many"},
+
+ {u"", 120000, u"1 200 000", u"other"},
+ {u"scientific", 1200000, u"1,2 millions", u"many"},
+
+ {u"", 1200001, u"1 200 001", u"other"},
+ {u"scientific", 1200001, u"1,2 millions", u"many"},
+
+ {u"", 2000000, u"2 000 000", u"many"},
+ {u"scientific", 2000000, u"2 millions", u"many"},
+ };
+ for (const auto& cas : cases) {
+ const char16_t* skeleton = cas.skeleton;
+ const int input = cas.input;
+ const char16_t* expectedPluralRuleKeyword = cas.expectedPluralRuleKeyword;
+
+ UnicodeString actualPluralRuleKeyword =
+ getPluralKeyword(rules, locale, input, skeleton);
+
+ UnicodeString message(UnicodeString(localeName) + u" " + DoubleToUnicodeString(input));
+ assertEquals(message, expectedPluralRuleKeyword, actualPluralRuleKeyword);
+ }
+}
+
void
PluralRulesTest::testCompactDecimalPluralKeyword() {
IcuTestErrorCode errorCode(*this, "testCompactDecimalPluralKeyword");
LocalPointer<PluralRules> rules(PluralRules::createRules(
u"one: i = 0,1 @integer 0, 1 @decimal 0.0~1.5; "
- u"many: e = 0 and i % 1000000 = 0 and v = 0 or e != 0 .. 5; "
+ u"many: c = 0 and i % 1000000 = 0 and v = 0 or c != 0 .. 5; "
u"other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, "
u" @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …", errorCode));
diff --git a/icu4c/source/test/intltest/plurults.h b/icu4c/source/test/intltest/plurults.h
index 8e220dc..927a24d 100644
--- a/icu4c/source/test/intltest/plurults.h
+++ b/icu4c/source/test/intltest/plurults.h
@@ -32,9 +32,11 @@
void testGetSamples();
void testGetFixedDecimalSamples();
void testSamplesWithExponent();
+ void testSamplesWithCompactNotation();
void testWithin();
void testGetAllKeywordValues();
void testCompactDecimalPluralKeyword();
+ void testScientificPluralKeyword();
void testOrdinal();
void testSelect();
void testSelectRange();
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
index bce3400..2d98cc2 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
@@ -265,6 +265,9 @@
return fractionCountWithoutTrailingZeros();
case e:
return getExponent();
+ case c:
+ // Plural operand `c` is currently an alias for `e`.
+ return getExponent();
default:
return Math.abs(toDouble());
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java
index b302b4e..870b947 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java
@@ -481,8 +481,8 @@
w,
/**
- * Suppressed exponent for compact notation (exponent needed in
- * scientific notation with compact notation to approximate i).
+ * Suppressed exponent for scientific notation (exponent needed in
+ * scientific notation to approximate i).
*
* @internal
* @deprecated This API is ICU internal only.
@@ -491,6 +491,19 @@
e,
/**
+ * This operand is currently treated as an alias for `PLURAL_OPERAND_E`.
+ * In the future, it will represent:
+ *
+ * Suppressed exponent for compact notation (exponent needed in
+ * compact notation to approximate i).
+ *
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ @Deprecated
+ c,
+
+ /**
* THIS OPERAND IS DEPRECATED AND HAS BEEN REMOVED FROM THE SPEC.
*
* <p>Returns the integer value, but will fail if the number has fraction digits.
@@ -657,10 +670,11 @@
* @param v number of digits to the right of the decimal place. e.g 1.00 = 2 25. = 0
* @param f Corresponds to f in the plural rules grammar.
* The digits to the right of the decimal place as an integer. e.g 1.10 = 10
- * @param e Suppressed exponent for scientific and compact notation
+ * @param e Suppressed exponent for scientific notation
+ * @param c Currently: an alias for param `e`
*/
@Deprecated
- public FixedDecimal(double n, int v, long f, int e) {
+ public FixedDecimal(double n, int v, long f, int e, int c) {
isNegative = n < 0;
source = isNegative ? -n : n;
visibleDecimalDigitCount = v;
@@ -668,7 +682,11 @@
integerValue = n > MAX
? MAX
: (long)n;
- exponent = e;
+ int initExpVal = e;
+ if (initExpVal == 0) {
+ initExpVal = c;
+ }
+ exponent = initExpVal;
hasIntegerValue = source == integerValue;
// check values. TODO make into unit test.
//
@@ -704,6 +722,15 @@
* @deprecated This API is ICU internal only.
*/
@Deprecated
+ public FixedDecimal(double n, int v, long f, int e) {
+ this(n, v, f, e, e);
+ }
+
+ /**
+ * @internal CLDR
+ * @deprecated This API is ICU internal only.
+ */
+ @Deprecated
public FixedDecimal(double n, int v, long f) {
this(n, v, f, 0);
}
@@ -848,8 +875,11 @@
*/
@Deprecated
private static FixedDecimal parseDecimalSampleRangeNumString(String num) {
- if (num.contains("e")) {
+ if (num.contains("e") || num.contains("c")) {
int ePos = num.lastIndexOf('e');
+ if (ePos < 0) {
+ ePos = num.lastIndexOf('c');
+ }
int expNumPos = ePos + 1;
String exponentStr = num.substring(expNumPos);
int exponent = Integer.parseInt(exponentStr);
@@ -890,6 +920,7 @@
case v: return visibleDecimalDigitCount;
case w: return visibleDecimalDigitCountWithoutTrailingZeros;
case e: return exponent;
+ case c: return exponent;
default: return source;
}
}
@@ -970,10 +1001,10 @@
@Override
public String toString() {
String baseString = String.format(Locale.ROOT, "%." + visibleDecimalDigitCount + "f", source);
- if (exponent == 0) {
- return baseString;
- } else {
+ if (exponent != 0) {
return baseString + "e" + exponent;
+ } else {
+ return baseString;
}
}
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java
index bca6fc9..b08f452 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java
@@ -219,6 +219,37 @@
new FixedDecimal(2.0, 1));
}
+ /**
+ * This test is for the support of X.YcZ compactnotation of numbers in
+ * the plural sample string.
+ */
+ @Test
+ public void testSamplesWithCompactNotation() {
+ String description = "one: i = 0,1 @integer 0, 1, 1c5 @decimal 0.0~1.5, 1.1c5; "
+ + "many: c = 0 and i != 0 and i % 1000000 = 0 and v = 0 or c != 0..5"
+ + " @integer 1000000, 2c6, 3c6, 4c6, 5c6, 6c6, 7c6, … @decimal 2.1c6, 3.1c6, 4.1c6, 5.1c6, 6.1c6, 7.1c6, …; "
+ + "other: @integer 2~17, 100, 1000, 10000, 100000, 2c5, 3c5, 4c5, 5c5, 6c5, 7c5, …"
+ + " @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1c5, 3.1c5, 4.1c5, 5.1c5, 6.1c5, 7.1c5, …"
+ ;
+ // Creating the PluralRules object means being able to parse numbers
+ // like 1c5 and 1.1c5.
+ // Note: Since `c` is currently an alias to `e`, the toString() of
+ // FixedDecimal will return "1e5" even when input is "1c5".
+ PluralRules test = PluralRules.createRules(description);
+ checkNewSamples(description, test, "one", PluralRules.SampleType.INTEGER, "@integer 0, 1, 1e5", true,
+ new FixedDecimal(0));
+ checkNewSamples(description, test, "one", PluralRules.SampleType.DECIMAL, "@decimal 0.0~1.5, 1.1e5", true,
+ new FixedDecimal(0, 1));
+ checkNewSamples(description, test, "many", PluralRules.SampleType.INTEGER, "@integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, …", false,
+ new FixedDecimal(1000000));
+ checkNewSamples(description, test, "many", PluralRules.SampleType.DECIMAL, "@decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …", false,
+ FixedDecimal.createWithExponent(2.1, 1, 6));
+ checkNewSamples(description, test, "other", PluralRules.SampleType.INTEGER, "@integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …", false,
+ new FixedDecimal(2));
+ checkNewSamples(description, test, "other", PluralRules.SampleType.DECIMAL, "@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …", false,
+ new FixedDecimal(2.0, 1));
+ }
+
public void checkOldSamples(String description, PluralRules rules, String keyword, SampleType sampleType,
Double... expected) {
Collection<Double> oldSamples = rules.getSamples(keyword, sampleType);
@@ -713,7 +744,7 @@
}
for (ULocale locale : uniqueRuleSet) {
if (locale.getLanguage().equals("fr") &&
- logKnownIssue("21299", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
+ logKnownIssue("21322", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
continue;
}
PluralRules rules = factory.forLocale(locale);
@@ -968,12 +999,61 @@
}
}
+ // For the time being, the compact notation exponent operand `c` is an alias
+ // for the scientific exponent operand `e` and compact notation.
+ @Test
+ public void testScientificPluralKeyword() {
+ PluralRules rules = PluralRules.createRules("one: i = 0,1 @integer 0, 1 @decimal 0.0~1.5; many: e = 0 and i % 1000000 = 0 and v = 0 or " +
+ "e != 0 .. 5; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …");
+ ULocale locale = new ULocale("fr-FR");
+ Object[][] casesData = {
+ // unlocalized formatter skeleton, input, string output, plural rule keyword
+ {"", 0, "0", "one"},
+ {"scientific", 0, "0", "one"},
+
+ {"", 1, "1", "one"},
+ {"scientific", 1, "1", "one"},
+
+ {"", 2, "2", "other"},
+ {"scientific", 2, "2", "other"},
+
+ {"", 1000000, "1 000 000", "many"},
+ {"scientific", 1000000, "1 million", "many"},
+
+ {"", 1000001, "1 000 001", "other"},
+ {"scientific", 1000001, "1 million", "many"},
+
+ {"", 120000, "1 200 000", "other"},
+ {"scientific", 1200000, "1,2 millions", "many"},
+
+ {"", 1200001, "1 200 001", "other"},
+ {"scientific", 1200001, "1,2 millions", "many"},
+
+ {"", 2000000, "2 000 000", "many"},
+ {"scientific", 2000000, "2 millions", "many"},
+ };
+
+ for (Object[] caseDatum : casesData) {
+ String skeleton = (String) caseDatum[0];
+ int input = (int) caseDatum[1];
+ // String expectedString = (String) caseDatum[2];
+ String expectPluralRuleKeyword = (String) caseDatum[3];
+
+ String actualPluralRuleKeyword =
+ getPluralKeyword(rules, locale, input, skeleton);
+
+ assertEquals(
+ String.format("PluralRules select %s: %d", skeleton, input),
+ expectPluralRuleKeyword,
+ actualPluralRuleKeyword);
+ }
+ }
@Test
public void testCompactDecimalPluralKeyword() {
- PluralRules rules = PluralRules.createRules("one: i = 0,1 @integer 0, 1 @decimal 0.0~1.5; many: e = 0 and i % 1000000 = 0 and v = 0 or " +
- "e != 0 .. 5; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …");
+ PluralRules rules = PluralRules.createRules("one: i = 0,1 @integer 0, 1 @decimal 0.0~1.5; many: c = 0 and i % 1000000 = 0 and v = 0 or " +
+ "c != 0 .. 5; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …");
ULocale locale = new ULocale("fr-FR");
Object[][] casesData = {
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java
index 9131382..ec2d598 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java
@@ -609,55 +609,57 @@
}
@Test
- public void testCompactDecimalSuppressedExponent() {
+ public void testScientificAndCompactSuppressedExponent() {
ULocale locale = new ULocale("fr-FR");
Object[][] casesData = {
- // unlocalized formatter skeleton, input, string output, long output, double output, BigDecimal output, plain string, suppressed exponent
- {"", 123456789, "123 456 789", 123456789L, 123456789.0, new BigDecimal("123456789"), "123456789", 0},
- {"compact-long", 123456789, "123 millions", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000", 6},
- {"compact-short", 123456789, "123 M", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000", 6},
- {"scientific", 123456789, "1,234568E8", 123456800L, 123456800.0, new BigDecimal("123456800"), "123456800", 8},
+ // unlocalized formatter skeleton, input, string output, long output,
+ // double output, BigDecimal output, plain string,
+ // suppressed scientific exponent, suppressed compact exponent
+ {"", 123456789, "123 456 789", 123456789L, 123456789.0, new BigDecimal("123456789"), "123456789", 0, 0},
+ {"compact-long", 123456789, "123 millions", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000", 6, 6},
+ {"compact-short", 123456789, "123 M", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000", 6, 6},
+ {"scientific", 123456789, "1,234568E8", 123456800L, 123456800.0, new BigDecimal("123456800"), "123456800", 8, 8},
- {"", 1234567, "1 234 567", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567", 0},
- {"compact-long", 1234567, "1,2 million", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000", 6},
- {"compact-short", 1234567, "1,2 M", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000", 6},
- {"scientific", 1234567, "1,234567E6", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567", 6},
+ {"", 1234567, "1 234 567", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567", 0, 0},
+ {"compact-long", 1234567, "1,2 million", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000", 6, 6},
+ {"compact-short", 1234567, "1,2 M", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000", 6, 6},
+ {"scientific", 1234567, "1,234567E6", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567", 6, 6},
- {"", 123456, "123 456", 123456L, 123456.0, new BigDecimal("123456"), "123456", 0},
- {"compact-long", 123456, "123 mille", 123000L, 123000.0, new BigDecimal("123000"), "123000", 3},
- {"compact-short", 123456, "123 k", 123000L, 123000.0, new BigDecimal("123000"), "123000", 3},
- {"scientific", 123456, "1,23456E5", 123456L, 123456.0, new BigDecimal("123456"), "123456", 5},
+ {"", 123456, "123 456", 123456L, 123456.0, new BigDecimal("123456"), "123456", 0, 0},
+ {"compact-long", 123456, "123 mille", 123000L, 123000.0, new BigDecimal("123000"), "123000", 3, 3},
+ {"compact-short", 123456, "123 k", 123000L, 123000.0, new BigDecimal("123000"), "123000", 3, 3},
+ {"scientific", 123456, "1,23456E5", 123456L, 123456.0, new BigDecimal("123456"), "123456", 5, 5},
- {"", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0},
- {"compact-long", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0},
- {"compact-short", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0},
- {"scientific", 123, "1,23E2", 123L, 123.0, new BigDecimal("123"), "123", 2},
+ {"", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0, 0},
+ {"compact-long", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0, 0},
+ {"compact-short", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0, 0},
+ {"scientific", 123, "1,23E2", 123L, 123.0, new BigDecimal("123"), "123", 2, 2},
- {"", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0},
- {"compact-long", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0},
- {"compact-short", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0},
- {"scientific", 1.2, "1,2E0", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0},
+ {"", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0, 0},
+ {"compact-long", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0, 0},
+ {"compact-short", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0, 0},
+ {"scientific", 1.2, "1,2E0", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0, 0},
- {"", 0.12, "0,12", 0L, 0.12, new BigDecimal("0.12"), "0.12", 0},
- {"compact-long", 0.12, "0,12", 0L, 0.12, new BigDecimal("0.12"), "0.12", 0},
- {"compact-short", 0.12, "0,12", 0L, 0.12, new BigDecimal("0.12"), "0.12", 0},
- {"scientific", 0.12, "1,2E-1", 0L, 0.12, new BigDecimal("0.12"), "0.12", -1},
+ {"", 0.12, "0,12", 0L, 0.12, new BigDecimal("0.12"), "0.12", 0, 0},
+ {"compact-long", 0.12, "0,12", 0L, 0.12, new BigDecimal("0.12"), "0.12", 0, 0},
+ {"compact-short", 0.12, "0,12", 0L, 0.12, new BigDecimal("0.12"), "0.12", 0, 0},
+ {"scientific", 0.12, "1,2E-1", 0L, 0.12, new BigDecimal("0.12"), "0.12", -1, -1},
- {"", 0.012, "0,012", 0L, 0.012, new BigDecimal("0.012"), "0.012", 0},
- {"compact-long", 0.012, "0,012", 0L, 0.012, new BigDecimal("0.012"), "0.012", 0},
- {"compact-short", 0.012, "0,012", 0L, 0.012, new BigDecimal("0.012"), "0.012", 0},
- {"scientific", 0.012, "1,2E-2", 0L, 0.012, new BigDecimal("0.012"), "0.012", -2},
+ {"", 0.012, "0,012", 0L, 0.012, new BigDecimal("0.012"), "0.012", 0, 0},
+ {"compact-long", 0.012, "0,012", 0L, 0.012, new BigDecimal("0.012"), "0.012", 0, 0},
+ {"compact-short", 0.012, "0,012", 0L, 0.012, new BigDecimal("0.012"), "0.012", 0, 0},
+ {"scientific", 0.012, "1,2E-2", 0L, 0.012, new BigDecimal("0.012"), "0.012", -2, -2},
- {"", 999.9, "999,9", 999L, 999.9, new BigDecimal("999.9"), "999.9", 0},
- {"compact-long", 999.9, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
- {"compact-short", 999.9, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
- {"scientific", 999.9, "9,999E2", 999L, 999.9, new BigDecimal("999.9"), "999.9", 2},
+ {"", 999.9, "999,9", 999L, 999.9, new BigDecimal("999.9"), "999.9", 0, 0},
+ {"compact-long", 999.9, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3, 3},
+ {"compact-short", 999.9, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3, 3},
+ {"scientific", 999.9, "9,999E2", 999L, 999.9, new BigDecimal("999.9"), "999.9", 2, 2},
- {"", 1000.0, "1 000", 1000L, 1000.0, new BigDecimal("1000"), "1000", 0},
- {"compact-long", 1000.0, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
- {"compact-short", 1000.0, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
- {"scientific", 1000.0, "1E3", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3},
+ {"", 1000.0, "1 000", 1000L, 1000.0, new BigDecimal("1000"), "1000", 0, 0},
+ {"compact-long", 1000.0, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3, 3},
+ {"compact-short", 1000.0, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3, 3},
+ {"scientific", 1000.0, "1E3", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3, 3},
};
for (Object[] caseDatum : casesData) {
@@ -673,7 +675,8 @@
double expectedDouble = (double) caseDatum[4];
BigDecimal expectedBigDecimal = (BigDecimal) caseDatum[5];
String expectedPlainString = (String) caseDatum[6];
- int expectedSuppressedExponent = (int) caseDatum[7];
+ int expectedSuppressedScientificExponent = (int) caseDatum[7];
+ int expectedSuppressedCompactExponent = (int) caseDatum[8];
FormattedNumber fn = formatter.format(input);
DecimalQuantity_DualStorageBCD dq = (DecimalQuantity_DualStorageBCD)
@@ -683,22 +686,23 @@
double actualDouble = dq.toDouble();
BigDecimal actualBigDecimal = dq.toBigDecimal();
String actualPlainString = dq.toPlainString();
- int actualSuppressedExponent = dq.getExponent();
+ int actualSuppressedScientificExponent = dq.getExponent();
+ int actualSuppressedCompactExponent = dq.getExponent();
assertEquals(
String.format("formatted number %s toString: %f", skeleton, input),
expectedString,
actualString);
assertEquals(
- String.format("compact decimal %s toLong: %f", skeleton, input),
+ String.format("formatted number %s toLong: %f", skeleton, input),
expectedLong,
actualLong);
assertDoubleEquals(
- String.format("compact decimal %s toDouble: %f", skeleton, input),
+ String.format("formatted number %s toDouble: %f", skeleton, input),
expectedDouble,
actualDouble);
assertBigDecimalEquals(
- String.format("compact decimal %s toBigDecimal: %f", skeleton, input),
+ String.format("formatted number %s toBigDecimal: %f", skeleton, input),
expectedBigDecimal,
actualBigDecimal);
assertEquals(
@@ -706,35 +710,45 @@
expectedPlainString,
actualPlainString);
assertEquals(
- String.format("compact decimal %s suppressed exponent: %f", skeleton, input),
- expectedSuppressedExponent,
- actualSuppressedExponent);
+ String.format("formatted number %s suppressed scientific exponent: %f", skeleton, input),
+ expectedSuppressedScientificExponent,
+ actualSuppressedScientificExponent);
+ assertEquals(
+ String.format("formatted number %s suppressed compact exponent: %f", skeleton, input),
+ expectedSuppressedCompactExponent,
+ actualSuppressedCompactExponent);
// test the actual computed values of the plural operands
double expectedNOperand = expectedDouble;
double expectedIOperand = expectedLong;
- double expectedEOperand = expectedSuppressedExponent;
+ double expectedEOperand = expectedSuppressedScientificExponent;
+ double expectedCOperand = expectedSuppressedCompactExponent;
double actualNOperand = dq.getPluralOperand(Operand.n);
double actualIOperand = dq.getPluralOperand(Operand.i);
double actualEOperand = dq.getPluralOperand(Operand.e);
+ double actualCOperand = dq.getPluralOperand(Operand.c);
assertEquals(
String.format("formatted number %s toString: %s", skeleton, input),
expectedString,
actualString);
assertDoubleEquals(
- String.format("compact decimal %s n operand: %f", skeleton, input),
+ String.format("formatted number %s n operand: %f", skeleton, input),
expectedNOperand,
actualNOperand);
assertDoubleEquals(
- String.format("compact decimal %s i operand: %f", skeleton, input),
+ String.format("formatted number %s i operand: %f", skeleton, input),
expectedIOperand,
actualIOperand);
assertDoubleEquals(
- String.format("compact decimal %s e operand: %f", skeleton, input),
+ String.format("formatted number %s e operand: %f", skeleton, input),
expectedEOperand,
actualEOperand);
+ assertDoubleEquals(
+ String.format("formatted number %s c operand: %f", skeleton, input),
+ expectedCOperand,
+ actualCOperand);
}
}
@@ -760,6 +774,7 @@
double expectedVOperand = 2;
double expectedWOperand = 1;
double expectedEOperand = 3;
+ double expectedCOperand = 3;
String expectedString = "1,23450 millier";
double actualNOperand = dq.getPluralOperand(Operand.n);
double actualIOperand = dq.getPluralOperand(Operand.i);
@@ -768,6 +783,7 @@
double actualVOperand = dq.getPluralOperand(Operand.v);
double actualWOperand = dq.getPluralOperand(Operand.w);
double actualEOperand = dq.getPluralOperand(Operand.e);
+ double actualCOperand = dq.getPluralOperand(Operand.c);
String actualString = fn.toString();
assertDoubleEquals(
@@ -798,6 +814,10 @@
String.format("compact decimal fraction e operand: %f", inputVal),
expectedEOperand,
actualEOperand);
+ assertDoubleEquals(
+ String.format("compact decimal fraction c operand: %f", inputVal),
+ expectedCOperand,
+ actualCOperand);
assertEquals(
String.format("compact decimal fraction toString: %f", inputVal),
expectedString,
@@ -815,11 +835,12 @@
Object[][] casesData = {
// input, compact long string output,
- // compact n operand, compact i operand, compact e operand
- {123456789, "123 millions", 123000000.0, 123000000.0, 6.0},
- {1234567, "1,2 million", 1200000.0, 1200000.0, 6.0},
- {123456, "123 mille", 123000.0, 123000.0, 3.0},
- {123, "123", 123.0, 123.0, 0.0},
+ // compact n operand, compact i operand, compact e operand,
+ // compact c operand
+ {123456789, "123 millions", 123000000.0, 123000000.0, 6.0, 6.0},
+ {1234567, "1,2 million", 1200000.0, 1200000.0, 6.0, 6.0},
+ {123456, "123 mille", 123000.0, 123000.0, 3.0, 3.0},
+ {123, "123", 123.0, 123.0, 0.0, 0.0},
};
for (Object[] caseDatum : casesData) {
@@ -828,11 +849,12 @@
double expectedNOperand = (double) caseDatum[2];
double expectedIOperand = (double) caseDatum[3];
double expectedEOperand = (double) caseDatum[4];
+ double expectedCOperand = (double) caseDatum[5];
FormattedNumber fnCompactScaled = compactScaled.format(input);
DecimalQuantity_DualStorageBCD dqCompactScaled =
(DecimalQuantity_DualStorageBCD) fnCompactScaled.getFixedDecimal();
- double compactScaledEOperand = dqCompactScaled.getPluralOperand(Operand.e);
+ double compactScaledCOperand = dqCompactScaled.getPluralOperand(Operand.c);
FormattedNumber fnCompact = compactLong.format(input);
DecimalQuantity_DualStorageBCD dqCompact =
@@ -841,6 +863,7 @@
double compactNOperand = dqCompact.getPluralOperand(Operand.n);
double compactIOperand = dqCompact.getPluralOperand(Operand.i);
double compactEOperand = dqCompact.getPluralOperand(Operand.e);
+ double compactCOperand = dqCompact.getPluralOperand(Operand.c);
assertEquals(
String.format("formatted number compactLong toString: %s", input),
expectedString,
@@ -857,14 +880,18 @@
String.format("compact decimal %d, e operand vs. expected", input),
expectedEOperand,
compactEOperand);
+ assertDoubleEquals(
+ String.format("compact decimal %d, c operand vs. expected", input),
+ expectedCOperand,
+ compactCOperand);
// By scaling by 10^3 in a locale that has words / compact notation
// based on powers of 10^3, we guarantee that the suppressed
// exponent will differ by 3.
assertDoubleEquals(
- String.format("decimal %d, e operand for compact vs. compact scaled", input),
- compactEOperand + 3,
- compactScaledEOperand);
+ String.format("decimal %d, c operand for compact vs. compact scaled", input),
+ compactCOperand + 3,
+ compactScaledCOperand);
}
}