+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
- * Copyright (C) 1996-2015, International Business Machines Corporation and *
- * others. All Rights Reserved. *
+ * Copyright (C) 1996-2016, International Business Machines Corporation and
+ * others. All Rights Reserved.
*******************************************************************************
*/
#include "unicode/ustring.h"
#include "unicode/decimfmt.h"
#include "unicode/udata.h"
+#include "cmemory.h"
+#include "putilimp.h"
#include "testutil.h"
#include <string.h>
TESTCASE(19, TestSetDecimalFormatSymbols);
TESTCASE(20, TestPluralRules);
TESTCASE(21, TestMultiplePluralRules);
+ TESTCASE(22, TestInfinityNaN);
+ TESTCASE(23, TestVariableDecimalPoint);
+ TESTCASE(24, TestLargeNumbers);
+ TESTCASE(25, TestCompactDecimalFormatStyle);
#else
TESTCASE(0, TestRBNFDisabled);
#endif
UnicodeString rules("%spellout-cardinal-feminine-genitive:"
"0: zero;"
"1: ono;"
+ "2: two;"
"1000: << $(cardinal,one{thousand}few{thousanF}other{thousanO})$[ >>];"
"%spellout-cardinal-feminine:"
+ "x.x: [<< $(cardinal,one{singleton}other{plurality})$ ]>%%fractions>;"
"0: zero;"
"1: one;"
- "1000: << $(cardinal,one{thousand}few{thousanF}other{thousanO})$[ >>];");
+ "2: two;"
+ "1000: << $(cardinal,one{thousand}few{thousanF}other{thousanO})$[ >>];"
+ "%%fractions:"
+ "10: <%spellout-cardinal-feminine< $(cardinal,one{oneth}other{tenth})$;"
+ "100: <%spellout-cardinal-feminine< $(cardinal,one{1hundredth}other{hundredth})$;");
UErrorCode status = U_ZERO_ERROR;
UParseError pError;
RuleBasedNumberFormat formatter(rules, Locale("ru"), pError, status);
errln("RuleBasedNumberFormat(spellout-cardinal-feminine) did not return the correct value. Got: %d", result.getLong());
errln(resultStr);
}
+ static const char* const testData[][2] = {
+ { "0", "zero" },
+ { "1", "one" },
+ { "2", "two" },
+ { "0.1", "one oneth" },
+ { "0.2", "two tenth" },
+ { "1.1", "one singleton one oneth" },
+ { "1.2", "one singleton two tenth" },
+ { "2.1", "two plurality one oneth" },
+ { "2.2", "two plurality two tenth" },
+ { "0.01", "one 1hundredth" },
+ { "0.02", "two hundredth" },
+ { NULL, NULL }
+ };
+ doTest(&formatter, testData, TRUE);
}
void IntlTestRBNF::TestFractionalRuleSet()
{ "1.2856", "1 2/7" },
{ NULL, NULL }
};
- doTest(&formatter, testData, FALSE); // exact values aren't parsable from fractions
+ doTest(&formatter, testData, FALSE); // exact values aren't parsable from fractions
}
}
&NEG_TWO_TO_32X5, &TWO_TO_32, &NEG_FIVE
};
const int TUPLE_WIDTH = 3;
- const int TUPLE_COUNT = (int)(sizeof(tuples)/sizeof(tuples[0]))/TUPLE_WIDTH;
+ const int TUPLE_COUNT = UPRV_LENGTHOF(tuples)/TUPLE_WIDTH;
for (int i = 0; i < TUPLE_COUNT; ++i) {
const llong lhs = *tuples[i*TUPLE_WIDTH+0];
const llong rhs = *tuples[i*TUPLE_WIDTH+1];
&BIG_FIVEp1, &FIVE, &ONE
};
const int TUPLE_WIDTH = 3;
- const int TUPLE_COUNT = (int)(sizeof(tuples)/sizeof(tuples[0]))/TUPLE_WIDTH;
+ const int TUPLE_COUNT = UPRV_LENGTHOF(tuples)/TUPLE_WIDTH;
for (int i = 0; i < TUPLE_COUNT; ++i) {
const llong lhs = *tuples[i*TUPLE_WIDTH+0];
const llong rhs = *tuples[i*TUPLE_WIDTH+1];
doTest(formatter, testData, TRUE);
#if !UCONFIG_NO_COLLATION
- if( !logKnownIssue("9503") ) {
- formatter->setLenient(TRUE);
- static const char* lpTestData[][2] = {
+ formatter->setLenient(TRUE);
+ static const char* lpTestData[][2] = {
{ "fifty-7", "57" },
{ " fifty-7", "57" },
{ " fifty-7", "57" },
{ "fifteen hundred and zero", "1,500" },
{ "FOurhundred thiRTY six", "436" },
{ NULL, NULL}
- };
- doLenientParseTest(formatter, lpTestData);
- }
+ };
+ doLenientParseTest(formatter, lpTestData);
#endif
}
delete formatter;
"<<%main>,<'en', \"it's ok\">>", // double quotes work too
" \n <\n <\n %main\n >\n , \t <\t en\t , \tfoo \t\t > \n\n > \n ", // Pattern_White_Space ok
};
- int32_t goodLocsLen = sizeof(goodLocs)/sizeof(goodLocs[0]);
+ int32_t goodLocsLen = UPRV_LENGTHOF(goodLocs);
static const char* badLocs[] = {
" ", // non-zero length
"<<%main>> x", // extra non-space text at end
};
- int32_t badLocsLen = sizeof(badLocs)/sizeof(badLocs[0]);
+ int32_t badLocsLen = UPRV_LENGTHOF(badLocs);
for (i = 0; i < goodLocsLen; ++i) {
logln("[%d] '%s'", i, goodLocs[i]);
continue;
}
#if !UCONFIG_NO_COLLATION
- for (unsigned int numidx = 0; numidx < sizeof(numbers)/sizeof(double); numidx++) {
+ for (unsigned int numidx = 0; numidx < UPRV_LENGTHOF(numbers); numidx++) {
double n = numbers[numidx];
UnicodeString str;
f->format(n, str);
// Make sure there are no divide by 0 errors.
UnicodeString result;
- RuleBasedNumberFormat(ruRules, Locale("ru"), parseError, status).format(21000, result);
+ RuleBasedNumberFormat(ruRules, Locale("ru"), parseError, status).format((int32_t)21000, result);
if (result.compare(UNICODE_STRING_SIMPLE("twenty-one thousand")) != 0) {
errln("Got " + result + " for 21000");
}
}
+void IntlTestRBNF::TestInfinityNaN() {
+ UErrorCode status = U_ZERO_ERROR;
+ UParseError parseError;
+ UnicodeString enRules("%default:"
+ "-x: minus >>;"
+ "Inf: infinite;"
+ "NaN: not a number;"
+ "0: =#,##0=;");
+ RuleBasedNumberFormat enFormatter(enRules, Locale::getEnglish(), parseError, status);
+ const char * const enTestData[][2] = {
+ {"1", "1"},
+ {"\\u221E", "infinite"},
+ {"-\\u221E", "minus infinite"},
+ {"NaN", "not a number"},
+ { NULL, NULL }
+ };
+ if (U_FAILURE(status)) {
+ dataerrln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_errorName(status)));
+ return;
+ }
+
+ doTest(&enFormatter, enTestData, true);
+
+ // Test the default behavior when the rules are undefined.
+ UnicodeString enRules2("%default:"
+ "-x: ->>;"
+ "0: =#,##0=;");
+ RuleBasedNumberFormat enFormatter2(enRules2, Locale::getEnglish(), parseError, status);
+ if (U_FAILURE(status)) {
+ errln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_errorName(status)));
+ return;
+ }
+ const char * const enDefaultTestData[][2] = {
+ {"1", "1"},
+ {"\\u221E", "\\u221E"},
+ {"-\\u221E", "-\\u221E"},
+ {"NaN", "NaN"},
+ { NULL, NULL }
+ };
+
+ doTest(&enFormatter2, enDefaultTestData, true);
+}
+
+void IntlTestRBNF::TestVariableDecimalPoint() {
+ UErrorCode status = U_ZERO_ERROR;
+ UParseError parseError;
+ UnicodeString enRules("%spellout-numbering:"
+ "-x: minus >>;"
+ "x.x: << point >>;"
+ "x,x: << comma >>;"
+ "0.x: xpoint >>;"
+ "0,x: xcomma >>;"
+ "0: zero;"
+ "1: one;"
+ "2: two;"
+ "3: three;"
+ "4: four;"
+ "5: five;"
+ "6: six;"
+ "7: seven;"
+ "8: eight;"
+ "9: nine;");
+ RuleBasedNumberFormat enFormatter(enRules, Locale::getEnglish(), parseError, status);
+ const char * const enTestPointData[][2] = {
+ {"1.1", "one point one"},
+ {"1.23", "one point two three"},
+ {"0.4", "xpoint four"},
+ { NULL, NULL }
+ };
+ if (U_FAILURE(status)) {
+ dataerrln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_errorName(status)));
+ return;
+ }
+ doTest(&enFormatter, enTestPointData, true);
+
+ DecimalFormatSymbols decimalFormatSymbols(Locale::getEnglish(), status);
+ decimalFormatSymbols.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, UNICODE_STRING_SIMPLE(","));
+ enFormatter.setDecimalFormatSymbols(decimalFormatSymbols);
+ const char * const enTestCommaData[][2] = {
+ {"1.1", "one comma one"},
+ {"1.23", "one comma two three"},
+ {"0.4", "xcomma four"},
+ { NULL, NULL }
+ };
+ doTest(&enFormatter, enTestCommaData, true);
+}
+
+void IntlTestRBNF::TestLargeNumbers() {
+ UErrorCode status = U_ZERO_ERROR;
+ RuleBasedNumberFormat rbnf(URBNF_SPELLOUT, Locale::getEnglish(), status);
+
+ const char * const enTestFullData[][2] = {
+ {"-9007199254740991", "minus nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-one"}, // Maximum precision in both a double and a long
+ {"9007199254740991", "nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-one"}, // Maximum precision in both a double and a long
+ {"-9007199254740992", "minus nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-two"}, // Only precisely contained in a long
+ {"9007199254740992", "nine quadrillion seven trillion one hundred ninety-nine billion two hundred fifty-four million seven hundred forty thousand nine hundred ninety-two"}, // Only precisely contained in a long
+ {"9999999999999998", "nine quadrillion nine hundred ninety-nine trillion nine hundred ninety-nine billion nine hundred ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-eight"},
+ {"9999999999999999", "nine quadrillion nine hundred ninety-nine trillion nine hundred ninety-nine billion nine hundred ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-nine"},
+ {"999999999999999999", "nine hundred ninety-nine quadrillion nine hundred ninety-nine trillion nine hundred ninety-nine billion nine hundred ninety-nine million nine hundred ninety-nine thousand nine hundred ninety-nine"},
+ {"1000000000000000000", "1,000,000,000,000,000,000"}, // The rules don't go to 1 quintillion yet
+ {"-9223372036854775809", "-9,223,372,036,854,775,809"}, // We've gone beyond 64-bit precision
+ {"-9223372036854775808", "-9,223,372,036,854,775,808"}, // We've gone beyond +64-bit precision
+ {"-9223372036854775807", "minus 9,223,372,036,854,775,807"}, // Minimum 64-bit precision
+ {"-9223372036854775806", "minus 9,223,372,036,854,775,806"}, // Minimum 64-bit precision + 1
+ {"9223372036854774111", "9,223,372,036,854,774,111"}, // Below 64-bit precision
+ {"9223372036854774999", "9,223,372,036,854,774,999"}, // Below 64-bit precision
+ {"9223372036854775000", "9,223,372,036,854,775,000"}, // Below 64-bit precision
+ {"9223372036854775806", "9,223,372,036,854,775,806"}, // Maximum 64-bit precision - 1
+ {"9223372036854775807", "9,223,372,036,854,775,807"}, // Maximum 64-bit precision
+ {"9223372036854775808", "9,223,372,036,854,775,808"}, // We've gone beyond 64-bit precision. This can only be represented with BigDecimal.
+ { NULL, NULL }
+ };
+ doTest(&rbnf, enTestFullData, false);
+}
+
+void IntlTestRBNF::TestCompactDecimalFormatStyle() {
+ UErrorCode status = U_ZERO_ERROR;
+ UParseError parseError;
+ // This is not a common use case, but we're testing it anyway.
+ UnicodeString numberPattern("=###0.#####=;"
+ "1000: <###0.00< K;"
+ "1000000: <###0.00< M;"
+ "1000000000: <###0.00< B;"
+ "1000000000000: <###0.00< T;"
+ "1000000000000000: <###0.00< Q;");
+ RuleBasedNumberFormat rbnf(numberPattern, UnicodeString(), Locale::getEnglish(), parseError, status);
+
+ const char * const enTestFullData[][2] = {
+ {"1000", "1.00 K"},
+ {"1234", "1.23 K"},
+ {"999994", "999.99 K"},
+ {"999995", "1000.00 K"},
+ {"1000000", "1.00 M"},
+ {"1200000", "1.20 M"},
+ {"1200000000", "1.20 B"},
+ {"1200000000000", "1.20 T"},
+ {"1200000000000000", "1.20 Q"},
+ {"4503599627370495", "4.50 Q"},
+ {"4503599627370496", "4.50 Q"},
+ {"8990000000000000", "8.99 Q"},
+ {"9008000000000000", "9.00 Q"}, // Number doesn't precisely fit into a double
+ {"9456000000000000", "9.00 Q"}, // Number doesn't precisely fit into a double
+ {"10000000000000000", "10.00 Q"}, // Number doesn't precisely fit into a double
+ {"9223372036854775807", "9223.00 Q"}, // Maximum 64-bit precision
+ {"9223372036854775808", "9,223,372,036,854,775,808"}, // We've gone beyond 64-bit precision. This can only be represented with BigDecimal.
+ { NULL, NULL }
+ };
+ doTest(&rbnf, enTestFullData, false);
+}
+
void
IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing)
{
log("[%i] %s = ", i, numString);
Formattable expectedNumber;
- decFmt.parse(numString, expectedNumber, status);
+ UnicodeString escapedNumString = UnicodeString(numString, -1, US_INV).unescape();
+ decFmt.parse(escapedNumString, expectedNumber, status);
if (U_FAILURE(status)) {
errln("FAIL: decFmt could not parse %s", numString);
break;
errln(msg);
break;
} else {
- if (parsedNumber != expectedNumber) {
+ if (parsedNumber != expectedNumber
+ && (!uprv_isNaN(parsedNumber.getDouble()) || !uprv_isNaN(expectedNumber.getDouble())))
+ {
UnicodeString msg = "FAIL: parse failed for ";
msg.append(actualString);
msg.append(", expected ");