ICU-21519 Add PluralOperand 'c' as alias to 'e', parse FixedDecimal strings

This commit is contained in:
Elango Cheran 2021-03-03 23:33:03 -08:00 committed by Peter Edberg
parent 2f89152830
commit c263b5b370
11 changed files with 466 additions and 145 deletions

View file

@ -273,6 +273,9 @@ double DecimalQuantity::getPluralOperand(PluralOperand operand) const {
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());
}

View file

@ -60,6 +60,7 @@ static const UChar PK_VAR_I[]={LOW_I,0};
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 @@ getSamplesFromString(const UnicodeString &samples, double *destDbl,
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 @@ PluralRules::getSamples(const UnicodeString &keyword, FixedDecimal *dest,
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 @@ PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErr
case tVariableF:
case tVariableT:
case tVariableE:
case tVariableC:
case tVariableV:
U_ASSERT(curAndConstraint != nullptr);
curAndConstraint->digitsType = type;
@ -1092,6 +1094,8 @@ static UnicodeString tokenString(tokenType tok) {
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 @@ PluralRuleParser::checkSyntax(UErrorCode &status)
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 @@ PluralRuleParser::checkSyntax(UErrorCode &status)
type == tVariableF ||
type == tVariableT ||
type == tVariableE ||
type == tVariableC ||
type == tVariableV ||
type == tAt)) {
status = U_UNEXPECTED_TOKEN;
@ -1318,6 +1324,7 @@ PluralRuleParser::checkSyntax(UErrorCode &status)
type != tVariableF &&
type != tVariableT &&
type != tVariableE &&
type != tVariableC &&
type != tVariableV) {
status = U_UNEXPECTED_TOKEN;
}
@ -1497,6 +1504,8 @@ PluralRuleParser::getKeyType(const UnicodeString &token, tokenType keyType)
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 @@ PluralOperand tokenTypeToPluralOperand(tokenType tt) {
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() {
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 @@ void FixedDecimal::init(double n, int32_t v, int64_t f) {
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 @@ double FixedDecimal::getPluralOperand(PluralOperand operand) const {
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 @@ bool FixedDecimal::operator==(const FixedDecimal &other) const {
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);
}

View file

@ -145,6 +145,7 @@ enum tokenType {
tVariableV,
tVariableT,
tVariableE,
tVariableC,
tDecimal,
tInteger,
tEOF
@ -222,11 +223,20 @@ enum PluralOperand {
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.
*
@ -280,8 +290,10 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject {
* @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 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject {
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);

View file

@ -201,7 +201,7 @@ class DecimalQuantityTest : public IntlTest {
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);

View file

@ -30,7 +30,7 @@ void DecimalQuantityTest::runIndexedTest(int32_t index, UBool exec, const char *
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 @@ void DecimalQuantityTest::testNickelRounding() {
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 @@ void DecimalQuantityTest::testCompactDecimalSuppressedExponent() {
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"123456789", 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"123456789", 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"1234567", 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"1234567", 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"123456", 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"123456", 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"1000", 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"1000", 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 @@ void DecimalQuantityTest::testCompactDecimalSuppressedExponent() {
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 @@ void DecimalQuantityTest::testCompactDecimalSuppressedExponent() {
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 @@ void DecimalQuantityTest::testSuppressedExponentUnchangedByInitialScaling() {
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 @@ void DecimalQuantityTest::testSuppressedExponentUnchangedByInitialScaling() {
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 @@ void DecimalQuantityTest::testSuppressedExponentUnchangedByInitialScaling() {
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);
}
}

View file

@ -52,8 +52,10 @@ void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &na
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 @@ void PluralRulesTest::testGetSamples() {
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 @@ void PluralRulesTest::testGetFixedDecimalSamples() {
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 @@ void PluralRulesTest::testSamplesWithExponent() {
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 @@ PluralRulesTest::testGetAllKeywordValues() {
}
}
// 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"1000000", u"many"},
{u"scientific", 1000000, u"1 million", u"many"},
{u"", 1000001, u"1000001", u"other"},
{u"scientific", 1000001, u"1 million", u"many"},
{u"", 120000, u"1200000", u"other"},
{u"scientific", 1200000, u"1,2 millions", u"many"},
{u"", 1200001, u"1200001", u"other"},
{u"scientific", 1200001, u"1,2 millions", u"many"},
{u"", 2000000, u"2000000", 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));

View file

@ -32,9 +32,11 @@ private:
void testGetSamples();
void testGetFixedDecimalSamples();
void testSamplesWithExponent();
void testSamplesWithCompactNotation();
void testWithin();
void testGetAllKeywordValues();
void testCompactDecimalPluralKeyword();
void testScientificPluralKeyword();
void testOrdinal();
void testSelect();
void testSelectRange();

View file

@ -265,6 +265,9 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
return fractionCountWithoutTrailingZeros();
case e:
return getExponent();
case c:
// Plural operand `c` is currently an alias for `e`.
return getExponent();
default:
return Math.abs(toDouble());
}

View file

@ -481,8 +481,8 @@ public class PluralRules implements Serializable {
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.
@ -490,6 +490,19 @@ public class PluralRules implements Serializable {
@Deprecated
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.
*
@ -657,10 +670,11 @@ public class PluralRules implements Serializable {
* @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 @@ public class PluralRules implements Serializable {
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.
//
@ -699,6 +717,15 @@ public class PluralRules implements Serializable {
baseFactor = (int) Math.pow(10, v);
}
/**
* @internal CLDR
* @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.
@ -848,8 +875,11 @@ public class PluralRules implements Serializable {
*/
@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 @@ public class PluralRules implements Serializable {
case v: return visibleDecimalDigitCount;
case w: return visibleDecimalDigitCountWithoutTrailingZeros;
case e: return exponent;
case c: return exponent;
default: return source;
}
}
@ -970,10 +1001,10 @@ public class PluralRules implements Serializable {
@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;
}
}

View file

@ -219,6 +219,37 @@ public class PluralRulesTest extends TestFmwk {
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 @@ public class PluralRulesTest extends TestFmwk {
}
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 @@ public class PluralRulesTest extends TestFmwk {
}
}
// 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, "1000000", "many"},
{"scientific", 1000000, "1 million", "many"},
{"", 1000001, "1000001", "other"},
{"scientific", 1000001, "1 million", "many"},
{"", 120000, "1200000", "other"},
{"scientific", 1200000, "1,2 millions", "many"},
{"", 1200001, "1200001", "other"},
{"scientific", 1200001, "1,2 millions", "many"},
{"", 2000000, "2000000", "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 = {

View file

@ -609,55 +609,57 @@ public class DecimalQuantityTest extends TestFmwk {
}
@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, "123456789", 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, "123456789", 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, "1234567", 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, "1234567", 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, "123456", 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, "123456", 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, "1000", 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, "1000", 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 @@ public class DecimalQuantityTest extends TestFmwk {
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 @@ public class DecimalQuantityTest extends TestFmwk {
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 @@ public class DecimalQuantityTest extends TestFmwk {
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 @@ public class DecimalQuantityTest extends TestFmwk {
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 @@ public class DecimalQuantityTest extends TestFmwk {
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 @@ public class DecimalQuantityTest extends TestFmwk {
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 @@ public class DecimalQuantityTest extends TestFmwk {
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 @@ public class DecimalQuantityTest extends TestFmwk {
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 @@ public class DecimalQuantityTest extends TestFmwk {
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 @@ public class DecimalQuantityTest extends TestFmwk {
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);
}
}