+/*
+ * Ticket #12684
+ * Test unum_formatDoubleForFields (and UFieldPositionIterator)
+ */
+
+typedef struct {
+ int32_t field;
+ int32_t beginPos;
+ int32_t endPos;
+} FieldsData;
+
+typedef struct {
+ const char * locale;
+ UNumberFormatStyle style;
+ double value;
+ const FieldsData * expectedFields;
+} FormatForFieldsItem;
+
+static const UChar patNoFields[] = { 0x0027, 0x0078, 0x0027, 0 }; /* "'x'", for UNUM_PATTERN_DECIMAL */
+
+
+/* "en_US", UNUM_CURRENCY, 123456.0 : "¤#,##0.00" => "$123,456.00" */
+static const FieldsData fields_en_CURR[] = {
+ { UNUM_CURRENCY_FIELD /*7*/, 0, 1 },
+ { UNUM_GROUPING_SEPARATOR_FIELD /*6*/, 4, 5 },
+ { UNUM_INTEGER_FIELD /*0*/, 1, 8 },
+ { UNUM_DECIMAL_SEPARATOR_FIELD /*2*/, 8, 9 },
+ { UNUM_FRACTION_FIELD /*1*/, 9, 11 },
+ { -1, -1, -1 },
+};
+/* "en_US"/"es_US/MX" , UNUM_PERCENT, -34 : "#,##0%" => "-34%" */
+static const FieldsData fields_en_PRCT[] = {
+ { UNUM_SIGN_FIELD /*10*/, 0, 1 },
+ { UNUM_INTEGER_FIELD /*0*/, 1, 3 },
+ { UNUM_PERCENT_FIELD /*8*/, 3, 4 },
+ { -1, -1, -1 },
+};
+/* "fr_FR", UNUM_CURRENCY, 123456.0 : "#,##0.00 ¤" => "123,456.00 €" */
+static const FieldsData fields_fr_CURR[] = {
+ { UNUM_GROUPING_SEPARATOR_FIELD /*6*/, 3, 4 },
+ { UNUM_INTEGER_FIELD /*0*/, 0, 7 },
+ { UNUM_DECIMAL_SEPARATOR_FIELD /*2*/, 7, 8 },
+ { UNUM_FRACTION_FIELD /*1*/, 8, 10 },
+ { UNUM_CURRENCY_FIELD /*7*/, 11, 12 },
+ { -1, -1, -1 },
+};
+/* "en_US", UNUM_PATTERN_DECIMAL, 12.0 : "'x'" => "x12" */
+static const FieldsData fields_en_PATN[] = {
+ { UNUM_INTEGER_FIELD /*0*/, 1, 3 },
+ { -1, -1, -1 },
+};
+
+static const FormatForFieldsItem fffItems[] = {
+ { "en_US", UNUM_CURRENCY_STANDARD, 123456.0, fields_en_CURR },
+ { "en_US", UNUM_PERCENT, -0.34, fields_en_PRCT },
+ { "es_US", UNUM_PERCENT, -0.34, fields_en_PRCT }, // rdar://57000745
+ { "es_MX", UNUM_PERCENT, -0.34, fields_en_PRCT }, // rdar://42948387
+ { "fr_FR", UNUM_CURRENCY_STANDARD, 123456.0, fields_fr_CURR },
+ { "en_US", UNUM_PATTERN_DECIMAL, 12.0, fields_en_PATN },
+ { NULL, (UNumberFormatStyle)0, 0, NULL },
+};
+
+static void TestFormatForFields(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
+ if ( U_FAILURE(status) ) {
+ log_err("ufieldpositer_open fails, status %s\n", u_errorName(status));
+ } else {
+ const FormatForFieldsItem * itemPtr;
+ for (itemPtr = fffItems; itemPtr->locale != NULL; itemPtr++) {
+ UNumberFormat* unum;
+ status = U_ZERO_ERROR;
+ unum = (itemPtr->style == UNUM_PATTERN_DECIMAL)?
+ unum_open(itemPtr->style, patNoFields, -1, itemPtr->locale, NULL, &status):
+ unum_open(itemPtr->style, NULL, 0, itemPtr->locale, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open fails for locale %s, style %d: status %s (Are you missing data?)\n", itemPtr->locale, itemPtr->style, u_errorName(status));
+ } else {
+ UChar ubuf[kUBufSize];
+ int32_t ulen = unum_formatDoubleForFields(unum, itemPtr->value, ubuf, kUBufSize, fpositer, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDoubleForFields fails for locale %s, style %d: status %s\n", itemPtr->locale, itemPtr->style, u_errorName(status));
+ } else {
+ const FieldsData * fptr;
+ int32_t field, beginPos, endPos;
+ for (fptr = itemPtr->expectedFields; TRUE; fptr++) {
+ field = ufieldpositer_next(fpositer, &beginPos, &endPos);
+ if (field != fptr->field || (field >= 0 && (beginPos != fptr->beginPos || endPos != fptr->endPos))) {
+ if (fptr->field >= 0) {
+ log_err("unum_formatDoubleForFields for locale %s as \"%s\"; expect field %d range %d-%d, get field %d range %d-%d\n",
+ itemPtr->locale, aescstrdup(ubuf, ulen), fptr->field, fptr->beginPos, fptr->endPos, field, beginPos, endPos);
+ } else {
+ log_err("unum_formatDoubleForFields for locale %s as \"%s\"; expect field < 0, get field %d range %d-%d\n",
+ itemPtr->locale, aescstrdup(ubuf, ulen), field, beginPos, endPos);
+ }
+ break;
+ }
+ if (field < 0) {
+ break;
+ }
+ }
+ }
+ unum_close(unum);
+ }
+ }
+ ufieldpositer_close(fpositer);
+ }
+}
+
+static void Test12052_NullPointer() {
+ UErrorCode status = U_ZERO_ERROR;
+ static const UChar input[] = u"199a";
+ UChar currency[200] = {0};
+ UNumberFormat *theFormatter = unum_open(UNUM_CURRENCY, NULL, 0, "en_US", NULL, &status);
+ if (!assertSuccessCheck("unum_open() failed", &status, TRUE)) { return; }
+ status = U_ZERO_ERROR;
+ unum_setAttribute(theFormatter, UNUM_LENIENT_PARSE, 1);
+ int32_t pos = 1;
+ unum_parseDoubleCurrency(theFormatter, input, -1, &pos, currency, &status);
+ assertEquals("should fail gracefully", "U_PARSE_ERROR", u_errorName(status));
+ unum_close(theFormatter);
+}
+
+typedef struct {
+ const char* locale;
+ const UChar* text; // text to parse
+ UBool lenient; // leniency to use
+ UBool intOnly; // whether to set PARSE_INT_ONLY
+ UErrorCode intStatus; // expected status from parse
+ int32_t intPos; // expected final pos from parse
+ int32_t intValue; // expected value from parse
+ UErrorCode doubStatus; // expected status from parseDouble
+ int32_t doubPos; // expected final pos from parseDouble
+ double doubValue; // expected value from parseDouble
+ UErrorCode decStatus; // expected status from parseDecimal
+ int32_t decPos; // expected final pos from parseDecimal
+ const char* decString; // expected output string from parseDecimal
+
+} ParseCaseItem;
+
+static const ParseCaseItem parseCaseItems[] = {
+ { "en", u"0,000", FALSE, FALSE, U_ZERO_ERROR, 5, 0, U_ZERO_ERROR, 5, 0.0, U_ZERO_ERROR, 5, "0" },
+ { "en", u"0,000", TRUE, FALSE, U_ZERO_ERROR, 5, 0, U_ZERO_ERROR, 5, 0.0, U_ZERO_ERROR, 5, "0" },
+ { "en", u",024", FALSE, FALSE, U_ZERO_ERROR, 4, 24, U_ZERO_ERROR, 4, 24.0, U_ZERO_ERROR, 4, "24" },
+ { "en", u",024", TRUE, FALSE, U_ZERO_ERROR, 4, 24, U_ZERO_ERROR, 4, 24.0, U_ZERO_ERROR, 4, "24" },
+ { "en", u"1000,000", FALSE, FALSE, U_PARSE_ERROR, 0, 0, U_PARSE_ERROR, 0, 0.0, U_PARSE_ERROR, 0, "" },
+ { "en", u"1000,000", TRUE, FALSE, U_ZERO_ERROR, 8, 1000000, U_ZERO_ERROR, 8, 1000000.0, U_ZERO_ERROR, 8, "1000000" },
+ { "en", u"", FALSE, FALSE, U_PARSE_ERROR, 0, 0, U_PARSE_ERROR, 0, 0.0, U_PARSE_ERROR, 0, "" },
+ { "en", u"", TRUE, FALSE, U_PARSE_ERROR, 0, 0, U_PARSE_ERROR, 0, 0.0, U_PARSE_ERROR, 0, "" },
+ { "en", u"9999990000503021", FALSE, FALSE, U_INVALID_FORMAT_ERROR, 16, 2147483647, U_ZERO_ERROR, 16, 9999990000503020.0, U_ZERO_ERROR, 16, "9999990000503021" },
+ { "en", u"9999990000503021", FALSE, TRUE, U_INVALID_FORMAT_ERROR, 16, 2147483647, U_ZERO_ERROR, 16, 9999990000503020.0, U_ZERO_ERROR, 16, "9999990000503021" },
+ { "en", u"1000000.5", FALSE, FALSE, U_ZERO_ERROR, 9, 1000000, U_ZERO_ERROR, 9, 1000000.5, U_ZERO_ERROR, 9, "1.0000005E+6" /* change from ICU-62nnn */},
+ { "en", u"1000000.5", FALSE, TRUE, U_ZERO_ERROR, 7, 1000000, U_ZERO_ERROR, 7, 1000000.0, U_ZERO_ERROR, 7, "1000000" },
+ { "en", u"123.5", FALSE, FALSE, U_ZERO_ERROR, 5, 123, U_ZERO_ERROR, 5, 123.5, U_ZERO_ERROR, 5, "123.5" },
+ { "en", u"123.5", FALSE, TRUE, U_ZERO_ERROR, 3, 123, U_ZERO_ERROR, 3, 123.0, U_ZERO_ERROR, 3, "123" },
+ { NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0.0, 0, 0, NULL }
+};
+
+static void TestParseCases(void) {
+ const ParseCaseItem* itemPtr;
+ for (itemPtr = parseCaseItems; itemPtr->locale != NULL; itemPtr++) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unumDec = unum_open(UNUM_DECIMAL, NULL, 0, itemPtr->locale, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_data_err("unum_open UNUM_DECIMAL fails for locale %s: %s\n", itemPtr->locale, u_errorName(status));
+ continue;
+ }
+ int32_t intValue, parsePos, dclen;
+ double doubValue;
+ char decstr[32];
+ unum_setAttribute(unumDec, UNUM_LENIENT_PARSE, itemPtr->lenient);
+ unum_setAttribute(unumDec, UNUM_PARSE_INT_ONLY, itemPtr->intOnly);
+
+ parsePos = 0;
+ status = U_ZERO_ERROR;
+ intValue = unum_parse(unumDec, itemPtr->text, -1, &parsePos, &status);
+ if (status != itemPtr->intStatus || parsePos != itemPtr->intPos || intValue != itemPtr->intValue) {
+ char btext[32];
+ u_austrcpy(btext, itemPtr->text);
+ log_err("locale %s, text \"%s\", lenient %d, intOnly %d;\n parse expected status %s, pos %d, value %d;\n got %s, %d, %d\n",
+ itemPtr->locale, btext, itemPtr->lenient, itemPtr->intOnly,
+ u_errorName(itemPtr->intStatus), itemPtr->intPos, itemPtr->intValue,
+ u_errorName(status), parsePos, intValue);
+ }
+
+ parsePos = 0;
+ status = U_ZERO_ERROR;
+ doubValue = unum_parseDouble(unumDec, itemPtr->text, -1, &parsePos, &status);
+ if (status != itemPtr->doubStatus || parsePos != itemPtr->doubPos || doubValue != itemPtr->doubValue) {
+ char btext[32];
+ u_austrcpy(btext, itemPtr->text);
+ log_err("locale %s, text \"%s\", lenient %d, intOnly %d;\n parseDouble expected status %s, pos %d, value %.1f;\n got %s, %d, %.1f\n",
+ itemPtr->locale, btext, itemPtr->lenient, itemPtr->intOnly,
+ u_errorName(itemPtr->doubStatus), itemPtr->doubPos, itemPtr->doubValue,
+ u_errorName(status), parsePos, doubValue);
+ }
+
+ parsePos = 0;
+ status = U_ZERO_ERROR;
+ decstr[0] = 0;
+ dclen = unum_parseDecimal(unumDec, itemPtr->text, -1, &parsePos, decstr, 32, &status);
+ (void)dclen;
+ if (status != itemPtr->decStatus || parsePos != itemPtr->decPos || uprv_strcmp(decstr,itemPtr->decString) != 0) {
+ char btext[32];
+ u_austrcpy(btext, itemPtr->text);
+ log_err("locale %s, text \"%s\", lenient %d, intOnly %d;\n parseDecimal expected status %s, pos %d, str \"%s\";\n got %s, %d, \"%s\"\n",
+ itemPtr->locale, btext, itemPtr->lenient, itemPtr->intOnly,
+ u_errorName(itemPtr->decStatus), itemPtr->decPos, itemPtr->decString,
+ u_errorName(status), parsePos, decstr);
+ }
+
+ unum_close(unumDec);
+ }
+}
+
+typedef struct {
+ const char* descrip;
+ const char* locale;
+ UNumberFormatStyle style;
+ int32_t minInt;
+ int32_t minFrac;
+ int32_t maxFrac;
+ double roundIncr;
+ const UChar* expPattern;
+ double valueToFmt;
+ const UChar* expFormat;
+} SetMaxFracAndRoundIncrItem;
+
+static const SetMaxFracAndRoundIncrItem maxFracAndRoundIncrItems[] = {
+ // descrip locale style mnI mnF mxF rdInc expPat value expFmt
+ { "01 en_US DEC 1/0/3/0.0", "en_US", UNUM_DECIMAL, 1, 0, 3, 0.0, u"#,##0.###", 0.128, u"0.128" },
+ { "02 en_US DEC 1/0/1/0.0", "en_US", UNUM_DECIMAL, 1, 0, 1, 0.0, u"#,##0.#", 0.128, u"0.1" },
+ { "03 en_US DEC 1/0/1/0.01", "en_US", UNUM_DECIMAL, 1, 0, 1, 0.01, u"#,##0.#", 0.128, u"0.1" },
+ { "04 en_US DEC 1/1/1/0.01", "en_US", UNUM_DECIMAL, 1, 1, 1, 0.01, u"#,##0.0", 0.128, u"0.1" },
+ { "05 en_US DEC 1/0/1/0.1", "en_US", UNUM_DECIMAL, 1, 0, 1, 0.1, u"#,##0.1", 0.128, u"0.1" }, // use incr
+ { "06 en_US DEC 1/1/1/0.1", "en_US", UNUM_DECIMAL, 1, 1, 1, 0.1, u"#,##0.1", 0.128, u"0.1" }, // use incr
+
+ { "10 en_US DEC 1/0/1/0.02", "en_US", UNUM_DECIMAL, 1, 0, 1, 0.02, u"#,##0.#", 0.128, u"0.1" },
+ { "11 en_US DEC 1/0/2/0.02", "en_US", UNUM_DECIMAL, 1, 0, 2, 0.02, u"#,##0.02", 0.128, u"0.12" }, // use incr
+ { "12 en_US DEC 1/0/3/0.02", "en_US", UNUM_DECIMAL, 1, 0, 3, 0.02, u"#,##0.02#", 0.128, u"0.12" }, // use incr
+ { "13 en_US DEC 1/1/1/0.02", "en_US", UNUM_DECIMAL, 1, 1, 1, 0.02, u"#,##0.0", 0.128, u"0.1" },
+ { "14 en_US DEC 1/1/2/0.02", "en_US", UNUM_DECIMAL, 1, 1, 2, 0.02, u"#,##0.02", 0.128, u"0.12" }, // use incr
+ { "15 en_US DEC 1/1/3/0.02", "en_US", UNUM_DECIMAL, 1, 1, 3, 0.02, u"#,##0.02#", 0.128, u"0.12" }, // use incr
+ { "16 en_US DEC 1/2/2/0.02", "en_US", UNUM_DECIMAL, 1, 2, 2, 0.02, u"#,##0.02", 0.128, u"0.12" }, // use incr
+ { "17 en_US DEC 1/2/3/0.02", "en_US", UNUM_DECIMAL, 1, 2, 3, 0.02, u"#,##0.02#", 0.128, u"0.12" }, // use incr
+ { "18 en_US DEC 1/3/3/0.02", "en_US", UNUM_DECIMAL, 1, 3, 3, 0.02, u"#,##0.020", 0.128, u"0.120" }, // use incr
+
+ { "20 en_US DEC 1/1/1/0.0075", "en_US", UNUM_DECIMAL, 1, 1, 1, 0.0075, u"#,##0.0", 0.019, u"0.0" },
+ { "21 en_US DEC 1/1/2/0.0075", "en_US", UNUM_DECIMAL, 1, 1, 2, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
+ { "22 en_US DEC 1/1/2/0.0075", "en_US", UNUM_DECIMAL, 1, 1, 2, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
+ { "23 en_US DEC 1/1/3/0.0075", "en_US", UNUM_DECIMAL, 1, 1, 3, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
+ { "24 en_US DEC 1/1/3/0.0075", "en_US", UNUM_DECIMAL, 1, 1, 3, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
+ { "25 en_US DEC 1/2/2/0.0075", "en_US", UNUM_DECIMAL, 1, 2, 2, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
+ { "26 en_US DEC 1/2/2/0.0075", "en_US", UNUM_DECIMAL, 1, 2, 2, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
+ { "27 en_US DEC 1/2/3/0.0075", "en_US", UNUM_DECIMAL, 1, 2, 3, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
+ { "28 en_US DEC 1/2/3/0.0075", "en_US", UNUM_DECIMAL, 1, 2, 3, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
+ { "29 en_US DEC 1/3/3/0.0075", "en_US", UNUM_DECIMAL, 1, 3, 3, 0.0075, u"#,##0.0075", 0.004, u"0.0075" }, // use incr
+ { "2A en_US DEC 1/3/3/0.0075", "en_US", UNUM_DECIMAL, 1, 3, 3, 0.0075, u"#,##0.0075", 0.019, u"0.0225" }, // use incr
+
+ // Additions for rdar://51452216
+ { "30 en_US DEC 1/0/1/0.01", "en_US", UNUM_DECIMAL, 1, 1, 3, 0.001, u"#,##0.001", 1.23456789, u"1.235" },
+ { "31 en_US DEC 1/1/1/0.01", "en_US", UNUM_DECIMAL, 1, 1, 3, 0.001f, u"#,##0.001", 1.23456789, u"1.235" },
+
+ { NULL, NULL, UNUM_IGNORE, 0, 0, 0, 0.0, NULL, 0.0, NULL }
+};
+
+// The following is copied from C++ number_patternstring.cpp for this C test.
+//
+// Determine whether a given roundingIncrement should be ignored for formatting
+// based on the current maxFrac value (maximum fraction digits). For example a
+// roundingIncrement of 0.01 should be ignored if maxFrac is 1, but not if maxFrac
+// is 2 or more. Note that roundingIncrements are rounded in significance, so
+// a roundingIncrement of 0.006 is treated like 0.01 for this determination, i.e.
+// it should not be ignored if maxFrac is 2 or more (but a roundingIncrement of
+// 0.005 is treated like 0.001 for significance). This is the reason for the
+// initial doubling below.
+// roundIncr must be non-zero
+static UBool ignoreRoundingIncrement(double roundIncr, int32_t maxFrac) {
+ if (maxFrac < 0) {
+ return FALSE;
+ }
+ int32_t frac = 0;
+ roundIncr *= 2.0;
+ for (frac = 0; frac <= maxFrac && roundIncr <= 1.0; frac++, roundIncr *= 10.0);
+ return (frac > maxFrac);
+}
+
+static void TestSetMaxFracAndRoundIncr(void) {
+ const SetMaxFracAndRoundIncrItem* itemPtr;
+ for (itemPtr = maxFracAndRoundIncrItems; itemPtr->descrip != NULL; itemPtr++) {
+ UChar ubuf[kUBufMax];
+ char bbufe[kBBufMax];
+ char bbufg[kBBufMax];
+ int32_t ulen;
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unf = unum_open(itemPtr->style, NULL, 0, itemPtr->locale, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_data_err("locale %s: unum_open style %d fails with %s\n", itemPtr->locale, itemPtr->style, u_errorName(status));
+ continue;
+ }
+
+ unum_setAttribute(unf, UNUM_MIN_INTEGER_DIGITS, itemPtr->minInt);
+ unum_setAttribute(unf, UNUM_MIN_FRACTION_DIGITS, itemPtr->minFrac);
+ unum_setAttribute(unf, UNUM_MAX_FRACTION_DIGITS, itemPtr->maxFrac);
+ unum_setDoubleAttribute(unf, UNUM_ROUNDING_INCREMENT, itemPtr->roundIncr);
+
+ UBool roundIncrUsed = (itemPtr->roundIncr != 0.0 && !ignoreRoundingIncrement(itemPtr->roundIncr, itemPtr->maxFrac));
+
+ int32_t minInt = unum_getAttribute(unf, UNUM_MIN_INTEGER_DIGITS);
+ if (minInt != itemPtr->minInt) {
+ log_err("test %s: unum_getAttribute UNUM_MIN_INTEGER_DIGITS, expected %d, got %d\n",
+ itemPtr->descrip, itemPtr->minInt, minInt);
+ }
+ int32_t minFrac = unum_getAttribute(unf, UNUM_MIN_FRACTION_DIGITS);
+ if (minFrac != itemPtr->minFrac) {
+ log_err("test %s: unum_getAttribute UNUM_MIN_FRACTION_DIGITS, expected %d, got %d\n",
+ itemPtr->descrip, itemPtr->minFrac, minFrac);
+ }
+ // If incrementRounding is used, maxFrac is set equal to minFrac
+ int32_t maxFrac = unum_getAttribute(unf, UNUM_MAX_FRACTION_DIGITS);
+ // If incrementRounding is used, maxFrac is set equal to minFrac
+ int32_t expMaxFrac = (roundIncrUsed)? itemPtr->minFrac: itemPtr->maxFrac;
+ if (maxFrac != expMaxFrac) {
+ log_err("test %s: unum_getAttribute UNUM_MAX_FRACTION_DIGITS, expected %d, got %d\n",
+ itemPtr->descrip, expMaxFrac, maxFrac);
+ }
+ double roundIncr = unum_getDoubleAttribute(unf, UNUM_ROUNDING_INCREMENT);
+ // If incrementRounding is not used, roundIncr is set to 0.0
+ double expRoundIncr = (roundIncrUsed)? itemPtr->roundIncr: 0.0;
+ if (roundIncr != expRoundIncr) {
+ log_err("test %s: unum_getDoubleAttribute UNUM_ROUNDING_INCREMENT, expected %f, got %f\n",
+ itemPtr->descrip, expRoundIncr, roundIncr);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_toPattern(unf, FALSE, ubuf, kUBufMax, &status);
+ (void)ulen;
+ if ( U_FAILURE(status) ) {
+ log_err("test %s: unum_toPattern fails with %s\n", itemPtr->descrip, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expPattern)!=0) {
+ u_austrcpy(bbufe, itemPtr->expPattern);
+ u_austrcpy(bbufg, ubuf);
+ log_err("test %s: unum_toPattern expect \"%s\", get \"%s\"\n", itemPtr->descrip, bbufe, bbufg);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unf, itemPtr->valueToFmt, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("test %s: unum_formatDouble fails with %s\n", itemPtr->descrip, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expFormat)!=0) {
+ u_austrcpy(bbufe, itemPtr->expFormat);
+ u_austrcpy(bbufg, ubuf);
+ log_err("test %s: unum_formatDouble expect \"%s\", get \"%s\"\n", itemPtr->descrip, bbufe, bbufg);
+ }
+
+ unum_close(unf);
+ }
+}
+
+static void TestIgnorePadding(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unum = unum_open(UNUM_PATTERN_DECIMAL, NULL, 0, "en_US", NULL, &status);
+ if (U_FAILURE(status)) {
+ log_data_err("unum_open UNUM_PATTERN_DECIMAL for en_US and NULL pattern fails:%s\n", u_errorName(status));
+ } else {
+ unum_setAttribute(unum, UNUM_GROUPING_USED, 0);
+ unum_setAttribute(unum, UNUM_FORMAT_WIDTH, 0);
+ unum_setTextAttribute(unum, UNUM_PADDING_CHARACTER, u"*", 1, &status);
+ if (U_FAILURE(status)) {
+ log_err("unum_setTextAttribute UNUM_PADDING_CHARACTER to '*' fails: %s\n", u_errorName(status));
+ } else {
+ unum_setAttribute(unum, UNUM_PADDING_POSITION, 0);
+ unum_setAttribute(unum, UNUM_MIN_INTEGER_DIGITS, 0);
+ unum_setAttribute(unum, UNUM_MAX_INTEGER_DIGITS, 8);
+ unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 0);
+ unum_setAttribute(unum, UNUM_MAX_FRACTION_DIGITS, 0);
+
+ UChar ubuf[kUBufMax];
+ int32_t ulen = unum_toPattern(unum, FALSE, ubuf, kUBufMax, &status);
+ if (U_FAILURE(status)) {
+ log_err("unum_toPattern fails: %s\n", u_errorName(status));
+ } else {
+ char bbuf[kBBufMax];
+ if (ulen > 0 && ubuf[0]==u'*') {
+ ubuf[kUBufMax-1] = 0; // ensure zero termination
+ u_austrncpy(bbuf, ubuf, kBBufMax);
+ log_err("unum_toPattern result should ignore padding but get %s\n", bbuf);
+ }
+ unum_applyPattern(unum, FALSE, ubuf, ulen, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err("unum_applyPattern fails: %s\n", u_errorName(status));
+ } else {
+ ulen = unum_formatDecimal(unum, "24", -1, ubuf, kUBufMax, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err("unum_formatDecimal fails: %s\n", u_errorName(status));
+ } else if (u_strcmp(ubuf, u"24") != 0) {
+ ubuf[kUBufMax-1] = 0; // ensure zero termination
+ u_austrncpy(bbuf, ubuf, kBBufMax);
+ log_err("unum_formatDecimal result expect 24 but get %s\n", bbuf);
+ }
+ }
+ }
+ }
+ unum_close(unum);
+ }
+}
+
+static void TestSciNotationMaxFracCap(void) {
+ static const UChar* pat1 = u"#.##E+00;-#.##E+00";
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unum = unum_open(UNUM_PATTERN_DECIMAL, pat1, -1, "en_US", NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_PATTERN_DECIMAL with scientific pattern for \"en_US\" fails with %s\n", u_errorName(status));
+ } else {
+ double value;
+ UChar ubuf[kUBufMax];
+ char bbuf[kBBufMax];
+ int32_t ulen;
+
+ unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 0);
+ unum_setAttribute(unum, UNUM_MAX_FRACTION_DIGITS, 2147483647);
+ ulen = unum_toPattern(unum, FALSE, ubuf, kUBufMax, &status);
+ if ( U_SUCCESS(status) ) {
+ u_austrncpy(bbuf, ubuf, kUBufMax);
+ log_info("unum_toPattern (%d): %s\n", ulen, bbuf);
+ }
+
+ for (value = 10.0; value < 1000000000.0; value *= 10.0) {
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, value, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble value %.1f status %s\n", value, u_errorName(status));
+ } else if (u_strncmp(ubuf,u"1E+0",4) != 0) {
+ u_austrncpy(bbuf, ubuf, kUBufMax);
+ log_err("unum_formatDouble value %.1f expected result to begin with 1E+0, got %s\n", value, bbuf);
+ }
+ }
+ unum_close(unum);
+ }
+}
+
+typedef struct {
+ const char* locale;
+ double value;
+ const UChar* formatLimitPrecision;
+ const UChar* formatFullPrecision;
+} FormatPrecisionItem;
+
+static const FormatPrecisionItem formatPrecisionItems[] = {
+ { "en_US", 0.33333333 - 0.00333333, u"0.33", u"0.32999999999999996" },
+ { "en_US", 0.07 * 100.0, u"7", u"7.000000000000001" },
+ { NULL, 0.0, NULL, NULL }
+};
+
+static const UChar* patternTestPrecision = u"#0.################################################################################"; // 80 fraction places
+
+// Currently Apple only
+static void TestFormatPrecision(void) {
+ const FormatPrecisionItem* itemPtr;
+ for (itemPtr = formatPrecisionItems; itemPtr->locale != NULL; itemPtr++) {
+ UErrorCode status = U_ZERO_ERROR;
+ UParseError perr;
+ UNumberFormat *unum = unum_open(UNUM_PATTERN_DECIMAL, patternTestPrecision, -1, itemPtr->locale, &perr, &status);
+ if (U_FAILURE(status)) {
+ log_data_err("unum_open UNUM_PATTERN_DECIMAL fails for locale %s: %s\n", itemPtr->locale, u_errorName(status));
+ continue;
+ }
+ UChar ubuf[kUBufSize];
+ int32_t ulen;
+ UFieldPosition fpos;
+ UBool formatFullPrecision;
+
+ formatFullPrecision = (UBool)unum_getAttribute(unum, UNUM_FORMAT_WITH_FULL_PRECISION);
+ if (formatFullPrecision) {
+ log_err("unum_getAttribute, default for UNUM_FORMAT_WITH_FULL_PRECISION is not FALSE\n");
+ } else {
+ fpos.field = UNUM_DECIMAL_SEPARATOR_FIELD;
+ fpos.beginIndex = 0;
+ fpos.endIndex = 0;
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, itemPtr->value, ubuf, kUBufSize, &fpos, &status);
+ if (U_FAILURE(status)) {
+ log_err("unum_formatDouble locale %s val %.20f limit precision fails with %s\n", itemPtr->locale, itemPtr->value, u_errorName(status));
+ } else if (ulen != u_strlen(itemPtr->formatLimitPrecision) || u_strcmp(ubuf, itemPtr->formatLimitPrecision) != 0) {
+ char bbufe[kBBufSize];
+ char bbufg[kBBufSize];
+ u_strToUTF8(bbufe, kBBufSize, NULL, itemPtr->formatLimitPrecision, -1, &status);
+ u_strToUTF8(bbufg, kBBufSize, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble locale %s val %.20f limit precision, expect %s, get %s\n", itemPtr->locale, itemPtr->value, bbufe, bbufg);
+ }
+ }
+
+ unum_setAttribute(unum, UNUM_FORMAT_WITH_FULL_PRECISION, TRUE);
+ formatFullPrecision = (UBool)unum_getAttribute(unum, UNUM_FORMAT_WITH_FULL_PRECISION);
+ if (!formatFullPrecision) {
+ log_err("unum_getAttribute, after set UNUM_FORMAT_WITH_FULL_PRECISION is not TRUE\n");
+ } else {
+ fpos.field = UNUM_DECIMAL_SEPARATOR_FIELD;
+ fpos.beginIndex = 0;
+ fpos.endIndex = 0;
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, itemPtr->value, ubuf, kUBufSize, &fpos, &status);
+ if (U_FAILURE(status)) {
+ log_err("unum_formatDouble locale %s val %.20f full precision fails with %s\n", itemPtr->locale, itemPtr->value, u_errorName(status));
+ } else if (ulen != u_strlen(itemPtr->formatFullPrecision) || u_strcmp(ubuf, itemPtr->formatFullPrecision) != 0) {
+ char bbufe[kBBufSize];
+ char bbufg[kBBufSize];
+ u_strToUTF8(bbufe, kBBufSize, NULL, itemPtr->formatFullPrecision, -1, &status);
+ u_strToUTF8(bbufg, kBBufSize, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble locale %s val %.20f full precision, expect %s, get %s\n", itemPtr->locale, itemPtr->value, bbufe, bbufg);
+ }
+ }
+
+ unum_close(unum);
+ }
+}
+
+// Apple only for <rdar://problem/52538227>
+static void TestSetSigDigAndRoundIncr(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unum = unum_open(UNUM_PATTERN_DECIMAL, u"#", 1, "en_US", NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_PATTERN_DECIMAL # for \"en_US\" fails with %s\n", u_errorName(status));
+ } else {
+ static const double value = 1.034000;
+ UChar ubuf[kUBufMax];
+ char bbuf[kBBufMax];
+ int32_t ulen;
+
+ unum_setAttribute(unum, UNUM_MAX_INTEGER_DIGITS, 42);
+ unum_setAttribute(unum, UNUM_MAX_FRACTION_DIGITS, 0);
+ unum_setAttribute(unum, UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP); // =6
+ unum_setDoubleAttribute(unum, UNUM_ROUNDING_INCREMENT, 0.01);
+ unum_setAttribute(unum, UNUM_SIGNIFICANT_DIGITS_USED, 1);
+ unum_setAttribute(unum, UNUM_MAX_SIGNIFICANT_DIGITS, 5);
+ unum_setAttribute(unum, UNUM_MIN_SIGNIFICANT_DIGITS, 1);
+
+ log_info("unum_getAttribute minSig %d maxSig %d sigUsed %d\n",
+ unum_getAttribute(unum,UNUM_MIN_SIGNIFICANT_DIGITS), unum_getAttribute(unum,UNUM_MAX_SIGNIFICANT_DIGITS),
+ unum_getAttribute(unum,UNUM_SIGNIFICANT_DIGITS_USED));
+ log_info("unum_getDoubleAttribute UNUM_ROUNDING_INCREMENT %f\n", unum_getDoubleAttribute(unum,UNUM_SIGNIFICANT_DIGITS_USED));
+ ulen = unum_toPattern(unum, FALSE, ubuf, kUBufMax, &status);
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ if ( U_SUCCESS(status) ) {
+ log_info("unum_toPattern (%d): %s\n", ulen, bbuf);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, value, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble value %.1f status %s\n", value, u_errorName(status));
+ } else if (u_strcmp(ubuf,u"1.03") != 0) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble value %.1f expected 1.03, got %s\n", value, bbuf);
+ }
+ unum_close(unum);
+ }
+}
+
+// Apple for <rdar://problem/46755430>
+typedef struct {
+ const char* descrip;
+ const char* locale;
+ const UChar* setPosPrefix;
+ const UChar* setPosSuffix;
+ const UChar* setNegPrefix;
+ const UChar* setNegSuffix;
+ const UChar* expPosPrefix;
+ const UChar* expPosSuffix;
+ const UChar* expNegPrefix;
+ const UChar* expNegSuffix;
+ const UChar* expPattern;
+ double value;
+ const UChar* expPosFormat;
+ const UChar* expNegFormat;
+} SetAffixOnCurrFmtItem;
+
+static const SetAffixOnCurrFmtItem affixOnCurrFmtItems[] = {
+ // descrip loc sPP sPS sNP sNS ePP ePS eNP eNS ePattern value ePosFmt eNegFmt
+ { "01 en set no affix ", "en", NULL, NULL, NULL, NULL, u"¤", u"", u"-¤", u"", u"¤#,##0.00", 123.4, u"¤123.40", u"-¤123.40" },
+ { "02 en set + prefix", "en", u"$", NULL, NULL, NULL, u"$", u"", u"-¤", u"", u"$#,##0.00;-¤#,##0.00", 123.4, u"$123.40", u"-¤123.40" },
+ { "03 en set +- prefix", "en", u"$", NULL, u"-$", NULL, u"$", u"", u"-$", u"", u"$#,##0.00;'-'$#,##0.00", 123.4, u"$123.40", u"-$123.40" },
+ { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0.0,NULL,NULL }
+};
+
+// Apple only for <rdar://problem/46755430>
+static void TestSetAffixOnCurrFmt(void) {
+ const SetAffixOnCurrFmtItem* itemPtr;
+ for (itemPtr = affixOnCurrFmtItems; itemPtr->descrip != NULL; itemPtr++) {
+ UChar ubuf[kUBufMax];
+ char bbufe[kBBufMax];
+ char bbufg[kBBufMax];
+ int32_t ulen;
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unf = unum_open(UNUM_CURRENCY, NULL, 0, itemPtr->locale, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_data_err("locale %s: unum_open UNUM_CURRENCY fails with %s\n", itemPtr->locale, u_errorName(status));
+ continue;
+ }
+
+ if (itemPtr->setPosPrefix) {
+ status = U_ZERO_ERROR;
+ unum_setTextAttribute(unf, UNUM_POSITIVE_PREFIX, itemPtr->setPosPrefix, -1, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_setTextAttribute UNUM_POSITIVE_PREFIX fails with %s\n", itemPtr->descrip, u_errorName(status));
+ }
+ }
+ if (itemPtr->setPosSuffix) {
+ status = U_ZERO_ERROR;
+ unum_setTextAttribute(unf, UNUM_POSITIVE_SUFFIX, itemPtr->setPosSuffix, -1, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_setTextAttribute UNUM_POSITIVE_SUFFIX fails with %s\n", itemPtr->descrip, u_errorName(status));
+ }
+ }
+ if (itemPtr->setNegPrefix) {
+ status = U_ZERO_ERROR;
+ unum_setTextAttribute(unf, UNUM_NEGATIVE_PREFIX, itemPtr->setNegPrefix, -1, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_setTextAttribute UNUM_NEGATIVE_PREFIX fails with %s\n", itemPtr->descrip, u_errorName(status));
+ }
+ }
+ if (itemPtr->setNegSuffix) {
+ status = U_ZERO_ERROR;
+ unum_setTextAttribute(unf, UNUM_NEGATIVE_SUFFIX, itemPtr->setNegSuffix, -1, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_setTextAttribute UNUM_NEGATIVE_SUFFIX fails with %s\n", itemPtr->descrip, u_errorName(status));
+ }
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_getTextAttribute(unf, UNUM_POSITIVE_PREFIX, ubuf, kUBufMax, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_getTextAttribute UNUM_POSITIVE_PREFIX fails with %s\n", itemPtr->descrip, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expPosPrefix)!=0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expPosPrefix, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("test %s: unum_getTextAttribute UNUM_POSITIVE_PREFIX expect %s, get %s\n", itemPtr->descrip, bbufe, bbufg);
+ }
+ status = U_ZERO_ERROR;
+ ulen = unum_getTextAttribute(unf, UNUM_POSITIVE_SUFFIX, ubuf, kUBufMax, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_getTextAttribute UNUM_POSITIVE_SUFFIX fails with %s\n", itemPtr->descrip, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expPosSuffix)!=0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expPosSuffix, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("test %s: unum_getTextAttribute UNUM_POSITIVE_SUFFIX expect %s, get %s\n", itemPtr->descrip, bbufe, bbufg);
+ }
+ status = U_ZERO_ERROR;
+ ulen = unum_getTextAttribute(unf, UNUM_NEGATIVE_PREFIX, ubuf, kUBufMax, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_getTextAttribute UNUM_NEGATIVE_PREFIX fails with %s\n", itemPtr->descrip, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expNegPrefix)!=0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expNegPrefix, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("test %s: unum_getTextAttribute UNUM_NEGATIVE_PREFIX expect %s, get %s\n", itemPtr->descrip, bbufe, bbufg);
+ }
+ status = U_ZERO_ERROR;
+ ulen = unum_getTextAttribute(unf, UNUM_NEGATIVE_SUFFIX, ubuf, kUBufMax, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_getTextAttribute UNUM_NEGATIVE_SUFFIX fails with %s\n", itemPtr->descrip, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expNegSuffix)!=0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expNegSuffix, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("test %s: unum_getTextAttribute UNUM_NEGATIVE_SUFFIX expect %s, get %s\n", itemPtr->descrip, bbufe, bbufg);
+ }
+ status = U_ZERO_ERROR;
+ ulen = unum_toPattern(unf, FALSE, ubuf, kUBufMax, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_toPattern fails with %s\n", itemPtr->descrip, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expPattern)!=0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expPattern, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("test %s: unum_toPattern expect %s, get %s\n", itemPtr->descrip, bbufe, bbufg);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unf, itemPtr->value, ubuf, kUBufMax, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_formatDouble positive fails with %s\n", itemPtr->descrip, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expPosFormat)!=0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expPosFormat, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("test %s: unum_formatDouble positive expect %s, get %s\n", itemPtr->descrip, bbufe, bbufg);
+ }
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unf, -1.0*itemPtr->value, ubuf, kUBufMax, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err("test %s: unum_formatDouble negative fails with %s\n", itemPtr->descrip, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expNegFormat)!=0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expNegFormat, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("test %s: unum_formatDouble negative expect %s, get %s\n", itemPtr->descrip, bbufe, bbufg);
+ }
+
+ unum_close(unf);
+ }
+}
+
+// Apple only for <rdar://problem/46915356>
+static void TestParseWithEmptyCurr(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unum = unum_open(UNUM_CURRENCY, NULL, 0, "en_US", NULL, &status);
+ if (U_FAILURE(status)) {
+ log_data_err("unum_open UNUM_CURRENCY for \"en_US\" fails with %s\n", u_errorName(status));
+ } else {
+ unum_setSymbol(unum, UNUM_CURRENCY_SYMBOL, u"", 0, &status);
+ if (U_FAILURE(status)) {
+ log_err("unum_setSymbol UNUM_CURRENCY_SYMBOL u\"\" fails with %s\n", u_errorName(status));
+ } else {
+ char bbuf[kBBufMax] = { 0 };
+ UChar curr[4] = { 0 };
+ int32_t ppos, blen;
+ double val;
+ const UChar* text = u"3";
+
+ status = U_ZERO_ERROR;
+ ppos = 0;
+ blen = unum_parseDecimal(unum, text, -1, &ppos, bbuf, kBBufMax, &status);
+ if (U_FAILURE(status)) {
+ log_err("unum_parseDecimal u\"3\" with empty curr symbol fails with %s, ppos %d\n", u_errorName(status), ppos);
+ } else if (ppos != 1 || blen != 1 || bbuf[0] != '3') {
+ log_err("unum_parseDecimal expect ppos 1, blen 1, str 3; get %d, %d, %s\n", ppos, blen, bbuf);
+ }
+
+ status = U_ZERO_ERROR;
+ ppos = 0;
+ val = unum_parseDouble(unum, text, -1, &ppos, &status);
+ if (U_FAILURE(status)) {
+ log_err("unum_parseDouble u\"3\" with empty curr symbol fails with %s, ppos %d\n", u_errorName(status), ppos);
+ } else if (ppos != 1 || val != 3.0) {
+ log_err("unum_parseDouble expect ppos 1, val 3.0; get %d, %.2f\n", ppos, val);
+ }
+
+ status = U_ZERO_ERROR;
+ ppos = 0;
+ val = unum_parseDoubleCurrency(unum, text, -1, &ppos, curr, &status);
+ if (U_SUCCESS(status)) {
+ log_err("unum_parseDoubleCurrency u\"3\" with empty curr symbol succeeds, get ppos %d, val %.2f\n", ppos, val);
+ }
+ }
+ unum_close(unum);
+ }
+
+ // Additions for <rdar://problem/51938595>
+ // "¤#,##0.00" "¤ #,##0.00" "#,##0.00 ¤" "#,##,##0.00¤"
+ static const char* locales[] = {"en_US", "en_NO", "en_CZ", "en_BD", NULL };
+ const char ** localesPtr = locales;
+ const char* locale;
+ while ((locale = *localesPtr++) != NULL) {
+ status = U_ZERO_ERROR;
+ unum = unum_open(UNUM_CURRENCY, NULL, 0, locale, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_data_err("locale %s unum_open UNUM_CURRENCY fails with %s\n", locale, u_errorName(status));
+ } else {
+ char bbuf[kBBufMax] = { 0 };
+ UChar curr[4] = { 0 };
+ UChar ubuf[kUBufMax];
+ int32_t ppos, blen, ulen;
+ const double posValToUse = 37.0;
+ const double negValToUse = -3.0;
+ double val;
+
+ status = U_ZERO_ERROR;
+ unum_setSymbol(unum, UNUM_CURRENCY_SYMBOL, u"", 0, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s unum_setSymbol UNUM_CURRENCY_SYMBOL u\"\" fails with %s, skipping\n", locale, u_errorName(status));
+ continue;
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, posValToUse, ubuf, kUBufMax, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s unum_formatDouble %.1f fails with %s, skipping\n", locale, posValToUse, u_errorName(status));
+ continue;
+ }
+
+ status = U_ZERO_ERROR;
+ ppos = 0;
+ val = unum_parseDouble(unum, ubuf, ulen, &ppos, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s unum_parseDouble fails with %s, ppos %d, expect %.1f\n", locale, u_errorName(status), ppos, posValToUse);
+ } else if (ppos != ulen || val != posValToUse) {
+ log_err("locale %s unum_parseDouble expect ppos %d, val %.1f; get %d, %.2f\n", locale, ulen, posValToUse, ppos, val);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, negValToUse, ubuf, kUBufMax, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s unum_formatDouble %.1f fails with %s, skipping\n", locale, negValToUse, u_errorName(status));
+ continue;
+ }
+
+ status = U_ZERO_ERROR;
+ ppos = 0;
+ val = unum_parseDouble(unum, ubuf, ulen, &ppos, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s unum_parseDouble fails with %s, ppos %d, expect %.1f\n", locale, u_errorName(status), ppos, negValToUse);
+ } else if (ppos != ulen || val != negValToUse) {
+ log_err("locale %s unum_parseDouble expect ppos %d, val %.1f; get %d, %.2f\n", locale, ulen, negValToUse, ppos, val);
+ }
+
+ status = U_ZERO_ERROR;
+ unum_applyPattern(unum, FALSE, u"#,##0.00¤", -1, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s unum_applyPattern \"#,##0.00¤\" fails with %s, skipping\n", locale, u_errorName(status));
+ continue;
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, posValToUse, ubuf, kUBufMax, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s with \"#,##0.00¤\" unum_formatDouble %.1f fails with %s, skipping\n", locale, posValToUse, u_errorName(status));
+ continue;
+ }
+
+ status = U_ZERO_ERROR;
+ ppos = 0;
+ val = unum_parseDouble(unum, ubuf, ulen, &ppos, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s with \"#,##0.00¤\" unum_parseDouble fails with %s, ppos %d, expect %.1f\n", locale, u_errorName(status), ppos, posValToUse);
+ } else if (ppos != ulen || val != posValToUse) {
+ log_err("locale %s with \"#,##0.00¤\" unum_parseDouble expect ppos %d, val %.1f; get %d, %.2f\n", locale, ulen, posValToUse, ppos, val);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, negValToUse, ubuf, kUBufMax, NULL, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s with \"#,##0.00¤\" unum_formatDouble %.1f fails with %s, skipping\n", locale, negValToUse, u_errorName(status));
+ continue;
+ }
+
+ status = U_ZERO_ERROR;
+ ppos = 0;
+ val = unum_parseDouble(unum, ubuf, ulen, &ppos, &status);
+ if (U_FAILURE(status)) {
+ log_err("locale %s with \"#,##0.00¤\" unum_parseDouble fails with %s, ppos %d, expect %.1f\n", locale, u_errorName(status), ppos, negValToUse);
+ } else if (ppos != ulen || val != negValToUse) {
+ log_err("locale %s with \"#,##0.00¤\" unum_parseDouble expect ppos %d, val %.1f; get %d, %.2f\n", locale, ulen, negValToUse, ppos, val);
+ }
+
+ unum_close(unum);
+ }
+ }
+}
+
+// Apple only for <rdar://problem/50113359>
+static const UChar* pat1 = u"#.##E+00;-#.##E+00";
+static void TestSciNotationNumbers(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unum = unum_open(UNUM_PATTERN_DECIMAL, NULL, 0, "en_US", NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_PATTERN_DECIMAL with null pattern for \"en_US\" fails with %s\n", u_errorName(status));
+ } else {
+ unum_applyPattern(unum, FALSE, pat1, u_strlen(pat1), NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_applyPattern fails with %s\n", u_errorName(status));
+ } else {
+ double value;
+ UChar ubuf[kUBufMax];
+ char bbuf[kBBufMax];
+ int32_t ulen;
+
+ unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 0);
+ unum_setAttribute(unum, UNUM_MAX_FRACTION_DIGITS, 2147483647);
+ log_info("unum_getAttribute minInt %d maxInt %d minFrac %d maxFrac %d\n",
+ unum_getAttribute(unum,UNUM_MIN_INTEGER_DIGITS), unum_getAttribute(unum,UNUM_MAX_INTEGER_DIGITS),
+ unum_getAttribute(unum,UNUM_MIN_FRACTION_DIGITS), unum_getAttribute(unum,UNUM_MAX_FRACTION_DIGITS));
+ ulen = unum_toPattern(unum, FALSE, ubuf, kUBufMax, &status);
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ if ( U_SUCCESS(status) ) {
+ log_info("unum_toPattern (%d): %s\n", ulen, bbuf);
+ }
+
+ for (value = 10.0; value < 1000000000.0; value *= 10.0) {
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, value, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble value %.1f status %s\n", value, u_errorName(status));
+ } else if (u_strncmp(ubuf,u"1E+0",4) != 0) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble value %.1f expected result to begin with 1E+0, got %s\n", value, bbuf);
+ }
+ }
+ }
+ unum_close(unum);
+ }
+}
+
+// Apple only for <rdar://problem/51601250>
+static const UChar* patFmt3Exp = u"0.000E+00";
+static void TestSciNotationPrecision(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unum = unum_open(UNUM_PATTERN_DECIMAL, NULL, 0, "en_US", NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_PATTERN_DECIMAL with null pattern for \"en_US\" fails with %s\n", u_errorName(status));
+ } else {
+ unum_applyPattern(unum, FALSE, pat1, u_strlen(pat1), NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_applyPattern fails with %s\n", u_errorName(status));
+ } else {
+ double value;
+ UChar ubuf[kUBufMax];
+ char bexp[kBBufMax];
+ char bget[kBBufMax];
+ int32_t ulen;
+
+ unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 3);
+ unum_setAttribute(unum, UNUM_MAX_FRACTION_DIGITS, 3);
+ unum_setAttribute(unum, UNUM_GROUPING_USED, 0);
+
+ status = U_ZERO_ERROR;
+ ulen = unum_toPattern(unum, FALSE, ubuf, kUBufMax, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_toPattern fails with %s\n", u_errorName(status));
+ } else if (u_strcmp(ubuf,patFmt3Exp) != 0) {
+ u_strToUTF8(bget, kBBufMax, NULL, ubuf, ulen, &status);
+ log_info("unum_toPattern, get \"%s\"\n", bget);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, 0.0, ubuf, ulen, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble fails with %s\n", u_errorName(status));
+ } else if (u_strcmp(ubuf,patFmt3Exp) != 0) {
+ u_strToUTF8(bexp, kBBufMax, NULL, patFmt3Exp, -1, &status);
+ u_strToUTF8(bget, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble error, expect \"%s\", get \"%s\"\n", bexp, bget);
+ }
+ }
+ unum_close(unum);
+ }
+}
+
+// Apple only for <rdar://problem/49808819>
+static const char* minGroupLocale = "pt_PT"; // has minimumGroupingDigits{"2"}
+typedef struct {
+ double value;
+ const char* string;
+ const UChar* expectDecimal;
+ const UChar* expectCurrency;
+} TestMinGroupItem;
+static const TestMinGroupItem minGroupItems[] = {
+ { 123.0, "123.0", u"123,00", u"123,00 €" },
+ { 1234.0, "1234.0", u"1234,00", u"1234,00 €" },
+ { 12345.0, "12345.0", u"12 345,00", u"12 345,00 €" },
+ { 123456.0, "123456.0", u"123 456,00", u"123 456,00 €" },
+ { 1234567.0, "1234567.0", u"1 234 567,00", u"1 234 567,00 €" },
+ { 12345678.0, "12345678.0", u"12 345 678,00", u"12 345 678,00 €" },
+ { 0.0, NULL, NULL, NULL } // terminator
+};
+static void TestMinimumGrouping(void) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unumd = unum_open(UNUM_DECIMAL, NULL, 0, minGroupLocale, NULL, &status);
+ UNumberFormat* unumc = unum_open(UNUM_CURRENCY, NULL, 0, minGroupLocale, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_PATTERN_DECIMAL/CURRENCY for %s fails with %s\n", minGroupLocale, u_errorName(status));
+ } else {
+ const TestMinGroupItem* itemPtr;
+ unum_setAttribute(unumd, UNUM_MIN_FRACTION_DIGITS, 2);
+ for (itemPtr = minGroupItems; itemPtr->value != 0.0; itemPtr++) {
+ UChar ubuf[kUBufMax];
+ char bbufe[kBBufMax];
+ char bbufg[kBBufMax];
+ int32_t ulen;
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDecimal(unumd, itemPtr->string, -1, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDecimal DEC for locale %s, value %.1f fails with %s\n", minGroupLocale, itemPtr->value, u_errorName(status));
+ } else if (u_strcmp(ubuf, itemPtr->expectDecimal) != 0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expectDecimal, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDecimal DEC for locale %s, value %.1f, expected \"%s\" but got \"%s\"\n",
+ minGroupLocale, itemPtr->value, bbufe, bbufg);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unumd, itemPtr->value, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble DEC for locale %s, value %.1f fails with %s\n", minGroupLocale, itemPtr->value, u_errorName(status));
+ } else if (u_strcmp(ubuf, itemPtr->expectDecimal) != 0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expectDecimal, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble DEC for locale %s, value %.1f, expected \"%s\" but got \"%s\"\n",
+ minGroupLocale, itemPtr->value, bbufe, bbufg);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unumc, itemPtr->value, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble CUR for locale %s, value %.1f fails with %s\n", minGroupLocale, itemPtr->value, u_errorName(status));
+ } else if (u_strcmp(ubuf, itemPtr->expectCurrency) != 0) {
+ u_strToUTF8(bbufe, kBBufMax, NULL, itemPtr->expectCurrency, -1, &status);
+ u_strToUTF8(bbufg, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble CUR for locale %s, value %.1f, expected \"%s\" but got \"%s\"\n",
+ minGroupLocale, itemPtr->value, bbufe, bbufg);
+ }
+ }
+ unum_close(unumc);
+ unum_close(unumd);
+ }
+}
+
+// Apple only for <rdar://problem/49120648>
+static const char* const numSysLocales[] = {
+ "en_US", "en_US@numbers=arab", "en_US@numbers=roman", "en_US@numbers=grek", "en_US@numbers=hebr",
+ "zh@numbers=hanidec", "zh_Hant@numbers=traditional", "en@numbers=finance", NULL
+};
+static void TestNumberSystemsMultiplier(void) {
+ const char* const* localesPtr = numSysLocales;
+ const char* locale;
+ while ((locale = *localesPtr++) != NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat *unum = unum_open(UNUM_DECIMAL, NULL, 0, locale, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_DECIMAL for %s fails with %s\n", locale, u_errorName(status));
+ } else {
+ int32_t multiplier = unum_getAttribute(unum, UNUM_MULTIPLIER);
+ log_info("for locale %s with UNUM_DECIMAL, UNUM_MULTIPLIER is %d\n", locale, multiplier);
+ unum_close(unum);
+ }
+ }
+}
+
+// Apple only for <rdar://problem/39156484>
+typedef struct {
+ const char* locale;
+ const UChar* stringToParse;
+ double expectValue;
+ int32_t expectPos;
+} ParseScientificItem;
+static const ParseScientificItem parseSciItems[] = {
+ { "en_US", u"4E\u200E+05", 400000.0, 6 },
+ { "en_US", u"4E+05", 400000.0, 5 },
+ { "en_US", u"4E\u200E-02", 0.04, 6 },
+ { "en_US", u"4E-02", 0.04, 5 },
+ { "he_IL", u"4E\u200E+05", 400000.0, 6 },
+ { "he_IL", u"4E+05", 400000.0, 5 },
+ { "he_IL", u"4E\u200E-02", 0.04, 6 },
+ { "he_IL", u"4E-02", 0.04, 5 },
+ { "en@numbers=arabext", u"\u06F4\u00D7\u06F1\u06F0^\u200E+\u200E\u06F0\u06F5", 400000.0, 10 },
+ { "en@numbers=arabext", u"\u06F4\u00D7\u06F1\u06F0^+\u06F0\u06F5", 400000.0, 8 },
+ { NULL,NULL, 0.0, 0 } // terminator
+};
+static void TestParseScientific(void) {
+ const ParseScientificItem* itemPtr;
+ for (itemPtr = parseSciItems; itemPtr->locale != NULL; itemPtr++) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat *unum = unum_open(UNUM_SCIENTIFIC, NULL, 0, itemPtr->locale, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_SCIENTIFIC for %s fails with %s\n", itemPtr->locale, u_errorName(status));
+ } else {
+ int32_t parsePos = 0;
+ double result = unum_parseDouble(unum, itemPtr->stringToParse, -1, &parsePos, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_parseDouble for %s fails with %s\n", itemPtr->locale, u_errorName(status));
+ } else if (result != itemPtr->expectValue || parsePos != itemPtr->expectPos) {
+ log_err("unum_parseDouble for %s, expected result %.1f pos %d, got %.1f %d\n",
+ itemPtr->locale, itemPtr->expectValue, itemPtr->expectPos, result, parsePos);
+ }
+ unum_close(unum);
+ }
+ }
+}
+
+// Apple only for <rdar://problem/51985640>
+typedef struct {
+ const char* locale;
+ const UChar* expCurrCode;
+} LocaleCurrCodeItem;
+static const LocaleCurrCodeItem currCodeItems[] = {
+ { "en", u"" }, // curr ICU has "XXX"
+ { "en@currency=EUR", u"EUR" },
+ { "en_US", u"USD" },
+ { "en_ZZ", u"XAG" },
+ { "_US", u"USD" },
+ { "_ZZ", u"XAG" },
+ { "us", u"" }, // curr ICU has "XXX"
+ { "", u"" }, // curr ICU has "XXX"
+ { NULL, NULL }
+};
+static void TestCurrForUnkRegion(void) {
+ const LocaleCurrCodeItem* itemPtr;
+ for (itemPtr = currCodeItems; itemPtr->locale != NULL; itemPtr++) {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unum = unum_open(UNUM_CURRENCY, NULL, 0, itemPtr->locale, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_CURRENCY for %s fails with %s\n", itemPtr->locale, u_errorName(status));
+ } else {
+ UChar ubuf[kUBufMax];
+ int32_t ulen = unum_getTextAttribute(unum, UNUM_CURRENCY_CODE, ubuf, kUBufMax, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_getTextAttribute UNUM_CURRENCY_CODE for %s fails with %s\n", itemPtr->locale, u_errorName(status));
+ } else if (u_strcmp(ubuf,itemPtr->expCurrCode)!=0) {
+ char bexp[kBBufMax];
+ char bget[kBBufMax];
+ u_strToUTF8(bexp, kBBufMax, NULL, itemPtr->expCurrCode, -1, &status);
+ u_strToUTF8(bget, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_getTextAttribute UNUM_CURRENCY_CODE error for %s, expect \"%s\" but get \"%s\"\n", itemPtr->locale, bexp, bget);
+ }
+ status = U_ZERO_ERROR;
+ unum_setTextAttribute(unum, UNUM_CURRENCY_CODE, u"XXX", 3, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_setTextAttribute UNUM_CURRENCY_CODE for %s fails with %s\n", itemPtr->locale, u_errorName(status));
+ } else {
+ ulen = unum_getTextAttribute(unum, UNUM_CURRENCY_CODE, ubuf, kUBufMax, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_getTextAttribute UNUM_CURRENCY_CODE XXX for %s fails with %s\n", itemPtr->locale, u_errorName(status));
+ } else if (u_strcmp(ubuf,u"XXX")!=0) {
+ char bget[kBBufMax];
+ u_strToUTF8(bget, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_getTextAttribute UNUM_CURRENCY_CODE error for %s, expect XXX but get \"%s\"\n", itemPtr->locale, bget);
+ }
+ }
+ unum_close(unum);
+ }
+ }
+}
+
+static void TestMinIntMinFracZero(void) {
+ UChar ubuf[kUBufMax];
+ char bbuf[kBBufMax];
+ int minInt, minFrac, maxFrac, ulen;
+
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormat* unum = unum_open(UNUM_DECIMAL, NULL, 0, "en_US", NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_DECIMAL for en_US fails with %s\n", u_errorName(status));
+ } else {
+ unum_setAttribute(unum, UNUM_MIN_INTEGER_DIGITS, 0);
+ unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 0);
+ minInt = unum_getAttribute(unum, UNUM_MIN_INTEGER_DIGITS);
+ minFrac = unum_getAttribute(unum, UNUM_MIN_FRACTION_DIGITS);
+ if (minInt != 0 || minFrac != 0) {
+ log_err("after setting minInt=minFrac=0, get minInt %d, minFrac %d\n", minInt, minFrac);
+ }
+
+ ulen = unum_toPattern(unum, FALSE, ubuf, kUBufMax, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_toPattern fails with %s\n", u_errorName(status));
+ } else if (ulen < 3 || u_strstr(ubuf, u"#.#")==NULL) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("after setting minInt=minFrac=0, expect pattern to contain \"#.#\", but get (%d): \"%s\"\n", ulen, bbuf);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, 10.0, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble 10.0 ulen %d fails with %s\n", ulen, u_errorName(status));
+ } else if (u_strcmp(ubuf, u"10") != 0) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble 10.0 expected \"10\", got \"%s\"\n", bbuf);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, 0.9, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble 0.9 ulen %d fails with %s\n", ulen, u_errorName(status));
+ } else if (u_strcmp(ubuf, u".9") != 0) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble 0.9 expected \".9\", got \"%s\"\n", bbuf);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, 0.0, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble 0.0 ulen %d fails with %s\n", ulen, u_errorName(status));
+ } else if (u_strcmp(ubuf, u"0") != 0) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble 0.0 expected \"0\", got \"%s\"\n", bbuf);
+ }
+
+ unum_close(unum);
+ }
+
+ status = U_ZERO_ERROR;
+ unum = unum_open(UNUM_CURRENCY, NULL, 0, "en_US", NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_CURRENCY for en_US fails with %s\n", u_errorName(status));
+ } else {
+ unum_setAttribute(unum, UNUM_MIN_INTEGER_DIGITS, 0);
+ unum_setAttribute(unum, UNUM_MIN_FRACTION_DIGITS, 0);
+ minInt = unum_getAttribute(unum, UNUM_MIN_INTEGER_DIGITS);
+ minFrac = unum_getAttribute(unum, UNUM_MIN_FRACTION_DIGITS);
+ if (minInt != 0 || minFrac != 0) {
+ log_err("after setting CURRENCY minInt=minFrac=0, get minInt %d, minFrac %d\n", minInt, minFrac);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, 10.0, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble (CURRRENCY) 10.0 ulen %d fails with %s\n", ulen, u_errorName(status));
+ } else if (u_strcmp(ubuf, u"$10") != 0) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble (CURRRENCY) 10.0 expected \"$10\", got \"%s\"\n", bbuf);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, 0.9, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble (CURRRENCY) 0.9 ulen %d fails with %s\n", ulen, u_errorName(status));
+ } else if (u_strcmp(ubuf, u"$.9") != 0) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble (CURRRENCY) 0.9 expected \"$.9\", got \"%s\"\n", bbuf);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, 0.0, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble (CURRRENCY) 0.0 ulen %d fails with %s\n", ulen, u_errorName(status));
+ } else if (u_strcmp(ubuf, u"$0") != 0) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble (CURRRENCY) 0.0 expected \"$0\", got \"%s\"\n", bbuf);
+ }
+
+ unum_close(unum);
+ }
+
+ // addition for rdar://57291456
+ status = U_ZERO_ERROR;
+ unum = unum_open(UNUM_PATTERN_DECIMAL, NULL, 0, "en_IE", NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open UNUM_DECIMAL for en_US fails with %s\n", u_errorName(status));
+ } else {
+ unum_setAttribute(unum, UNUM_MIN_INTEGER_DIGITS, 0);
+ unum_setAttribute(unum, UNUM_MAX_FRACTION_DIGITS, 1);
+ minInt = unum_getAttribute(unum, UNUM_MIN_INTEGER_DIGITS);
+ maxFrac = unum_getAttribute(unum, UNUM_MAX_FRACTION_DIGITS);
+ if (minInt != 0 || maxFrac != 1) {
+ log_err("after setting minInt=0, maxFrac=1, get minInt %d, maxFrac %d\n", minInt, maxFrac);
+ }
+
+ status = U_ZERO_ERROR;
+ ulen = unum_formatDouble(unum, 0.0, ubuf, kUBufMax, NULL, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDouble (maxFrac 1) 0.0 ulen %d fails with %s\n", ulen, u_errorName(status));
+ } else if (u_strcmp(ubuf, u".0") != 0) {
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ log_err("unum_formatDouble (maxFrac 1) 0.0 expected \".0\", got \"%s\"\n", bbuf);
+ }
+
+ unum_close(unum);
+ }
+}
+
+#if APPLE_ADDITIONS
+#include <stdio.h>
+#if U_PLATFORM_IS_DARWIN_BASED
+#include <unistd.h>
+#include <mach/mach_time.h>
+#define GET_START() mach_absolute_time()
+#define GET_DURATION(start, info) ((mach_absolute_time() - start) * info.numer)/info.denom
+#elif !U_PLATFORM_HAS_WIN32_API
+#include <unistd.h>
+#include "putilimp.h"
+#define GET_START() (uint64_t)uprv_getUTCtime()
+#define GET_DURATION(start, info) ((uint64_t)uprv_getUTCtime() - start)
+#else
+#include "putilimp.h"
+#define GET_START() (unsigned long long)uprv_getUTCtime()
+#define GET_DURATION(start, info) ((unsigned long long)uprv_getUTCtime() - start)
+#endif
+
+// Apple only for <rdar://problem/51672521>
+
+static void TestFormatDecPerf(void) {
+ static const char* locales[] =
+ { "en_US", "ar_EG", "ar_EG@numbers=latn", "zh_CN@numbers=traditional", NULL };
+ static const UNumberFormatStyle styles[] =
+ { UNUM_DECIMAL, UNUM_CURRENCY, UNUM_PATTERN_DECIMAL, UNUM_FORMAT_STYLE_COUNT };
+ static const char* values[] =
+ { "123.4", "234.5", "345.6", "456.7", NULL };
+ static const UChar* pattern = u"#";
+#if U_PLATFORM_IS_DARWIN_BASED
+ mach_timebase_info_data_t info;
+ mach_timebase_info(&info);
+#endif
+ const char** localesPtr = locales;
+ const char* locale;
+ while ((locale = *localesPtr++) != NULL) {
+ const UNumberFormatStyle* stylesPtr = styles;
+ UNumberFormatStyle style;
+ while ((style = *stylesPtr++) < UNUM_FORMAT_STYLE_COUNT) {
+#if !U_PLATFORM_HAS_WIN32_API
+ uint64_t start, duration;
+#else
+ unsigned long long start, duration;
+#endif
+ UErrorCode status = U_ZERO_ERROR;
+ const UChar* patternPtr = (style == UNUM_PATTERN_DECIMAL)? pattern: NULL;
+ int32_t patternLen = (style == UNUM_PATTERN_DECIMAL)? u_strlen(pattern): 0;
+ UNumberFormat *unum;
+
+ start = GET_START();
+ unum = unum_open(style, patternPtr, patternLen, locale, NULL, &status);
+ duration = GET_DURATION(start, info);
+ log_info("== start locale %s, style %d\n", locale, (int)style);
+ if ( U_FAILURE(status) ) {
+ log_data_err("unum_open fails with %s\n", u_errorName(status));
+ } else {
+ log_info("unum_open nsec %5llu\n", duration);
+ const char** valuesPtr = values;
+ const char* value;
+ while ((value = *valuesPtr++) != NULL) {
+ UChar ubuf[kUBufMax];
+ char bbuf[kBBufMax];
+ int32_t ulen;
+
+ status = U_ZERO_ERROR;
+ start = GET_START();
+ ulen = unum_formatDecimal(unum, value, strlen(value), ubuf, kUBufMax, NULL, &status);
+ duration = GET_DURATION(start, info);
+ u_strToUTF8(bbuf, kBBufMax, NULL, ubuf, ulen, &status);
+ if ( U_FAILURE(status) ) {
+ log_err("unum_formatDecimal %s fails with %s\n", value, u_errorName(status));
+ } else {
+ log_info("unum_formatDecimal %s, nsec %5llu, result %s\n", value, duration, bbuf);
+ }
+ }
+ unum_close(unum);
+ }
+ }
+ }
+}
+
+// Apple only for <rdar://problem/54886964> and <rdar://problem/62544359>
+
+static void TestCountryFallback(void) {
+ // Locales to check fallback behavior for-- the first column (subscript % 3 == 0)
+ // is the locale to test with; the second column (subscript % 3 == 1) is the locale
+ // whose number-formatting properties we should be matching when we use the test locale,
+ // and the third column (subscript % 3 == 2) is the locale we should be inheriting
+ // certain language-dependent symbols from (generally, the locale you'd normally get
+ // as a fallback).
+ // NOTE: The currency format always follows the country locale, except for the Euro in a handful
+ // of countries, where it follows the US English currency format. If the country-fallback locale
+ // ID begins with a *, that's a signal that the currency format comes from the language locale
+ // instead of the country locale.
+ char* testLocales[] = {
+ // locales we still have synthetic resources for that were working okay to begin with
+ "en_BG", "bg_BG", "en_150",
+ "en_CN", "zh_CN", "en_001",
+ "en_CZ", "cs_CZ", "en_CZ",
+ "en_JP", "ja_JP", "en_001",
+ "en_KR", "ko_KR", "en_001",
+ "en_TH", "th_TH", "en_001",
+ "en_TW", "zh_TW", "en_001",
+
+ // locales that were okay except for currency-pattern issues
+ // (note that we have to specify the language locale as en, not en_150-- the en_150 currency format is wrong too)
+ "en_EE", "*et_EE", "en",
+ "en_LT", "*lt_LT", "en_LT",
+ "en_LV", "*lv_LV", "en",
+ "en_PT", "*pt_PT", "en",
+ "en_SK", "*sk_SK", "en_SK",
+ "en_FR", "*fr_FR", "en_FR",
+
+ // locales we still have synthetic resources for that we had to modify to match the country-fallback results
+ "en_AL", "sq_AL", "en_150",
+ "en_AR", "es_AR", "en_AR",
+ "en_BD", "bn_BD@numbers=latn", "en_BD",
+ "en_BR", "pt_BR", "en_001",
+ "en_CL", "es_CL", "en_CL",
+ "en_CO", "es_CO", "en_CO",
+ "en_GR", "el_GR", "en_150",
+ "en_HU", "hu_HU", "en_150",
+ "en_ID", "id_ID", "en_001",
+ "en_MM", "my_MM@numbers=latn", "en_001",
+ "en_MV", "dv_MV", "en_001",
+ "en_MX", "es_MX", "en_001",
+ "en_RU", "ru_RU", "en_RU",
+ "en_TR", "tr_TR", "en_TR",
+ "en_UA", "uk_UA", "en_150",
+ "es_AG", "en_AG", "es_AG",
+ "es_BB", "en_BB", "es_BB",
+ "es_BM", "en_BM", "es_BM",
+ "es_BQ", "nl_BQ", "es_BQ",
+ "es_BS", "en_BS", "es_BS",
+ "es_CA", "en_CA", "es_CA",
+ "es_CW", "nl_CW", "es_CW",
+ "es_DM", "en_DM", "es_DM",
+ "es_GD", "en_GD", "es_GD",
+ "es_GY", "en_GY", "es_GY",
+ "es_HT", "fr_HT", "es_HT",
+ "es_KN", "en_KN", "es_KN",
+ "es_KY", "en_KY", "es_KY",
+ "es_LC", "en_LC", "es_LC",
+ "es_TC", "en_TC", "es_TC",
+ "es_TT", "en_TT", "es_TT",
+ "es_VC", "en_VC", "es_VC",
+ "es_VG", "en_VG", "es_VG",
+ "es_VI", "en_VI", "es_VI",
+
+ // locales we used to have synthetic resources for but don't anymore
+ "en_AD", "ca_AD", "en_150",
+ "en_BA", "bs_BA", "en_150",
+ "en_ES", "es_ES", "en_150",
+ "en_HR", "hr_HR", "en_150",
+ "en_IS", "is_IS", "en_150",
+ "en_IT", "it_IT", "en_150",
+ "en_LU", "fr_LU", "en_150",
+ "en_ME", "sr_ME", "en_150",
+ "en_NO", "nb_NO", "en_NO",
+ "en_PL", "pl_PL", "en_150",
+ "en_RO", "ro_RO", "en_150",
+ "en_RS", "sr_RS", "en_150",
+ "en_SA", "ar_SA@numbers=latn", "en",
+ "es_AI", "en_AI", "es_419",
+ "es_AW", "nl_AW", "es_419",
+ "es_BL", "fr_BL", "es_419",
+ "es_FK", "en_FK", "es_419",
+ "es_GF", "fr_GF", "es_419",
+ "es_GL", "kl_GL", "es",
+ "es_GP", "fr_GP", "es_419",
+ "es_MF", "fr_MF", "es_419",
+ "es_MQ", "fr_MQ", "es_419",
+ "es_MS", "en_MS", "es_419",
+ "es_PM", "fr_PM", "es_419",
+ "es_SR", "nl_SR", "es_419",
+ "es_SX", "en_SX", "es_419",
+
+ // locales we have synthetic resources for, but they come from open-source ICU
+ "en_DE", "de_DE", "en_DE",
+ "en_ER", "ti_ER", "en_001",
+ "en_GH", "ak_GH", "en_001",
+ "en_HK", "zh_HK", "en_001",
+ "en_ME", "sr_ME", "en_150",
+ "en_MO", "zh_MO", "en_001",
+ "en_MT", "mt_MT", "en_150",
+ "en_MY", "ms_MY", "en_001",
+ "en_NL", "nl_NL", "en_150",
+ "en_PH", "fil_PH", "en_001",
+ "en_ZW", "sn_ZW", "en_001",
+ "es_US", "en_US", "es_US",
+
+ // locales where we're adding a NEW synthetic resource bundle, to PREVENT us from falling back by country
+ "en_BN", "en_001", "en_001", // rdar://69272236
+
+ // locales specifically mentioned in Radars
+ "fr_US", "en_US", "fr", // rdar://problem/54886964
+// "en_TH", "th_TH", "en", // rdar://problem/29299919 (listed above)
+// "en_BG", "bg_BG", "en", // rdar://problem/29299919 (listed above)
+ "en_LI", "de_LI", "en", // rdar://problem/29299919
+ "en_MC", "fr_MC", "en", // rdar://problem/29299919
+ "en_MD", "ro_MD", "en", // rdar://problem/29299919
+ "en_VA", "it_VA", "en", // rdar://problem/29299919
+ "fr_GB", "en_GB", "fr", // rdar://problem/36020946
+ "fr_CN", "zh_CN", "fr", // rdar://problem/50083902
+ "es_IE", "en_IE", "es", // rdar://problem/58733843
+ "en_LB", "ar_LB@numbers=latn", "en_001", // rdar://66609948
+
+ // tests for situations where the default number system is different depending on whether you
+ // fall back by language or by country:
+// "en_SA", "ar_SA@numbers=latn", "en", // (listed above)
+ "ar_US", "en_US@numbers=arab", "ar",
+ "en_SA@numbers=arab", "ar_SA", "en@numbers=arab",
+ "ar_US@numbers=latn", "en_US", "ar@numbers=latn",
+
+ // tests for situations where the original locale ID specifies a script:
+ "sr_Cyrl_SA", "ar_SA@numbers=latn", "sr_Cyrl",
+ "ru_Cyrl_BA", "bs_Cyrl_BA", "ru",
+
+ // a few additional arbitrary combinations:
+ "ja_US", "en_US", "ja",
+ "fr_DE", "de_DE", "fr",
+ "de_FR", "fr_FR", "de",
+ "es_TW", "zh_TW", "es",
+
+ // not in the resources, but to exercise the new ures_openWithCountryFallback() logic (AQ's default language is "und")
+ "fr_AQ", "en", "fr",
+
+ // test to make sure that nothing goes wrong if language and country fallback both lead to the same resource
+ // (This won't happen for any "real" locales, because ICU has resources for all of them, but we can fake it with
+ // a nonexistent country code such as QQ.)
+ "en_QQ", "en", "en"
+ };
+
+ for (int32_t i = 0; i < (sizeof(testLocales) / sizeof(char*)); i += 3) {
+ const char* testLocale = testLocales[i];
+ const char* fallbackLocale = testLocales[i + 1];
+ const char* languageLocale = testLocales[i + 2];
+ char errorMessage[200];
+ UBool currencyFollowsLanguage = FALSE;
+ UErrorCode err = U_ZERO_ERROR;
+
+ if (fallbackLocale[0] == '*') {
+ currencyFollowsLanguage = TRUE;
+ ++fallbackLocale;
+ }
+
+ // Check that the decimal, percentage, currency, and scientific formatting patterns
+ // for the test locale are the same as those for the fallback locale.
+ for (UNumberFormatStyle style = UNUM_DECIMAL; style <= UNUM_SCIENTIFIC; ++style) {
+ err = U_ZERO_ERROR;
+ UNumberFormat* testFormatter = unum_open(style, NULL, -1, testLocale, NULL, &err);
+ UNumberFormat* fallbackFormatter = unum_open(style, NULL, -1, fallbackLocale, NULL, &err);
+ UNumberFormat* languageLocaleFormatter = unum_open(style, NULL, -1, languageLocale, NULL, &err);
+
+ sprintf(errorMessage, "Error creating formatters for %s and %s", testLocale, fallbackLocale);
+ if (assertSuccess(errorMessage, &err)) {
+ UChar testPattern[100];
+ UChar fallbackPattern[100];
+
+ unum_toPattern(testFormatter, FALSE, testPattern, 100, &err);
+ if (style == UNUM_PERCENT || (style == UNUM_CURRENCY && currencyFollowsLanguage)) {
+ unum_toPattern(languageLocaleFormatter, FALSE, fallbackPattern, 100, &err);
+ } else {
+ unum_toPattern(fallbackFormatter, FALSE, fallbackPattern, 100, &err);
+ }
+
+ if (assertSuccess("Error getting number format patterns", &err)) {
+ sprintf(errorMessage, "In %s, formatting pattern for style %d doesn't match", testLocale, style);
+ assertUEquals(errorMessage, fallbackPattern, testPattern);
+ }
+ }
+ unum_close(testFormatter);
+ unum_close(fallbackFormatter);
+ unum_close(languageLocaleFormatter);
+ }
+
+ // Check that all of the number formatting symbols for the test locale (except for the
+ // currency, exponential, infinity, and NaN symbols) are the same as those for the fallback locale.
+ UNumberFormat* testFormatter = unum_open(UNUM_DECIMAL, NULL, -1, testLocale, NULL, &err);
+ UNumberFormat* fallbackFormatter = unum_open(UNUM_DECIMAL, NULL, -1, fallbackLocale, NULL, &err);
+ UNumberFormat* languageLocaleFormatter = unum_open(UNUM_DECIMAL, NULL, -1, languageLocale, NULL, &err);
+ if (assertSuccess("Error creating formatters", &err)) {
+ for (UNumberFormatSymbol symbol = UNUM_DECIMAL_SEPARATOR_SYMBOL; symbol < UNUM_FORMAT_SYMBOL_COUNT; ++symbol) {
+ UBool compareToFallbackLocale = TRUE;
+ switch (symbol) {
+ case UNUM_CURRENCY_SYMBOL:
+ case UNUM_INTL_CURRENCY_SYMBOL:
+ case UNUM_MONETARY_SEPARATOR_SYMBOL:
+ case UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL:
+ // we don't do anything special with currency here
+ continue;
+ case UNUM_PLUS_SIGN_SYMBOL:
+ case UNUM_MINUS_SIGN_SYMBOL:
+ case UNUM_PERCENT_SYMBOL:
+ case UNUM_EXPONENTIAL_SYMBOL:
+ case UNUM_INFINITY_SYMBOL:
+ case UNUM_NAN_SYMBOL:
+ // unlike most number symbols, these should follow the language
+ compareToFallbackLocale = FALSE;
+ break;
+ default:
+ // everything else follows the country
+ compareToFallbackLocale = TRUE;
+ break;
+ }
+
+ UChar testSymbol[50];
+ UChar fallbackSymbol[50];
+
+ err = U_ZERO_ERROR;
+ unum_getSymbol(testFormatter, symbol, testSymbol, 50, &err);
+ if (compareToFallbackLocale) {
+ unum_getSymbol(fallbackFormatter, symbol, fallbackSymbol, 50, &err);
+ } else {
+ unum_getSymbol(languageLocaleFormatter, symbol, fallbackSymbol, 50, &err);
+ }
+
+ if (assertSuccess("Error getting number format symbol", &err)) {
+ sprintf(errorMessage, "In %s, formatting symbol #%d doesn't match the fallback", testLocale, symbol);
+ assertUEquals(errorMessage, fallbackSymbol, testSymbol);
+ }
+ }
+ }
+ unum_close(testFormatter);
+ unum_close(fallbackFormatter);
+ unum_close(languageLocaleFormatter);
+ }
+}
+
+#endif /* APPLE_ADDITIONS */
+