ICU-21714 Use FixedDecimal instead of double for plural samples in tests

See #1875
This commit is contained in:
Elango Cheran 2021-09-22 05:21:58 +00:00 committed by Elango
parent 0a478416d2
commit 2660845480
6 changed files with 232 additions and 20 deletions

View file

@ -1881,8 +1881,8 @@ void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) {
double FixedDecimal::getPluralOperand(PluralOperand operand) const {
switch(operand) {
case PLURAL_OPERAND_N: return source;
case PLURAL_OPERAND_I: return static_cast<double>(intValue);
case PLURAL_OPERAND_N: return (exponent == 0 ? source : source * pow(10, exponent));
case PLURAL_OPERAND_I: return (double) longValue();
case PLURAL_OPERAND_F: return static_cast<double>(decimalDigits);
case PLURAL_OPERAND_T: return static_cast<double>(decimalDigitsWithoutTrailingZeros);
case PLURAL_OPERAND_V: return visibleDecimalDigitCount;
@ -1931,6 +1931,18 @@ UnicodeString FixedDecimal::toString() const {
return UnicodeString(buffer, -1, US_INV);
}
double FixedDecimal::doubleValue() const {
return (isNegative ? -source : source) * pow(10, exponent);
}
int64_t FixedDecimal::longValue() const {
if (exponent == 0) {
return intValue;
} else {
return (long) (pow(10, exponent) * intValue);
}
}
PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) {
fOpenStatus = status;

View file

@ -329,6 +329,9 @@ class U_I18N_API FixedDecimal: public IFixedDecimal, public UObject {
UnicodeString toString() const;
double doubleValue() const;
int64_t longValue() const;
double source;
int32_t visibleDecimalDigitCount;
int64_t decimalDigits;

View file

@ -57,6 +57,8 @@ void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &na
TESTCASE_AUTO(testGetAllKeywordValues);
TESTCASE_AUTO(testScientificPluralKeyword);
TESTCASE_AUTO(testCompactDecimalPluralKeyword);
TESTCASE_AUTO(testDoubleValue);
TESTCASE_AUTO(testLongValue);
TESTCASE_AUTO(testOrdinal);
TESTCASE_AUTO(testSelect);
TESTCASE_AUTO(testSelectRange);
@ -898,6 +900,94 @@ PluralRulesTest::testCompactDecimalPluralKeyword() {
}
}
void
PluralRulesTest::testDoubleValue() {
IcuTestErrorCode errorCode(*this, "testDoubleValue");
struct IntTestCase {
const int64_t inputNum;
const double expVal;
} intCases[] = {
{-101, -101.0},
{-100, -100.0},
{-1, -1.0},
{0, 0.0},
{1, 1.0},
{100, 100.0}
};
for (const auto& cas : intCases) {
const int64_t inputNum = cas.inputNum;
const double expVal = cas.expVal;
FixedDecimal fd(inputNum);
UnicodeString message(u"FixedDecimal::doubleValue() for" + Int64ToUnicodeString(inputNum));
assertEquals(message, expVal, fd.doubleValue());
}
struct DoubleTestCase {
const double inputNum;
const double expVal;
} dblCases[] = {
{-0.0, -0.0},
{0.1, 0.1},
{1.999, 1.999},
{2.0, 2.0},
{100.001, 100.001}
};
for (const auto & cas : dblCases) {
const double inputNum = cas.inputNum;
const double expVal = cas.expVal;
FixedDecimal fd(inputNum);
UnicodeString message(u"FixedDecimal::doubleValue() for" + DoubleToUnicodeString(inputNum));
assertEquals(message, expVal, fd.doubleValue());
}
}
void
PluralRulesTest::testLongValue() {
IcuTestErrorCode errorCode(*this, "testLongValue");
struct IntTestCase {
const int64_t inputNum;
const int64_t expVal;
} intCases[] = {
{-101, 101},
{-100, 100},
{-1, 1},
{0, 0},
{1, 1},
{100, 100}
};
for (const auto& cas : intCases) {
const int64_t inputNum = cas.inputNum;
const int64_t expVal = cas.expVal;
FixedDecimal fd(inputNum);
UnicodeString message(u"FixedDecimal::longValue() for" + Int64ToUnicodeString(inputNum));
assertEquals(message, expVal, fd.longValue());
}
struct DoubleTestCase {
const double inputNum;
const int64_t expVal;
} dblCases[] = {
{-0.0, 0},
{0.1, 0},
{1.999, 1},
{2.0, 2},
{100.001, 100}
};
for (const auto & cas : dblCases) {
const double inputNum = cas.inputNum;
const int64_t expVal = cas.expVal;
FixedDecimal fd(inputNum);
UnicodeString message(u"FixedDecimal::longValue() for" + DoubleToUnicodeString(inputNum));
assertEquals(message, expVal, fd.longValue());
}
}
UnicodeString PluralRulesTest::getPluralKeyword(const LocalPointer<PluralRules> &rules, Locale locale, double number, const char16_t* skeleton) {
IcuTestErrorCode errorCode(*this, "getPluralKeyword");
UnlocalizedNumberFormatter ulnf = NumberFormatter::forSkeleton(skeleton, errorCode);

View file

@ -36,6 +36,8 @@ private:
void testWithin();
void testGetAllKeywordValues();
void testCompactDecimalPluralKeyword();
void testDoubleValue();
void testLongValue();
void testScientificPluralKeyword();
void testOrdinal();
void testSelect();

View file

@ -688,9 +688,7 @@ public class PluralRules implements Serializable {
source = isNegative ? -n : n;
visibleDecimalDigitCount = v;
decimalDigits = f;
integerValue = n > MAX
? MAX
: (long)n;
integerValue = n > MAX ? MAX : (long) source;
int initExpVal = e;
if (initExpVal == 0) {
initExpVal = c;
@ -922,15 +920,15 @@ public class PluralRules implements Serializable {
@Deprecated
public double getPluralOperand(Operand operand) {
switch(operand) {
case n: return source;
case i: return integerValue;
case n: return (exponent == 0 ? source : source * Math.pow(10, exponent));
case i: return intValue();
case f: return decimalDigits;
case t: return decimalDigitsWithoutTrailingZeros;
case v: return visibleDecimalDigitCount;
case w: return visibleDecimalDigitCountWithoutTrailingZeros;
case e: return exponent;
case c: return exponent;
default: return source;
default: return doubleValue();
}
}
@ -1078,7 +1076,7 @@ public class PluralRules implements Serializable {
@Deprecated
public long getShiftedValue() {
if (exponent != 0 && visibleDecimalDigitCount == 0 && decimalDigits == 0) {
// Need to taxe exponent into account if we have it
// Need to take exponent into account if we have it
return (long)(source * Math.pow(10, exponent));
}
return integerValue * baseFactor + decimalDigits;

View file

@ -465,21 +465,56 @@ public class PluralRulesTest extends TestFmwk {
}
public void checkValue(String title1, PluralRules rules, String expected, String value) {
double number = Double.parseDouble(value);
int decimalPos = value.indexOf('.') + 1;
int countVisibleFractionDigits;
int fractionaldigits;
if (decimalPos == 0) {
countVisibleFractionDigits = fractionaldigits = 0;
} else {
countVisibleFractionDigits = value.length() - decimalPos;
fractionaldigits = Integer.parseInt(value.substring(decimalPos));
}
String result = rules.select(number, countVisibleFractionDigits, fractionaldigits);
FixedDecimal fdNum = new FixedDecimal(value);
String result = rules.select(fdNum);
ULocale locale = null;
assertEquals(getAssertMessage(title1, locale, rules, expected) + "; value: " + value, expected, result);
}
/**
* Check the testing helper method checkValue(), which parses a plural
* rule's sample string as a {@link FormattedNumber} in order to call
* {@code PluralRules.select(FormattedNumber)}, which in turn can support
* the exponent in plural sample numbers like 1e6 and 2.8c3.
*/
@Test
public void testCheckValue() {
String ruleString =
"many: e = 0 and i != 0 and i % 1000000 = 0 and v = 0 or e != 0..5"
+ " @integer 1000000, 1e6, 2e6, 3e6, 4e6, 5e6, 6e6, …"
+ " @decimal 1.0000001e6, 1.1e6, 2.0000001e6, 2.1e6, 3.0000001e6, 3.1e6, …; "
+ "one: i = 1 and v = 0"
+ " @integer 1; "
+ "other: "
+ " @integer 0, 2~16, 100, 1000, 10000, 100000, 1e3, 2e3, 3e3, 4e3, 5e3, 6e3, …"
+ " @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 1.0001e3, 1.1e3, 2.0001e3, 2.1e3, 3.0001e3, 3.1e3, …";
PluralRules rules = PluralRules.createRules(ruleString);
Object[][] casesData = {
// expected category, value string
{"many", "1000000"},
{"many", "1e6"},
{"many", "1.1e6"},
{"one", "1"},
{"other", "0"},
{"other", "1e5"},
{"other", "100000"},
{"other", "0.0"},
{"other", "100000.0"},
{"other", "1000000.0"}
};
for (Object[] caseDatum : casesData) {
String expCategory = (String) caseDatum[0];
String inputValueStr = (String) caseDatum[1];
String msg = "checkValue(" + inputValueStr + ")";
checkValue(msg, rules, expCategory, inputValueStr);
}
}
private static String[][] equalityTestData = {
// once we add fractions, we had to retract the "test all possibilities" for equality,
// so we only have a limited set of equality tests now.
@ -1110,6 +1145,78 @@ public class PluralRulesTest extends TestFmwk {
return pluralKeyword;
}
@Test
public void testDoubleValue() {
Object[][] intCasesData = {
// source number, expected double value
{-101, -101.0},
{-100, -100.0},
{-1, -1.0},
{0, 0.0},
{1, 1.0},
{100, 100.0}
};
for (Object[] caseDatum : intCasesData) {
double inputNum = (int) caseDatum[0];
double expVal = (double) caseDatum[1];
FixedDecimal fd = new FixedDecimal(inputNum);
assertEquals("FixedDecimal.doubleValue() for " + inputNum, expVal, fd.doubleValue());
}
Object[][] doubleCasesData = {
// source number, expected double value
{-0.0, -0.0},
{0.1, 0.1},
{1.999, 1.999},
{2.0, 2.0},
{100.001, 100.001}
};
for (Object[] caseDatum : doubleCasesData) {
double inputNum = (double) caseDatum[0];
double expVal = (double) caseDatum[1];
FixedDecimal fd = new FixedDecimal(inputNum);
assertEquals("FixedDecimal.doubleValue() for " + inputNum, expVal, fd.doubleValue());
}
}
@Test
public void testLongValue() {
Object[][] intCasesData = {
// source number, expected double value
{-101, 101},
{-100, 100},
{-1, 1},
{0, 0},
{1, 1},
{100, 100}
};
for (Object[] caseDatum : intCasesData) {
long inputNum = (int) caseDatum[0];
long expVal = (int) caseDatum[1];
FixedDecimal fd = new FixedDecimal(inputNum);
assertEquals("FixedDecimal.longValue() for " + inputNum, expVal, fd.longValue());
}
Object[][] doubleCasesData = {
// source number, expected double value
{-0.0, 0},
{0.1, 0},
{1.999, 1},
{2.0, 2},
{100.001, 100}
};
for (Object[] caseDatum : doubleCasesData) {
double inputNum = (double) caseDatum[0];
long expVal = (int) caseDatum[1];
FixedDecimal fd = new FixedDecimal(inputNum);
assertEquals("FixedDecimal.longValue() for " + inputNum, expVal, fd.longValue());
}
}
enum StandardPluralCategories {
zero, one, two, few, many, other;
/**