From: Shane Carr Date: Wed, 18 Apr 2018 03:59:39 +0000 (+0000) Subject: ICU-13634 Adding "formatFailIfMoreThanMaxDigits" property to C++ NumberFormatter... X-Git-Tag: release-62-rc~200^2~26 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3c9b343282b3841c440860ccd8ff6c51a092dfe6;p=icu ICU-13634 Adding "formatFailIfMoreThanMaxDigits" property to C++ NumberFormatter. Changing getSecondaryGroupingSize() for backwards compatibility. Other assorted test changes. X-SVN-Rev: 41239 --- diff --git a/icu4c/source/i18n/decimfmt.cpp b/icu4c/source/i18n/decimfmt.cpp index 1708b0d2e81..43399d1a139 100644 --- a/icu4c/source/i18n/decimfmt.cpp +++ b/icu4c/source/i18n/decimfmt.cpp @@ -221,11 +221,14 @@ DecimalFormat::setAttribute(UNumberFormatAttribute attr, int32_t newValue, UErro setSignAlwaysShown(static_cast(newValue)); break; + case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: + setFormatFailIfMoreThanMaxDigits(static_cast(newValue)); + break; + default: status = U_UNSUPPORTED_ERROR; break; } - // TODO: UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS? return *this; } @@ -312,6 +315,9 @@ int32_t DecimalFormat::getAttribute(UNumberFormatAttribute attr, UErrorCode& sta case UNUM_SIGN_ALWAYS_SHOWN: return isSignAlwaysShown(); + case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: + return isFormatFailIfMoreThanMaxDigits(); + default: status = U_UNSUPPORTED_ERROR; break; @@ -775,9 +781,8 @@ void DecimalFormat::setGroupingSize(int32_t newValue) { } int32_t DecimalFormat::getSecondaryGroupingSize(void) const { - int grouping1 = fProperties->groupingSize; int grouping2 = fProperties->secondaryGroupingSize; - if (grouping1 == grouping2 || grouping2 < 0) { + if (grouping2 < 0) { return 0; } return grouping2; @@ -833,6 +838,15 @@ void DecimalFormat::setParseCaseSensitive(UBool value) { refreshFormatterNoError(); } +UBool DecimalFormat::isFormatFailIfMoreThanMaxDigits() const { + return fProperties->formatFailIfMoreThanMaxDigits; +} + +void DecimalFormat::setFormatFailIfMoreThanMaxDigits(UBool value) { + fProperties->formatFailIfMoreThanMaxDigits = value; + refreshFormatterNoError(); +} + UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const { // Pull some properties from exportedProperties and others from properties // to keep affix patterns intact. In particular, pull rounding properties diff --git a/icu4c/source/i18n/number_decimfmtprops.cpp b/icu4c/source/i18n/number_decimfmtprops.cpp index 10a9eb00293..a292197e42a 100644 --- a/icu4c/source/i18n/number_decimfmtprops.cpp +++ b/icu4c/source/i18n/number_decimfmtprops.cpp @@ -23,6 +23,7 @@ void DecimalFormatProperties::clear() { decimalPatternMatchRequired = false; decimalSeparatorAlwaysShown = false; exponentSignAlwaysShown = false; + formatFailIfMoreThanMaxDigits = false; formatWidth = -1; groupingSize = -1; groupingUsed = true; @@ -68,6 +69,7 @@ bool DecimalFormatProperties::operator==(const DecimalFormatProperties &other) c eq = eq && decimalPatternMatchRequired == other.decimalPatternMatchRequired; eq = eq && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown; eq = eq && exponentSignAlwaysShown == other.exponentSignAlwaysShown; + eq = eq && formatFailIfMoreThanMaxDigits == other.formatFailIfMoreThanMaxDigits; eq = eq && formatWidth == other.formatWidth; eq = eq && groupingSize == other.groupingSize; eq = eq && groupingUsed == other.groupingUsed; diff --git a/icu4c/source/i18n/number_decimfmtprops.h b/icu4c/source/i18n/number_decimfmtprops.h index 8e823a80067..067c148ac75 100644 --- a/icu4c/source/i18n/number_decimfmtprops.h +++ b/icu4c/source/i18n/number_decimfmtprops.h @@ -96,6 +96,7 @@ struct U_I18N_API DecimalFormatProperties { bool decimalPatternMatchRequired; bool decimalSeparatorAlwaysShown; bool exponentSignAlwaysShown; + bool formatFailIfMoreThanMaxDigits; // ICU4C-only int32_t formatWidth; int32_t groupingSize; bool groupingUsed; diff --git a/icu4c/source/i18n/number_integerwidth.cpp b/icu4c/source/i18n/number_integerwidth.cpp index 87e543622cc..fa9991be0fa 100644 --- a/icu4c/source/i18n/number_integerwidth.cpp +++ b/icu4c/source/i18n/number_integerwidth.cpp @@ -14,14 +14,15 @@ using namespace icu; using namespace icu::number; using namespace icu::number::impl; -IntegerWidth::IntegerWidth(digits_t minInt, digits_t maxInt) { +IntegerWidth::IntegerWidth(digits_t minInt, digits_t maxInt, bool formatFailIfMoreThanMaxDigits) { fUnion.minMaxInt.fMinInt = minInt; fUnion.minMaxInt.fMaxInt = maxInt; + fUnion.minMaxInt.fFormatFailIfMoreThanMaxDigits = formatFailIfMoreThanMaxDigits; } IntegerWidth IntegerWidth::zeroFillTo(int32_t minInt) { if (minInt >= 0 && minInt <= kMaxIntFracSig) { - return {static_cast(minInt), -1}; + return {static_cast(minInt), -1, false}; } else { return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } @@ -31,9 +32,9 @@ IntegerWidth IntegerWidth::truncateAt(int32_t maxInt) { if (fHasError) { return *this; } // No-op on error digits_t minInt = fUnion.minMaxInt.fMinInt; if (maxInt >= 0 && maxInt <= kMaxIntFracSig && minInt <= maxInt) { - return {minInt, static_cast(maxInt)}; + return {minInt, static_cast(maxInt), false}; } else if (maxInt == -1) { - return {minInt, -1}; + return {minInt, -1, false}; } else { return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR}; } @@ -45,6 +46,11 @@ void IntegerWidth::apply(impl::DecimalQuantity& quantity, UErrorCode& status) co } else if (fUnion.minMaxInt.fMaxInt == -1) { quantity.setIntegerLength(fUnion.minMaxInt.fMinInt, INT32_MAX); } else { + // Enforce the backwards-compatibility feature "FormatFailIfMoreThanMaxDigits" + if (fUnion.minMaxInt.fFormatFailIfMoreThanMaxDigits && + fUnion.minMaxInt.fMaxInt < quantity.getMagnitude()) { + status = U_ILLEGAL_ARGUMENT_ERROR; + } quantity.setIntegerLength(fUnion.minMaxInt.fMinInt, fUnion.minMaxInt.fMaxInt); } } diff --git a/icu4c/source/i18n/number_mapper.cpp b/icu4c/source/i18n/number_mapper.cpp index 22fe87fc67c..b2cad1224d7 100644 --- a/icu4c/source/i18n/number_mapper.cpp +++ b/icu4c/source/i18n/number_mapper.cpp @@ -159,7 +159,10 @@ MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& propert // INTEGER WIDTH // /////////////////// - macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt); + macros.integerWidth = IntegerWidth( + static_cast(minInt), + static_cast(maxInt), + properties.formatFailIfMoreThanMaxDigits); /////////////////////// // GROUPING STRATEGY // diff --git a/icu4c/source/i18n/number_patternstring.cpp b/icu4c/source/i18n/number_patternstring.cpp index e96f958da0f..f967fb4bba6 100644 --- a/icu4c/source/i18n/number_patternstring.cpp +++ b/icu4c/source/i18n/number_patternstring.cpp @@ -819,7 +819,10 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP sb.append(AffixUtils::escape(UnicodeStringCharSequence(np))); // Copy the positive digit format into the negative. // This is optional; the pattern is the same as if '#' were appended here instead. - sb.append(sb, afterPrefixPos, beforeSuffixPos - afterPrefixPos); + // NOTE: It is not safe to append the UnicodeString to itself, so we need to copy. + // See http://bugs.icu-project.org/trac/ticket/13707 + UnicodeString copy(sb); + sb.append(copy, afterPrefixPos, beforeSuffixPos - afterPrefixPos); if (!nsp.isBogus()) { sb.append(nsp); } diff --git a/icu4c/source/i18n/unicode/decimfmt.h b/icu4c/source/i18n/unicode/decimfmt.h index 1ce1ea93981..d59e50d06d0 100644 --- a/icu4c/source/i18n/unicode/decimfmt.h +++ b/icu4c/source/i18n/unicode/decimfmt.h @@ -1724,7 +1724,7 @@ class U_I18N_API DecimalFormat : public NumberFormat { * @param value true to prevent exponents from being parsed; false to allow them to be parsed. * @internal This API is a technical preview. It may change in an upcoming release. */ - void setParseNoExponent(UBool value); + virtual void setParseNoExponent(UBool value); /** * {@icu} Returns whether parsing is sensitive to case (lowercase/uppercase). @@ -1738,11 +1738,30 @@ class U_I18N_API DecimalFormat : public NumberFormat { * {@icu} Whether to pay attention to case when parsing; default is to ignore case (perform * case-folding). For example, "A" == "a" in case-insensitive but not case-sensitive mode. * - * Currency codes are never case-folded. For example, "us$1.00" will not parse in case-insensitive + * Currency symbols are never case-folded. For example, "us$1.00" will not parse in case-insensitive * mode, even though "US$1.00" parses. + * + * @internal This API is a technical preview. It may change in an upcoming release. */ void setParseCaseSensitive(UBool value); + /** + * {@icu} Returns whether truncation of high-order integer digits should result in an error. + * By default, setMaximumIntegerDigits truncates high-order digits silently. + * + * @see setFormatFailIfMoreThanMaxDigits + * @internal This API is a technical preview. It may change in an upcoming release. + */ + UBool isFormatFailIfMoreThanMaxDigits() const; + + /** + * {@icu} Sets whether truncation of high-order integer digits should result in an error. + * By default, setMaximumIntegerDigits truncates high-order digits silently. + * + * @internal This API is a technical preview. It may change in an upcoming release. + */ + virtual void setFormatFailIfMoreThanMaxDigits(UBool value); + /** * Synthesizes a pattern string that represents the current state diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h index 897e4c45eb9..5f4a6bb7ac5 100644 --- a/icu4c/source/i18n/unicode/numberformatter.h +++ b/icu4c/source/i18n/unicode/numberformatter.h @@ -918,12 +918,13 @@ class U_I18N_API IntegerWidth : public UMemory { struct { impl::digits_t fMinInt; impl::digits_t fMaxInt; + bool fFormatFailIfMoreThanMaxDigits; } minMaxInt; UErrorCode errorCode; } fUnion; bool fHasError = false; - IntegerWidth(impl::digits_t minInt, impl::digits_t maxInt); + IntegerWidth(impl::digits_t minInt, impl::digits_t maxInt, bool formatFailIfMoreThanMaxDigits); IntegerWidth(UErrorCode errorCode) { // NOLINT fUnion.errorCode = errorCode; @@ -962,6 +963,9 @@ class U_I18N_API IntegerWidth : public UMemory { // To allow NumberFormatterImpl to access isBogus() and perform other operations: friend class impl::NumberFormatterImpl; + // So that NumberPropertyMapper can create instances + friend class impl::NumberPropertyMapper; + // To allow access to the skeleton generation code: friend class impl::GeneratorHelpers; }; diff --git a/icu4c/source/test/cintltst/cnumtst.c b/icu4c/source/test/cintltst/cnumtst.c index d11cca78b3e..f6ad0340a0b 100644 --- a/icu4c/source/test/cintltst/cnumtst.c +++ b/icu4c/source/test/cintltst/cnumtst.c @@ -765,7 +765,8 @@ free(result); newvalue=2; unum_setAttribute(def, attr, newvalue); if(unum_getAttribute(def,attr) != 2) - log_err("error in setting and getting attributes for UNUM_SECONDARY_GROUPING_SIZE\n"); + log_err("error in setting and getting attributes for UNUM_SECONDARY_GROUPING_SIZE: got %d\n", + unum_getAttribute(def,attr)); else log_verbose("Pass:setting and getting attributes for UNUM_SECONDARY_GROUPING_SIZE works fine\n"); @@ -840,7 +841,8 @@ free(result); const char *bdpattern = "#,##0.#########"; const char *numInitial = "12345678900987654321.1234567896"; const char *numFormatted = "12,345,678,900,987,654,321.12345679"; - const char *parseExpected = "12345678900987654321.12345679"; + const char *parseExpected = "1.234567890098765432112345679E+19"; + const char *parseExpected2 = "3.4567890098765432112345679E+17"; int32_t resultSize = 0; int32_t parsePos = 0; /* Output parameter for Parse operations. */ #define DESTCAPACITY 100 @@ -898,9 +900,12 @@ free(result); if (U_FAILURE(status)) { log_err("File %s, Line %d, status = %s\n", __FILE__, __LINE__, u_errorName(status)); } - if (strcmp(parseExpected, desta) != 0) { + if (uprv_strcmp(parseExpected, desta) != 0) { log_err("File %s, Line %d, (expected, actual) = (\"%s\", \"%s\")\n", __FILE__, __LINE__, parseExpected, desta); + } else { + log_verbose("File %s, Line %d, got expected = \"%s\"\n", + __FILE__, __LINE__, desta); } if (strlen(parseExpected) != resultSize) { log_err("File %s, Line %d, (expected, actual) = (%d, %d)\n", @@ -917,9 +922,12 @@ free(result); if (U_FAILURE(status)) { log_err("File %s, Line %d, status = %s\n", __FILE__, __LINE__, u_errorName(status)); } - if (strcmp(parseExpected+2, desta) != 0) { /* "345678900987654321.12345679" */ + if (strcmp(parseExpected2, desta) != 0) { /* "3.4567890098765432112345679E+17" */ log_err("File %s, Line %d, (expected, actual) = (\"%s\", \"%s\")\n", - __FILE__, __LINE__, parseExpected+2, desta); + __FILE__, __LINE__, parseExpected2, desta); + } else { + log_verbose("File %s, Line %d, got expected = \"%s\"\n", + __FILE__, __LINE__, desta); } if (strlen(numFormatted) != parsePos) { log_err("File %s, Line %d, parsePos (expected, actual) = (\"%d\", \"%d\")\n", @@ -1016,9 +1024,12 @@ typedef struct { const char * descrip; const UChar * currStr; const UChar * plurStr; + // ICU 62: currencies are accepted in non-currency mode + /* UErrorCode parsDoubExpectErr; int32_t parsDoubExpectPos; double parsDoubExpectVal; + */ UErrorCode parsCurrExpectErr; int32_t parsCurrExpectPos; double parsCurrExpectVal; @@ -1026,29 +1037,29 @@ typedef struct { } ParseCurrencyItem; static const ParseCurrencyItem parseCurrencyItems[] = { - { "en_US", "dollars2", dollars2Sym, NULL, U_ZERO_ERROR, 5, 2.0, U_ZERO_ERROR, 5, 2.0, "USD" }, - { "en_US", "dollars4", dollars4Sym, dollars4PluEn, U_ZERO_ERROR, 2, 4.0, U_ZERO_ERROR, 2, 4.0, "USD" }, - { "en_US", "dollars9", dollars9Sym, NULL, U_PARSE_ERROR, 1, 0.0, U_PARSE_ERROR, 1, 0.0, "" }, - { "en_US", "pounds3", pounds3Sym, NULL, U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR, 5, 3.0, "GBP" }, - { "en_US", "pounds5", pounds5Sym, pounds5PluEn, U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR, 2, 5.0, "GBP" }, - { "en_US", "pounds7", pounds7Sym, NULL, U_PARSE_ERROR, 1, 0.0, U_PARSE_ERROR, 1, 0.0, "" }, - { "en_US", "euros8", euros8Sym, euros8PluEn, U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR, 2, 8.0, "EUR" }, - - { "en_GB", "pounds3", pounds3Sym, NULL, U_ZERO_ERROR, 5, 3.0, U_ZERO_ERROR, 5, 3.0, "GBP" }, - { "en_GB", "pounds5", pounds5Sym, pounds5PluEn, U_ZERO_ERROR, 2, 5.0, U_ZERO_ERROR, 2, 5.0, "GBP" }, - { "en_GB", "pounds7", pounds7Sym, NULL, U_PARSE_ERROR, 1, 0.0, U_PARSE_ERROR, 1, 0.0, "" }, - { "en_GB", "euros4", euros4Sym, NULL, U_PARSE_ERROR, 4, 0.0, U_PARSE_ERROR, 4, 0.0, "" }, - { "en_GB", "euros6", euros6Sym, NULL, U_PARSE_ERROR, 1, 0.0, U_PARSE_ERROR, 1, 0.0, "" }, - { "en_GB", "euros8", euros8Sym, euros8PluEn, U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR, 2, 8.0, "EUR" }, - { "en_GB", "dollars4", dollarsUS4Sym, dollars4PluEn, U_PARSE_ERROR, 0, 0.0, U_ZERO_ERROR, 4, 4.0, "USD" }, - - { "fr_FR", "euros4", euros4Sym, NULL, U_ZERO_ERROR, 6, 4.0, U_ZERO_ERROR, 6, 4.0, "EUR" }, - { "fr_FR", "euros6", euros6Sym, euros6PluFr, U_ZERO_ERROR, 3, 6.0, U_ZERO_ERROR, 3, 6.0, "EUR" }, - { "fr_FR", "euros8", euros8Sym, NULL, U_PARSE_ERROR, 0, 0.0, U_PARSE_ERROR, 0, 0.0, "" }, - { "fr_FR", "dollars2", dollars2Sym, NULL, U_PARSE_ERROR, 0, 0.0, U_PARSE_ERROR, 0, 0.0, "" }, - { "fr_FR", "dollars4", dollars4Sym, NULL, U_PARSE_ERROR, 0, 0.0, U_PARSE_ERROR, 0, 0.0, "" }, + { "en_US", "dollars2", dollars2Sym, NULL, /* U_ZERO_ERROR, 5, 2.0, */ U_ZERO_ERROR, 5, 2.0, "USD" }, + { "en_US", "dollars4", dollars4Sym, dollars4PluEn, /* U_ZERO_ERROR, 2, 4.0, */ U_ZERO_ERROR, 2, 4.0, "USD" }, + { "en_US", "dollars9", dollars9Sym, NULL, /* U_PARSE_ERROR, 1, 0.0, */ U_PARSE_ERROR, 1, 0.0, "" }, + { "en_US", "pounds3", pounds3Sym, NULL, /* U_PARSE_ERROR, 0, 0.0, */ U_ZERO_ERROR, 5, 3.0, "GBP" }, + { "en_US", "pounds5", pounds5Sym, pounds5PluEn, /* U_PARSE_ERROR, 0, 0.0, */ U_ZERO_ERROR, 2, 5.0, "GBP" }, + { "en_US", "pounds7", pounds7Sym, NULL, /* U_PARSE_ERROR, 1, 0.0, */ U_PARSE_ERROR, 1, 0.0, "" }, + { "en_US", "euros8", euros8Sym, euros8PluEn, /* U_PARSE_ERROR, 0, 0.0, */ U_ZERO_ERROR, 2, 8.0, "EUR" }, + + { "en_GB", "pounds3", pounds3Sym, NULL, /* U_ZERO_ERROR, 5, 3.0, */ U_ZERO_ERROR, 5, 3.0, "GBP" }, + { "en_GB", "pounds5", pounds5Sym, pounds5PluEn, /* U_ZERO_ERROR, 2, 5.0, */ U_ZERO_ERROR, 2, 5.0, "GBP" }, + { "en_GB", "pounds7", pounds7Sym, NULL, /* U_PARSE_ERROR, 1, 0.0, */ U_PARSE_ERROR, 1, 0.0, "" }, + { "en_GB", "euros4", euros4Sym, NULL, /* U_PARSE_ERROR, 4, 0.0, */ U_PARSE_ERROR, 0, 0.0, "" }, + { "en_GB", "euros6", euros6Sym, NULL, /* U_PARSE_ERROR, 1, 0.0, */ U_PARSE_ERROR, 1, 0.0, "" }, + { "en_GB", "euros8", euros8Sym, euros8PluEn, /* U_PARSE_ERROR, 0, 0.0, */ U_ZERO_ERROR, 2, 8.0, "EUR" }, + { "en_GB", "dollars4", dollarsUS4Sym,dollars4PluEn, /* U_PARSE_ERROR, 0, 0.0, */ U_ZERO_ERROR, 4, 4.0, "USD" }, + + { "fr_FR", "euros4", euros4Sym, NULL, /* U_ZERO_ERROR, 6, 4.0, */ U_ZERO_ERROR, 6, 4.0, "EUR" }, + { "fr_FR", "euros6", euros6Sym, euros6PluFr, /* U_ZERO_ERROR, 3, 6.0, */ U_ZERO_ERROR, 3, 6.0, "EUR" }, + { "fr_FR", "euros8", euros8Sym, NULL, /* U_PARSE_ERROR, 0, 0.0, */ U_PARSE_ERROR, 2, 0.0, "" }, + { "fr_FR", "dollars2", dollars2Sym, NULL, /* U_PARSE_ERROR, 0, 0.0, */ U_PARSE_ERROR, 0, 0.0, "" }, + { "fr_FR", "dollars4", dollars4Sym, NULL, /* U_PARSE_ERROR, 0, 0.0, */ U_PARSE_ERROR, 0, 0.0, "" }, - { NULL, NULL, NULL, NULL, 0, 0, 0.0, 0, 0, 0.0, NULL } + { NULL, NULL, NULL, NULL, /* 0, 0, 0.0, */ 0, 0, 0.0, NULL } }; static void TestParseCurrency() @@ -1070,10 +1081,10 @@ static void TestParseCurrency() status = U_ZERO_ERROR; parsePos = 0; parseVal = unum_parseDouble(unum, currStr, -1, &parsePos, &status); - if (status != itemPtr->parsDoubExpectErr || parsePos != itemPtr->parsDoubExpectPos || parseVal != itemPtr->parsDoubExpectVal) { + if (status != itemPtr->parsCurrExpectErr || parsePos != itemPtr->parsCurrExpectPos || parseVal != itemPtr->parsCurrExpectVal) { log_err("UNUM_CURRENCY parseDouble %s/%s, expect %s pos %d val %.1f, get %s pos %d val %.1f\n", itemPtr->locale, itemPtr->descrip, - u_errorName(itemPtr->parsDoubExpectErr), itemPtr->parsDoubExpectPos, itemPtr->parsDoubExpectVal, + u_errorName(itemPtr->parsCurrExpectErr), itemPtr->parsCurrExpectPos, itemPtr->parsCurrExpectVal, u_errorName(status), parsePos, parseVal ); } status = U_ZERO_ERROR; @@ -1100,10 +1111,10 @@ static void TestParseCurrency() status = U_ZERO_ERROR; parsePos = 0; parseVal = unum_parseDouble(unum, itemPtr->plurStr, -1, &parsePos, &status); - if (status != itemPtr->parsDoubExpectErr || parseVal != itemPtr->parsDoubExpectVal) { + if (status != itemPtr->parsCurrExpectErr || parseVal != itemPtr->parsCurrExpectVal) { log_err("UNUM_CURRENCY parseDouble %s/%s, expect %s val %.1f, get %s val %.1f\n", itemPtr->locale, itemPtr->descrip, - u_errorName(itemPtr->parsDoubExpectErr), itemPtr->parsDoubExpectVal, + u_errorName(itemPtr->parsCurrExpectErr), itemPtr->parsCurrExpectVal, u_errorName(status), parseVal ); } status = U_ZERO_ERROR; @@ -1269,6 +1280,7 @@ static void TestNumberFormatPadding() { UChar *result=NULL; UChar temp1[512]; + UChar temp2[512]; UErrorCode status=U_ZERO_ERROR; int32_t resultlength; @@ -1294,7 +1306,8 @@ static void TestNumberFormatPadding() } /* u_uastrcpy(temp1, "*x#,###,###,##0.0#;(*x#,###,###,##0.0#)"); */ - u_uastrcpy(temp1, "*x#,###,###,##0.0#;*x(###,###,##0.0#)"); + u_uastrcpy(temp1, "*x#,###,###,##0.0#;*x(###,###,##0.0#)"); // input pattern + u_uastrcpy(temp2, "*x#########,##0.0#;(#########,##0.0#)"); // equivalent (?) output pattern status=U_ZERO_ERROR; pattern=unum_open(UNUM_IGNORE,temp1, u_strlen(temp1), "en_US",NULL, &status); if(U_FAILURE(status)) @@ -1321,12 +1334,17 @@ static void TestNumberFormatPadding() } else { - if(u_strcmp(result, temp1)!=0) - log_err("FAIL: Error in extracting the padding pattern using unum_toPattern()\n"); - else + if(u_strncmp(result, temp2, resultlengthneeded)!=0) { + log_err( + "FAIL: Error in extracting the padding pattern using unum_toPattern(): %d: %s != %s\n", + resultlengthneeded, + austrdup(temp2), + austrdup(result)); + } else { log_verbose("Pass: extracted the padding pattern correctly using unum_toPattern()\n"); -free(result); + } } + free(result); /* u_uastrcpy(temp1, "(xxxxxxx10,456.37)"); */ u_uastrcpy(temp1, "xxxxx(10,456.37)"); resultlength=0; diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp index 0e259e0d292..524a29361d2 100644 --- a/icu4c/source/test/intltest/numfmtst.cpp +++ b/icu4c/source/test/intltest/numfmtst.cpp @@ -3670,7 +3670,7 @@ NumberFormatTest::TestSpaceParsing() { {"$\\u00A0124 ", 5, -1, FALSE}, {" $ 124 ", 0, 0, FALSE}, {"124$", 0, 4, FALSE}, - {"124 $", 0, 5, FALSE}, + {"124 $", 0, 3, FALSE}, {"$124", 4, -1, TRUE}, {"$124 $124", 4, -1, TRUE}, {"$124 ", 4, -1, TRUE}, diff --git a/icu4c/source/test/testdata/numberformattestspecification.txt b/icu4c/source/test/testdata/numberformattestspecification.txt index 7b375222ca8..cd5b479d1f8 100644 --- a/icu4c/source/test/testdata/numberformattestspecification.txt +++ b/icu4c/source/test/testdata/numberformattestspecification.txt @@ -742,8 +742,8 @@ parse output breaks // JDK stops parsing at the spaces. JDK doesn't see space as a grouping separator (34 25E-1) -342.5 K (34,,25E-1) -342.5 -// J doesn't allow trailing separators before E but C does -(34,,25,E-1) -342.5 J +// J doesn't allow trailing separators before E +(34,,25,E-1) -342.5 CJ (34 25 E-1) -342.5 JK (34,,25 E-1) -342.5 JK // Spaces are not allowed after exponent symbol @@ -806,7 +806,7 @@ parse output breaks 1,234,,,+ 1234 JK 1,234- -1234 // J bails because of trailing separators -1,234,- -1234 J +1,234,- -1234 CJ // J bails here too 1234 - -1234 J diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java index 71d9675e309..3ea823c482b 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java @@ -1913,9 +1913,8 @@ public class DecimalFormat extends NumberFormat { * @stable ICU 2.0 */ public synchronized int getSecondaryGroupingSize() { - int grouping1 = properties.getGroupingSize(); int grouping2 = properties.getSecondaryGroupingSize(); - if (grouping1 == grouping2 || grouping2 < 0) { + if (grouping2 < 0) { return 0; } return grouping2; diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java index 4b63eab0982..5c679f30d83 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java @@ -5258,7 +5258,7 @@ public class NumberFormatTest extends TestFmwk { assertEquals("Secondary grouping should return 0", 0, df.getSecondaryGroupingSize()); df.setSecondaryGroupingSize(3); assertEquals("Primary grouping should still return 3", 3, df.getGroupingSize()); - assertEquals("Secondary grouping should still return 0", 0, df.getSecondaryGroupingSize()); + assertEquals("Secondary grouping should round-trip", 3, df.getSecondaryGroupingSize()); df.setGroupingSize(4); assertEquals("Primary grouping should return 4", 4, df.getGroupingSize()); assertEquals("Secondary should remember explicit setting and return 3", 3, df.getSecondaryGroupingSize());