ICU-20923 Fix compact notation with percent.
diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp index e6b1c5b..875f71b 100644 --- a/icu4c/source/i18n/number_formatimpl.cpp +++ b/icu4c/source/i18n/number_formatimpl.cpp
@@ -133,6 +133,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, bool isBaseUnit = utils::unitIsBaseUnit(macros.unit); bool isPercent = utils::unitIsPercent(macros.unit); bool isPermille = utils::unitIsPermille(macros.unit); + bool isCompactNotation = macros.notation.fType == Notation::NTN_COMPACT; bool isAccounting = macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS || macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO; @@ -144,8 +145,18 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) { unitWidth = macros.unitWidth; } - bool isCldrUnit = !isCurrency && !isBaseUnit && - (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME || !(isPercent || isPermille)); + // Use CLDR unit data for all MeasureUnits (not currency and not + // no-unit), except use the dedicated percent pattern for percent and + // permille. However, use the CLDR unit data for percent/permille if a + // long name was requested OR if compact notation is being used, since + // compact notation overrides the middle modifier (micros.modMiddle) + // normally used for the percent pattern. + bool isCldrUnit = !isCurrency + && !isBaseUnit + && (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME + || !(isPercent || isPermille) + || isCompactNotation + ); // Select the numbering system. LocalPointer<const NumberingSystem> nsLocal; @@ -232,7 +243,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, Precision precision; if (!macros.precision.isBogus()) { precision = macros.precision; - } else if (macros.notation.fType == Notation::NTN_COMPACT) { + } else if (isCompactNotation) { precision = Precision::integer().withMinDigits(2); } else if (isCurrency) { precision = Precision::currency(UCURR_USAGE_STANDARD); @@ -254,7 +265,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, // Grouping strategy if (!macros.grouper.isBogus()) { fMicros.grouping = macros.grouper; - } else if (macros.notation.fType == Notation::NTN_COMPACT) { + } else if (isCompactNotation) { // Compact notation uses minGrouping by default since ICU 59 fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2); } else { @@ -366,7 +377,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, } // Compact notation - if (macros.notation.fType == Notation::NTN_COMPACT) { + if (isCompactNotation) { CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME) ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL; auto newCompactHandler = new CompactHandler(
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp index 75b0d00..b0e1015 100644 --- a/icu4c/source/test/intltest/numbertest_api.cpp +++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -1014,6 +1014,65 @@ void NumberFormatterApiTest::unitPercent() { -98.7654321, u"-98.765432%"); + // ICU-20923 + assertFormatDescendingBig( + u"Compact Percent", + u"compact-short percent", + u"K %", + NumberFormatter::with() + .notation(Notation::compactShort()) + .unit(NoUnit::percent()), + Locale::getEnglish(), + u"88M%", + u"8.8M%", + u"876K%", + u"88K%", + u"8.8K%", + u"876%", + u"88%", + u"8.8%", + u"0%"); + + // ICU-20923 + assertFormatDescendingBig( + u"Compact Percent with Scale", + u"compact-short percent scale/100", + u"K %x100", + NumberFormatter::with() + .notation(Notation::compactShort()) + .unit(NoUnit::percent()) + .scale(Scale::powerOfTen(2)), + Locale::getEnglish(), + u"8.8B%", + u"876M%", + u"88M%", + u"8.8M%", + u"876K%", + u"88K%", + u"8.8K%", + u"876%", + u"0%"); + + // ICU-20923 + assertFormatDescendingBig( + u"Compact Percent Long Name", + u"compact-short percent unit-width-full-name", + u"K % unit-width-full-name", + NumberFormatter::with() + .notation(Notation::compactShort()) + .unit(NoUnit::percent()) + .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), + Locale::getEnglish(), + u"88M percent", + u"8.8M percent", + u"876K percent", + u"88K percent", + u"8.8K percent", + u"876 percent", + u"88 percent", + u"8.8 percent", + u"0 percent"); + assertFormatSingle( u"Per Percent", u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",
diff --git a/icu4c/source/test/intltest/numbertest_permutation.cpp b/icu4c/source/test/intltest/numbertest_permutation.cpp index 85f32d5..a96dd75 100644 --- a/icu4c/source/test/intltest/numbertest_permutation.cpp +++ b/icu4c/source/test/intltest/numbertest_permutation.cpp
@@ -71,7 +71,11 @@ static const double kNumbersToTest[]{0, 91827.3645, -0.22222}; * Test permutations of 3 orthogonal skeleton parts from the list above. * Compare the results against the golden data file: * numberpermutationtest.txt - * To regenerate that file, run intltest with the -G option. + * To regenerate that file, run intltest with the -e and -G options. + * On Linux, from icu4c/source: + * make -j8 tests && (cd test/intltest && LD_LIBRARY_PATH=../../lib:../../tools/ctestfw ./intltest -e -G format/NumberTest/NumberPermutationTest) + * After re-generating the file, copy it into icu4j: + * cp test/testdata/numberpermutationtest.txt ../../icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt */ void NumberPermutationTest::testPermutations() { IcuTestErrorCode status(*this, "testPermutations");
diff --git a/icu4c/source/test/testdata/numberpermutationtest.txt b/icu4c/source/test/testdata/numberpermutationtest.txt index e5f0b78..b63df7d 100644 --- a/icu4c/source/test/testdata/numberpermutationtest.txt +++ b/icu4c/source/test/testdata/numberpermutationtest.txt
@@ -4,15 +4,15 @@ compact-short percent unit-width-narrow es-MX 0 % - 92 k + 92 k % -0.22 % zh-TW 0% - 9.2萬 + 9.2萬% -0.22% bn-BD ০% - ৯২ হা + ৯২ হা% -০.২২% compact-short percent unit-width-full-name @@ -172,57 +172,57 @@ compact-short percent precision-integer es-MX 0 % - 92 k + 92 k % -0 % zh-TW 0% - 9萬 + 9萬% -0% bn-BD ০% - ৯২ হা + ৯২ হা% -০% compact-short percent .000 es-MX 0.000 % - 91.827 k + 91.827 k % -0.222 % zh-TW 0.000% - 9.183萬 + 9.183萬% -0.222% bn-BD ০.০০০% - ৯১.৮২৭ হা + ৯১.৮২৭ হা% -০.২২২% compact-short percent .##/@@@+ es-MX 0 % - 91.83 k + 91.83 k % -0.222 % zh-TW 0% - 9.18萬 + 9.18萬% -0.222% bn-BD ০% - ৯১.৮৩ হা + ৯১.৮৩ হা% -০.২২২% compact-short percent @@ es-MX 0.0 % - 92 k + 92 k % -0.22 % zh-TW 0.0% - 9.2萬 + 9.2萬% -0.22% bn-BD ০.০% - ৯২ হা + ৯২ হা% -০.২২% compact-short currency/EUR precision-integer @@ -508,15 +508,15 @@ compact-short percent rounding-mode-floor es-MX 0 % - 91 k + 91 k % -0.23 % zh-TW 0% - 9.1萬 + 9.1萬% -0.23% bn-BD ০% - ৯১ হা + ৯১ হা% -০.২৩% compact-short currency/EUR rounding-mode-floor @@ -592,15 +592,15 @@ compact-short percent integer-width/##00 es-MX 00 % - 92 k + 92 k % -00.22 % zh-TW 00% - 09.2萬 + 09.2萬% -00.22% bn-BD ০০% - ৯২ হা + ৯২ হা% -০০.২২% compact-short currency/EUR integer-width/##00 @@ -676,15 +676,15 @@ compact-short percent scale/0.5 es-MX 0 % - 46 k + 46 k % -0.11 % zh-TW 0% - 4.6萬 + 4.6萬% -0.11% bn-BD ০% - ৪৬ হা + ৪৬ হা% -০.১১% compact-short currency/EUR scale/0.5 @@ -760,15 +760,15 @@ compact-short percent group-on-aligned es-MX 0 % - 92 k + 92 k % -0.22 % zh-TW 0% - 9.2萬 + 9.2萬% -0.22% bn-BD ০% - ৯২ হা + ৯২ হা% -০.২২% compact-short currency/EUR group-on-aligned @@ -844,15 +844,15 @@ compact-short percent latin es-MX 0 % - 92 k + 92 k % -0.22 % zh-TW 0% - 9.2萬 + 9.2萬% -0.22% bn-BD 0% - 92 হা + 92 হা% -0.22% compact-short currency/EUR latin @@ -928,15 +928,15 @@ compact-short percent sign-accounting-except-zero es-MX 0 % - +92 k + +92 k % -0.22 % zh-TW 0% - +9.2萬 + +9.2萬% -0.22% bn-BD ০% - +৯২ হা + +৯২ হা% -০.২২% compact-short currency/EUR sign-accounting-except-zero @@ -1012,15 +1012,15 @@ compact-short percent decimal-always es-MX 0. % - 92. k + 92. k % -0.22 % zh-TW 0.% - 9.2萬 + 9.2萬% -0.22% bn-BD ০.% - ৯২. হা + ৯২. হা% -০.২২% compact-short currency/EUR decimal-always
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java index 629732e..fa483ef 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberFormatterImpl.java
@@ -182,6 +182,7 @@ private static MicroPropsGenerator macrosToMicroGenerator(MacroProps macros, Mic boolean isBaseUnit = unitIsBaseUnit(macros.unit); boolean isPercent = unitIsPercent(macros.unit); boolean isPermille = unitIsPermille(macros.unit); + boolean isCompactNotation = (macros.notation instanceof CompactNotation); boolean isAccounting = macros.sign == SignDisplay.ACCOUNTING || macros.sign == SignDisplay.ACCOUNTING_ALWAYS || macros.sign == SignDisplay.ACCOUNTING_EXCEPT_ZERO; @@ -190,8 +191,18 @@ private static MicroPropsGenerator macrosToMicroGenerator(MacroProps macros, Mic if (macros.unitWidth != null) { unitWidth = macros.unitWidth; } - boolean isCldrUnit = !isCurrency && !isBaseUnit && - (unitWidth == UnitWidth.FULL_NAME || !(isPercent || isPermille)); + // Use CLDR unit data for all MeasureUnits (not currency and not + // no-unit), except use the dedicated percent pattern for percent and + // permille. However, use the CLDR unit data for percent/permille if a + // long name was requested OR if compact notation is being used, since + // compact notation overrides the middle modifier (micros.modMiddle) + // normally used for the percent pattern. + boolean isCldrUnit = !isCurrency + && !isBaseUnit + && (unitWidth == UnitWidth.FULL_NAME + || !(isPercent || isPermille) + || isCompactNotation + ); PluralRules rules = macros.rules; // Select the numbering system. @@ -252,7 +263,7 @@ private static MicroPropsGenerator macrosToMicroGenerator(MacroProps macros, Mic // Rounding strategy if (macros.precision != null) { micros.rounder = macros.precision; - } else if (macros.notation instanceof CompactNotation) { + } else if (isCompactNotation) { micros.rounder = Precision.COMPACT_STRATEGY; } else if (isCurrency) { micros.rounder = Precision.MONETARY_STANDARD; @@ -270,7 +281,7 @@ private static MicroPropsGenerator macrosToMicroGenerator(MacroProps macros, Mic micros.grouping = (Grouper) macros.grouping; } else if (macros.grouping instanceof GroupingStrategy) { micros.grouping = Grouper.forStrategy((GroupingStrategy) macros.grouping); - } else if (macros.notation instanceof CompactNotation) { + } else if (isCompactNotation) { // Compact notation uses minGrouping by default since ICU 59 micros.grouping = Grouper.forStrategy(GroupingStrategy.MIN2); } else { @@ -356,7 +367,7 @@ private static MicroPropsGenerator macrosToMicroGenerator(MacroProps macros, Mic } // Compact notation - if (macros.notation instanceof CompactNotation) { + if (isCompactNotation) { if (rules == null) { // Lazily create PluralRules rules = PluralRules.forLocale(macros.loc);
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt b/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt index e5f0b78..b63df7d 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt
@@ -4,15 +4,15 @@ compact-short percent unit-width-narrow es-MX 0 % - 92 k + 92 k % -0.22 % zh-TW 0% - 9.2萬 + 9.2萬% -0.22% bn-BD ০% - ৯২ হা + ৯২ হা% -০.২২% compact-short percent unit-width-full-name @@ -172,57 +172,57 @@ compact-short percent precision-integer es-MX 0 % - 92 k + 92 k % -0 % zh-TW 0% - 9萬 + 9萬% -0% bn-BD ০% - ৯২ হা + ৯২ হা% -০% compact-short percent .000 es-MX 0.000 % - 91.827 k + 91.827 k % -0.222 % zh-TW 0.000% - 9.183萬 + 9.183萬% -0.222% bn-BD ০.০০০% - ৯১.৮২৭ হা + ৯১.৮২৭ হা% -০.২২২% compact-short percent .##/@@@+ es-MX 0 % - 91.83 k + 91.83 k % -0.222 % zh-TW 0% - 9.18萬 + 9.18萬% -0.222% bn-BD ০% - ৯১.৮৩ হা + ৯১.৮৩ হা% -০.২২২% compact-short percent @@ es-MX 0.0 % - 92 k + 92 k % -0.22 % zh-TW 0.0% - 9.2萬 + 9.2萬% -0.22% bn-BD ০.০% - ৯২ হা + ৯২ হা% -০.২২% compact-short currency/EUR precision-integer @@ -508,15 +508,15 @@ compact-short percent rounding-mode-floor es-MX 0 % - 91 k + 91 k % -0.23 % zh-TW 0% - 9.1萬 + 9.1萬% -0.23% bn-BD ০% - ৯১ হা + ৯১ হা% -০.২৩% compact-short currency/EUR rounding-mode-floor @@ -592,15 +592,15 @@ compact-short percent integer-width/##00 es-MX 00 % - 92 k + 92 k % -00.22 % zh-TW 00% - 09.2萬 + 09.2萬% -00.22% bn-BD ০০% - ৯২ হা + ৯২ হা% -০০.২২% compact-short currency/EUR integer-width/##00 @@ -676,15 +676,15 @@ compact-short percent scale/0.5 es-MX 0 % - 46 k + 46 k % -0.11 % zh-TW 0% - 4.6萬 + 4.6萬% -0.11% bn-BD ০% - ৪৬ হা + ৪৬ হা% -০.১১% compact-short currency/EUR scale/0.5 @@ -760,15 +760,15 @@ compact-short percent group-on-aligned es-MX 0 % - 92 k + 92 k % -0.22 % zh-TW 0% - 9.2萬 + 9.2萬% -0.22% bn-BD ০% - ৯২ হা + ৯২ হা% -০.২২% compact-short currency/EUR group-on-aligned @@ -844,15 +844,15 @@ compact-short percent latin es-MX 0 % - 92 k + 92 k % -0.22 % zh-TW 0% - 9.2萬 + 9.2萬% -0.22% bn-BD 0% - 92 হা + 92 হা% -0.22% compact-short currency/EUR latin @@ -928,15 +928,15 @@ compact-short percent sign-accounting-except-zero es-MX 0 % - +92 k + +92 k % -0.22 % zh-TW 0% - +9.2萬 + +9.2萬% -0.22% bn-BD ০% - +৯২ হা + +৯২ হা% -০.২২% compact-short currency/EUR sign-accounting-except-zero @@ -1012,15 +1012,15 @@ compact-short percent decimal-always es-MX 0. % - 92. k + 92. k % -0.22 % zh-TW 0.% - 9.2萬 + 9.2萬% -0.22% bn-BD ০.% - ৯২. হা + ৯২. হা% -০.২২% compact-short currency/EUR decimal-always
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java index d3bbc13..73df02c 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
@@ -967,6 +967,65 @@ public void unitPercent() { -98.7654321, "-98.765432%"); + // ICU-20923 + assertFormatDescendingBig( + "Compact Percent", + "compact-short percent", + "K %", + NumberFormatter.with() + .notation(Notation.compactShort()) + .unit(NoUnit.PERCENT), + ULocale.ENGLISH, + "88M%", + "8.8M%", + "876K%", + "88K%", + "8.8K%", + "876%", + "88%", + "8.8%", + "0%"); + + // ICU-20923 + assertFormatDescendingBig( + "Compact Percent with Scale", + "compact-short percent scale/100", + "K %x100", + NumberFormatter.with() + .notation(Notation.compactShort()) + .unit(NoUnit.PERCENT) + .scale(Scale.powerOfTen(2)), + ULocale.ENGLISH, + "8.8B%", + "876M%", + "88M%", + "8.8M%", + "876K%", + "88K%", + "8.8K%", + "876%", + "0%"); + + // ICU-20923 + assertFormatDescendingBig( + "Compact Percent Long Name", + "compact-short percent unit-width-full-name", + "K % unit-width-full-name", + NumberFormatter.with() + .notation(Notation.compactShort()) + .unit(NoUnit.PERCENT) + .unitWidth(UnitWidth.FULL_NAME), + ULocale.ENGLISH, + "88M percent", + "8.8M percent", + "876K percent", + "88K percent", + "8.8K percent", + "876 percent", + "88 percent", + "8.8 percent", + "0 percent"); + assertFormatSingle( "Per Percent", "measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",