1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
12 #include "unicode/unum.h"
13 #include "unicode/numberformatter.h"
14 #include "number_asformat.h"
15 #include "number_types.h"
16 #include "number_utils.h"
17 #include "numbertest.h"
18 #include "unicode/utypes.h"
20 // Horrible workaround for the lack of a status code in the constructor...
21 // (Also affects numbertest_range.cpp)
22 UErrorCode globalNumberFormatterApiTestStatus
= U_ZERO_ERROR
;
24 NumberFormatterApiTest::NumberFormatterApiTest()
25 : NumberFormatterApiTest(globalNumberFormatterApiTestStatus
) {
28 NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode
& status
)
29 : USD(u
"USD", status
),
36 FRENCH_SYMBOLS(Locale::getFrench(), status
),
37 SWISS_SYMBOLS(Locale("de-CH"), status
),
38 MYANMAR_SYMBOLS(Locale("my"), status
) {
40 // Check for error on the first MeasureUnit in case there is no data
41 LocalPointer
<MeasureUnit
> unit(MeasureUnit::createMeter(status
));
42 if (U_FAILURE(status
)) {
43 dataerrln("%s %d status = %s", __FILE__
, __LINE__
, u_errorName(status
));
48 DAY
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createDay(status
));
49 SQUARE_METER
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createSquareMeter(status
));
50 FAHRENHEIT
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createFahrenheit(status
));
51 SECOND
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createSecond(status
));
52 POUND
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createPound(status
));
53 SQUARE_MILE
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createSquareMile(status
));
54 JOULE
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createJoule(status
));
55 FURLONG
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createFurlong(status
));
56 KELVIN
= *LocalPointer
<MeasureUnit
>(MeasureUnit::createKelvin(status
));
58 MATHSANB
= *LocalPointer
<NumberingSystem
>(NumberingSystem::createInstanceByName("mathsanb", status
));
59 LATN
= *LocalPointer
<NumberingSystem
>(NumberingSystem::createInstanceByName("latn", status
));
62 void NumberFormatterApiTest::runIndexedTest(int32_t index
, UBool exec
, const char*& name
, char*) {
64 logln("TestSuite NumberFormatterApiTest: ");
67 TESTCASE_AUTO(notationSimple
);
68 TESTCASE_AUTO(notationScientific
);
69 TESTCASE_AUTO(notationCompact
);
70 TESTCASE_AUTO(unitMeasure
);
71 TESTCASE_AUTO(unitCompoundMeasure
);
72 TESTCASE_AUTO(unitCurrency
);
73 TESTCASE_AUTO(unitPercent
);
75 // Slow test: run in exhaustive mode only
76 TESTCASE_AUTO(percentParity
);
78 TESTCASE_AUTO(roundingFraction
);
79 TESTCASE_AUTO(roundingFigures
);
80 TESTCASE_AUTO(roundingFractionFigures
);
81 TESTCASE_AUTO(roundingOther
);
82 TESTCASE_AUTO(grouping
);
83 TESTCASE_AUTO(padding
);
84 TESTCASE_AUTO(integerWidth
);
85 TESTCASE_AUTO(symbols
);
86 // TODO: Add this method if currency symbols override support is added.
87 //TESTCASE_AUTO(symbolsOverride);
89 TESTCASE_AUTO(signCoverage
);
90 TESTCASE_AUTO(decimal
);
92 TESTCASE_AUTO(locale
);
93 TESTCASE_AUTO(skeletonUserGuideExamples
);
94 TESTCASE_AUTO(formatTypes
);
95 TESTCASE_AUTO(fieldPositionLogic
);
96 TESTCASE_AUTO(fieldPositionCoverage
);
97 TESTCASE_AUTO(toFormat
);
98 TESTCASE_AUTO(errors
);
100 // Slow test: run in exhaustive mode only
101 // (somewhat slow to check all permutations of settings)
102 TESTCASE_AUTO(validRanges
);
104 TESTCASE_AUTO(copyMove
);
105 TESTCASE_AUTO(localPointerCAPI
);
106 TESTCASE_AUTO(toObject
);
107 TESTCASE_AUTO(toDecimalNumber
);
111 void NumberFormatterApiTest::notationSimple() {
112 assertFormatDescending(
115 NumberFormatter::with(),
116 Locale::getEnglish(),
127 assertFormatDescendingBig(
130 NumberFormatter::with().notation(Notation::simple()),
131 Locale::getEnglish(),
143 u
"Basic with Negative Sign",
145 NumberFormatter::with(),
146 Locale::getEnglish(),
152 void NumberFormatterApiTest::notationScientific() {
153 assertFormatDescending(
156 NumberFormatter::with().notation(Notation::scientific()),
157 Locale::getEnglish(),
168 assertFormatDescending(
171 NumberFormatter::with().notation(Notation::engineering()),
172 Locale::getEnglish(),
183 assertFormatDescending(
184 u
"Scientific sign always shown",
185 u
"scientific/sign-always",
186 NumberFormatter::with().notation(
187 Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS
)),
188 Locale::getEnglish(),
199 assertFormatDescending(
200 u
"Scientific min exponent digits",
202 NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)),
203 Locale::getEnglish(),
215 u
"Scientific Negative",
217 NumberFormatter::with().notation(Notation::scientific()),
218 Locale::getEnglish(),
223 u
"Scientific Infinity",
225 NumberFormatter::with().notation(Notation::scientific()),
226 Locale::getEnglish(),
233 NumberFormatter::with().notation(Notation::scientific()),
234 Locale::getEnglish(),
239 void NumberFormatterApiTest::notationCompact() {
240 assertFormatDescending(
243 NumberFormatter::with().notation(Notation::compactShort()),
244 Locale::getEnglish(),
255 assertFormatDescending(
258 NumberFormatter::with().notation(Notation::compactLong()),
259 Locale::getEnglish(),
270 assertFormatDescending(
271 u
"Compact Short Currency",
272 u
"compact-short currency/USD",
273 NumberFormatter::with().notation(Notation::compactShort()).unit(USD
),
274 Locale::getEnglish(),
285 assertFormatDescending(
286 u
"Compact Short with ISO Currency",
287 u
"compact-short currency/USD unit-width-iso-code",
288 NumberFormatter::with().notation(Notation::compactShort())
290 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
291 Locale::getEnglish(),
302 assertFormatDescending(
303 u
"Compact Short with Long Name Currency",
304 u
"compact-short currency/USD unit-width-full-name",
305 NumberFormatter::with().notation(Notation::compactShort())
307 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
308 Locale::getEnglish(),
316 u
"0.0088 US dollars",
319 // Note: Most locales don't have compact long currency, so this currently falls back to short.
320 // This test case should be fixed when proper compact long currency patterns are added.
321 assertFormatDescending(
322 u
"Compact Long Currency",
323 u
"compact-long currency/USD",
324 NumberFormatter::with().notation(Notation::compactLong()).unit(USD
),
325 Locale::getEnglish(),
326 u
"$88K", // should be something like "$88 thousand"
336 // Note: Most locales don't have compact long currency, so this currently falls back to short.
337 // This test case should be fixed when proper compact long currency patterns are added.
338 assertFormatDescending(
339 u
"Compact Long with ISO Currency",
340 u
"compact-long currency/USD unit-width-iso-code",
341 NumberFormatter::with().notation(Notation::compactLong())
343 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
344 Locale::getEnglish(),
345 u
"USD 88K", // should be something like "USD 88 thousand"
355 // TODO: This behavior could be improved and should be revisited.
356 assertFormatDescending(
357 u
"Compact Long with Long Name Currency",
358 u
"compact-long currency/USD unit-width-full-name",
359 NumberFormatter::with().notation(Notation::compactLong())
361 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
362 Locale::getEnglish(),
363 u
"88 thousand US dollars",
364 u
"8.8 thousand US dollars",
370 u
"0.0088 US dollars",
374 u
"Compact Plural One",
376 NumberFormatter::with().notation(Notation::compactLong()),
377 Locale::createFromName("es"),
382 u
"Compact Plural Other",
384 NumberFormatter::with().notation(Notation::compactLong()),
385 Locale::createFromName("es"),
390 u
"Compact with Negative Sign",
392 NumberFormatter::with().notation(Notation::compactShort()),
393 Locale::getEnglish(),
400 NumberFormatter::with().notation(Notation::compactShort()),
401 Locale::getEnglish(),
408 NumberFormatter::with().notation(Notation::compactShort()),
409 Locale::getEnglish(),
416 NumberFormatter::with().notation(Notation::compactShort()),
417 Locale::getEnglish(),
424 NumberFormatter::with().notation(Notation::compactShort()),
425 Locale::getEnglish(),
432 NumberFormatter::with().notation(Notation::compactShort()),
433 Locale::getEnglish(),
438 u
"Compact in zh-Hant-HK",
440 NumberFormatter::with().notation(Notation::compactShort()),
441 Locale("zh-Hant-HK"),
446 u
"Compact in zh-Hant",
448 NumberFormatter::with().notation(Notation::compactShort()),
456 NumberFormatter::with().notation(Notation::compactShort()),
457 Locale::getEnglish(),
464 NumberFormatter::with().notation(Notation::compactShort()),
465 Locale::getEnglish(),
469 // NOTE: There is no API for compact custom data in C++
470 // and thus no "Compact Somali No Figure" test
473 void NumberFormatterApiTest::unitMeasure() {
474 assertFormatDescending(
475 u
"Meters Short and unit() method",
476 u
"measure-unit/length-meter",
477 NumberFormatter::with().unit(MeasureUnit::getMeter()),
478 Locale::getEnglish(),
489 assertFormatDescending(
490 u
"Meters Long and adoptUnit() method",
491 u
"measure-unit/length-meter unit-width-full-name",
492 NumberFormatter::with().adoptUnit(new MeasureUnit(METER
))
493 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
494 Locale::getEnglish(),
505 assertFormatDescending(
506 u
"Compact Meters Long",
507 u
"compact-long measure-unit/length-meter unit-width-full-name",
508 NumberFormatter::with().notation(Notation::compactLong())
510 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
511 Locale::getEnglish(),
512 u
"88 thousand meters",
513 u
"8.8 thousand meters",
522 // TODO: Implement Measure in C++
523 // assertFormatSingleMeasure(
524 // u"Meters with Measure Input",
525 // NumberFormatter::with().unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
526 // Locale::getEnglish(),
527 // new Measure(5.43, new MeasureUnit(METER)),
530 // TODO: Implement Measure in C++
531 // assertFormatSingleMeasure(
532 // u"Measure format method takes precedence over fluent chain",
533 // NumberFormatter::with().unit(METER),
534 // Locale::getEnglish(),
535 // new Measure(5.43, USD),
539 u
"Meters with Negative Sign",
540 u
"measure-unit/length-meter",
541 NumberFormatter::with().unit(METER
),
542 Locale::getEnglish(),
546 // The locale string "सान" appears only in brx.txt:
548 u
"Interesting Data Fallback 1",
549 u
"measure-unit/duration-day unit-width-full-name",
550 NumberFormatter::with().unit(DAY
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
551 Locale::createFromName("brx"),
555 // Requires following the alias from unitsNarrow to unitsShort:
557 u
"Interesting Data Fallback 2",
558 u
"measure-unit/duration-day unit-width-narrow",
559 NumberFormatter::with().unit(DAY
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW
),
560 Locale::createFromName("brx"),
564 // en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
565 // requiring fallback to the root.
567 u
"Interesting Data Fallback 3",
568 u
"measure-unit/area-square-meter unit-width-narrow",
569 NumberFormatter::with().unit(SQUARE_METER
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW
),
570 Locale::createFromName("en-GB"),
574 // Try accessing a narrow unit directly from root.
576 u
"Interesting Data Fallback 4",
577 u
"measure-unit/area-square-meter unit-width-narrow",
578 NumberFormatter::with().unit(SQUARE_METER
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW
),
579 Locale::createFromName("root"),
583 // es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
584 // NOTE: This example is in the documentation.
586 u
"Difference between Narrow and Short (Narrow Version)",
587 u
"measure-unit/temperature-fahrenheit unit-width-narrow",
588 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_NARROW
),
594 u
"Difference between Narrow and Short (Short Version)",
595 u
"measure-unit/temperature-fahrenheit unit-width-short",
596 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
602 u
"MeasureUnit form without {0} in CLDR pattern",
603 u
"measure-unit/temperature-kelvin unit-width-full-name",
604 NumberFormatter::with().unit(KELVIN
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
610 u
"MeasureUnit form without {0} in CLDR pattern and wide base form",
611 u
"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
612 NumberFormatter::with().precision(Precision::fixedFraction(20))
614 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
620 u
"Person unit not in short form",
621 u
"measure-unit/duration-year-person unit-width-full-name",
622 NumberFormatter::with().unit(MeasureUnit::getYearPerson())
623 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
629 void NumberFormatterApiTest::unitCompoundMeasure() {
630 assertFormatDescending(
631 u
"Meters Per Second Short (unit that simplifies) and perUnit method",
632 u
"measure-unit/length-meter per-measure-unit/duration-second",
633 NumberFormatter::with().unit(METER
).perUnit(SECOND
),
634 Locale::getEnglish(),
645 assertFormatDescending(
646 u
"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method",
647 u
"measure-unit/mass-pound per-measure-unit/area-square-mile",
648 NumberFormatter::with().unit(POUND
).adoptPerUnit(new MeasureUnit(SQUARE_MILE
)),
649 Locale::getEnglish(),
660 assertFormatDescending(
661 u
"Joules Per Furlong Short (unit with no simplifications or special patterns)",
662 u
"measure-unit/energy-joule per-measure-unit/length-furlong",
663 NumberFormatter::with().unit(JOULE
).perUnit(FURLONG
),
664 Locale::getEnglish(),
676 void NumberFormatterApiTest::unitCurrency() {
677 assertFormatDescending(
680 NumberFormatter::with().unit(GBP
),
681 Locale::getEnglish(),
692 assertFormatDescending(
694 u
"currency/GBP unit-width-iso-code",
695 NumberFormatter::with().unit(GBP
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
696 Locale::getEnglish(),
707 assertFormatDescending(
708 u
"Currency Long Name",
709 u
"currency/GBP unit-width-full-name",
710 NumberFormatter::with().unit(GBP
).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
711 Locale::getEnglish(),
712 u
"87,650.00 British pounds",
713 u
"8,765.00 British pounds",
714 u
"876.50 British pounds",
715 u
"87.65 British pounds",
716 u
"8.76 British pounds",
717 u
"0.88 British pounds",
718 u
"0.09 British pounds",
719 u
"0.01 British pounds",
720 u
"0.00 British pounds");
722 assertFormatDescending(
724 u
"currency/GBP unit-width-hidden",
725 NumberFormatter::with().unit(GBP
).unitWidth(UNUM_UNIT_WIDTH_HIDDEN
),
726 Locale::getEnglish(),
737 // TODO: Implement Measure in C++
738 // assertFormatSingleMeasure(
739 // u"Currency with CurrencyAmount Input",
740 // NumberFormatter::with(),
741 // Locale::getEnglish(),
742 // new CurrencyAmount(5.43, GBP),
745 // TODO: Enable this test when DecimalFormat wrapper is done.
746 // assertFormatSingle(
747 // u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat(
748 // PatternStringParser.parseToProperties("0 ¤¤¤"),
749 // DecimalFormatSymbols.getInstance(Locale::getEnglish()),
750 // null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds");
753 u
"Currency with Negative Sign",
755 NumberFormatter::with().unit(GBP
),
756 Locale::getEnglish(),
760 // The full currency symbol is not shown in NARROW format.
761 // NOTE: This example is in the documentation.
763 u
"Currency Difference between Narrow and Short (Narrow Version)",
764 u
"currency/USD unit-width-narrow",
765 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_NARROW
),
771 u
"Currency Difference between Narrow and Short (Short Version)",
772 u
"currency/USD unit-width-short",
773 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
779 u
"Currency-dependent format (Control)",
780 u
"currency/USD unit-width-short",
781 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
787 u
"Currency-dependent format (Test)",
788 u
"currency/ESP unit-width-short",
789 NumberFormatter::with().unit(ESP
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
795 u
"Currency-dependent symbols (Control)",
796 u
"currency/USD unit-width-short",
797 NumberFormatter::with().unit(USD
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
802 // NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero-
803 // width space), and they set the decimal separator to the $ symbol.
805 u
"Currency-dependent symbols (Test Short)",
806 u
"currency/PTE unit-width-short",
807 NumberFormatter::with().unit(PTE
).unitWidth(UNUM_UNIT_WIDTH_SHORT
),
810 u
"444,444$55 \u200B");
813 u
"Currency-dependent symbols (Test Narrow)",
814 u
"currency/PTE unit-width-narrow",
815 NumberFormatter::with().unit(PTE
).unitWidth(UNUM_UNIT_WIDTH_NARROW
),
818 u
"444,444$55 \u200B");
821 u
"Currency-dependent symbols (Test ISO Code)",
822 u
"currency/PTE unit-width-iso-code",
823 NumberFormatter::with().unit(PTE
).unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
),
829 u
"Plural form depending on visible digits (ICU-20499)",
830 u
"currency/RON unit-width-full-name",
831 NumberFormatter::with().unit(RON
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
834 u
"24,00 lei românești");
837 void NumberFormatterApiTest::unitPercent() {
838 assertFormatDescending(
841 NumberFormatter::with().unit(NoUnit::percent()),
842 Locale::getEnglish(),
853 assertFormatDescending(
856 NumberFormatter::with().unit(NoUnit::permille()),
857 Locale::getEnglish(),
871 NumberFormatter::with().unit(NoUnit::base()),
872 Locale::getEnglish(),
877 u
"Percent with Negative Sign",
879 NumberFormatter::with().unit(NoUnit::percent()),
880 Locale::getEnglish(),
885 void NumberFormatterApiTest::percentParity() {
886 IcuTestErrorCode
status(*this, "percentParity");
887 UnlocalizedNumberFormatter uNoUnitPercent
= NumberFormatter::with().unit(NoUnit::percent());
888 UnlocalizedNumberFormatter uNoUnitPermille
= NumberFormatter::with().unit(NoUnit::permille());
889 UnlocalizedNumberFormatter uMeasurePercent
= NumberFormatter::with().unit(MeasureUnit::getPercent());
890 UnlocalizedNumberFormatter uMeasurePermille
= NumberFormatter::with().unit(MeasureUnit::getPermille());
893 auto locales
= Locale::getAvailableLocales(localeCount
);
894 for (int32_t i
=0; i
<localeCount
; i
++) {
895 auto& locale
= locales
[i
];
896 UnicodeString sNoUnitPercent
= uNoUnitPercent
.locale(locale
)
897 .formatDouble(50, status
).toString(status
);
898 UnicodeString sNoUnitPermille
= uNoUnitPermille
.locale(locale
)
899 .formatDouble(50, status
).toString(status
);
900 UnicodeString sMeasurePercent
= uMeasurePercent
.locale(locale
)
901 .formatDouble(50, status
).toString(status
);
902 UnicodeString sMeasurePermille
= uMeasurePermille
.locale(locale
)
903 .formatDouble(50, status
).toString(status
);
905 assertEquals(u
"Percent, locale " + UnicodeString(locale
.getName()),
906 sNoUnitPercent
, sMeasurePercent
);
907 assertEquals(u
"Permille, locale " + UnicodeString(locale
.getName()),
908 sNoUnitPermille
, sMeasurePermille
);
912 void NumberFormatterApiTest::roundingFraction() {
913 assertFormatDescending(
915 u
"precision-integer",
916 NumberFormatter::with().precision(Precision::integer()),
917 Locale::getEnglish(),
928 assertFormatDescending(
931 NumberFormatter::with().precision(Precision::fixedFraction(3)),
932 Locale::getEnglish(),
943 assertFormatDescending(
946 NumberFormatter::with().precision(Precision::minFraction(1)),
947 Locale::getEnglish(),
958 assertFormatDescending(
961 NumberFormatter::with().precision(Precision::maxFraction(1)),
962 Locale::getEnglish(),
973 assertFormatDescending(
976 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)),
977 Locale::getEnglish(),
989 void NumberFormatterApiTest::roundingFigures() {
991 u
"Fixed Significant",
993 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
994 Locale::getEnglish(),
999 u
"Fixed Significant Rounding",
1001 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
1002 Locale::getEnglish(),
1007 u
"Fixed Significant Zero",
1009 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
1010 Locale::getEnglish(),
1017 NumberFormatter::with().precision(Precision::minSignificantDigits(2)),
1018 Locale::getEnglish(),
1025 NumberFormatter::with().precision(Precision::maxSignificantDigits(4)),
1026 Locale::getEnglish(),
1031 u
"Min/Max Significant",
1033 NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)),
1034 Locale::getEnglish(),
1039 u
"Fixed Significant on zero with lots of integer width",
1040 u
"@ integer-width/+000",
1041 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
1042 .integerWidth(IntegerWidth::zeroFillTo(3)),
1043 Locale::getEnglish(),
1048 u
"Fixed Significant on zero with zero integer width",
1049 u
"@ integer-width/+",
1050 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
1051 .integerWidth(IntegerWidth::zeroFillTo(0)),
1052 Locale::getEnglish(),
1057 void NumberFormatterApiTest::roundingFractionFigures() {
1058 assertFormatDescending(
1059 u
"Basic Significant", // for comparison
1061 NumberFormatter::with().precision(Precision::maxSignificantDigits(2)),
1062 Locale::getEnglish(),
1073 assertFormatDescending(
1074 u
"FracSig minMaxFrac minSig",
1076 NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)),
1077 Locale::getEnglish(),
1083 u
"0.876", // minSig beats maxFrac
1084 u
"0.0876", // minSig beats maxFrac
1085 u
"0.00876", // minSig beats maxFrac
1088 assertFormatDescending(
1089 u
"FracSig minMaxFrac maxSig A",
1091 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)),
1092 Locale::getEnglish(),
1093 u
"88,000.0", // maxSig beats maxFrac
1094 u
"8,800.0", // maxSig beats maxFrac
1095 u
"880.0", // maxSig beats maxFrac
1096 u
"88.0", // maxSig beats maxFrac
1097 u
"8.8", // maxSig beats maxFrac
1098 u
"0.88", // maxSig beats maxFrac
1103 assertFormatDescending(
1104 u
"FracSig minMaxFrac maxSig B",
1106 NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)),
1107 Locale::getEnglish(),
1108 u
"88,000.00", // maxSig beats maxFrac
1109 u
"8,800.00", // maxSig beats maxFrac
1110 u
"880.00", // maxSig beats maxFrac
1111 u
"88.00", // maxSig beats maxFrac
1112 u
"8.80", // maxSig beats maxFrac
1119 u
"FracSig with trailing zeros A",
1121 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
1122 Locale::getEnglish(),
1127 u
"FracSig with trailing zeros B",
1129 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
1130 Locale::getEnglish(),
1135 void NumberFormatterApiTest::roundingOther() {
1136 assertFormatDescending(
1138 u
"precision-unlimited",
1139 NumberFormatter::with().precision(Precision::unlimited()),
1140 Locale::getEnglish(),
1151 assertFormatDescending(
1153 u
"precision-increment/0.5",
1154 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)),
1155 Locale::getEnglish(),
1166 assertFormatDescending(
1167 u
"Increment with Min Fraction",
1168 u
"precision-increment/0.50",
1169 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)),
1170 Locale::getEnglish(),
1181 assertFormatDescending(
1182 u
"Strange Increment",
1183 u
"precision-increment/3.140",
1184 NumberFormatter::with().precision(Precision::increment(3.14).withMinFraction(3)),
1185 Locale::getEnglish(),
1196 assertFormatDescending(
1197 u
"Increment Resolving to Power of 10",
1198 u
"precision-increment/0.010",
1199 NumberFormatter::with().precision(Precision::increment(0.01).withMinFraction(3)),
1200 Locale::getEnglish(),
1211 assertFormatDescending(
1212 u
"Currency Standard",
1213 u
"currency/CZK precision-currency-standard",
1214 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD
))
1216 Locale::getEnglish(),
1227 assertFormatDescending(
1229 u
"currency/CZK precision-currency-cash",
1230 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH
))
1232 Locale::getEnglish(),
1243 assertFormatDescending(
1244 u
"Currency Cash with Nickel Rounding",
1245 u
"currency/CAD precision-currency-cash",
1246 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH
))
1248 Locale::getEnglish(),
1259 assertFormatDescending(
1260 u
"Currency not in top-level fluent chain",
1261 u
"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
1262 NumberFormatter::with().precision(
1263 Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH
).withCurrency(CZK
)),
1264 Locale::getEnglish(),
1275 // NOTE: Other tests cover the behavior of the other rounding modes.
1276 assertFormatDescending(
1277 u
"Rounding Mode CEILING",
1278 u
"precision-integer rounding-mode-ceiling",
1279 NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING
),
1280 Locale::getEnglish(),
1292 void NumberFormatterApiTest::grouping() {
1293 assertFormatDescendingBig(
1294 u
"Western Grouping",
1296 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1297 Locale::getEnglish(),
1308 assertFormatDescendingBig(
1311 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1323 assertFormatDescendingBig(
1324 u
"Western Grouping, Min 2",
1326 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1327 Locale::getEnglish(),
1338 assertFormatDescendingBig(
1339 u
"Indic Grouping, Min 2",
1341 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1353 assertFormatDescendingBig(
1356 NumberFormatter::with().grouping(UNUM_GROUPING_OFF
),
1368 assertFormatDescendingBig(
1369 u
"Indic locale with THOUSANDS grouping",
1371 NumberFormatter::with().grouping(UNUM_GROUPING_THOUSANDS
),
1383 // NOTE: Polish is interesting because it has minimumGroupingDigits=2 in locale data
1384 // (Most locales have either 1 or 2)
1385 // If this test breaks due to data changes, find another locale that has minimumGroupingDigits.
1386 assertFormatDescendingBig(
1389 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
),
1401 assertFormatDescendingBig(
1402 u
"Polish Grouping, Min 2",
1404 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2
),
1416 assertFormatDescendingBig(
1417 u
"Polish Grouping, Always",
1418 u
"group-on-aligned",
1419 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED
),
1431 // NOTE: Bulgarian is interesting because it has no grouping in the default currency format.
1432 // If this test breaks due to data changes, find another locale that has no default grouping.
1433 assertFormatDescendingBig(
1434 u
"Bulgarian Currency Grouping",
1435 u
"currency/USD group-auto",
1436 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO
).unit(USD
),
1438 u
"87650000,00 щ.д.",
1448 assertFormatDescendingBig(
1449 u
"Bulgarian Currency Grouping, Always",
1450 u
"currency/USD group-on-aligned",
1451 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED
).unit(USD
),
1453 u
"87 650 000,00 щ.д.",
1454 u
"8 765 000,00 щ.д.",
1464 macros
.grouper
= Grouper(4, 1, 3, UNUM_GROUPING_COUNT
);
1465 assertFormatDescendingBig(
1466 u
"Custom Grouping via Internal API",
1468 NumberFormatter::with().macros(macros
),
1469 Locale::getEnglish(),
1481 void NumberFormatterApiTest::padding() {
1482 assertFormatDescending(
1485 NumberFormatter::with().padding(Padder::none()),
1486 Locale::getEnglish(),
1497 assertFormatDescending(
1500 NumberFormatter::with().padding(
1502 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1503 Locale::getEnglish(),
1514 assertFormatDescending(
1515 u
"Padding with code points",
1517 NumberFormatter::with().padding(
1519 0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1520 Locale::getEnglish(),
1531 assertFormatDescending(
1532 u
"Padding with wide digits",
1534 NumberFormatter::with().padding(
1536 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1537 .adoptSymbols(new NumberingSystem(MATHSANB
)),
1538 Locale::getEnglish(),
1549 assertFormatDescending(
1550 u
"Padding with currency spacing",
1552 NumberFormatter::with().padding(
1554 '*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1556 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
1557 Locale::getEnglish(),
1569 u
"Pad Before Prefix",
1571 NumberFormatter::with().padding(
1573 '*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX
)),
1574 Locale::getEnglish(),
1579 u
"Pad After Prefix",
1581 NumberFormatter::with().padding(
1583 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX
)),
1584 Locale::getEnglish(),
1589 u
"Pad Before Suffix",
1591 NumberFormatter::with().padding(
1593 '*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX
)).unit(NoUnit::percent()),
1594 Locale::getEnglish(),
1599 u
"Pad After Suffix",
1601 NumberFormatter::with().padding(
1603 '*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX
)).unit(NoUnit::percent()),
1604 Locale::getEnglish(),
1609 u
"Currency Spacing with Zero Digit Padding Broken",
1611 NumberFormatter::with().padding(
1613 '0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX
))
1615 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE
),
1616 Locale::getEnglish(),
1618 u
"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
1621 void NumberFormatterApiTest::integerWidth() {
1622 assertFormatDescending(
1623 u
"Integer Width Default",
1624 u
"integer-width/+0",
1625 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
1626 Locale::getEnglish(),
1637 assertFormatDescending(
1638 u
"Integer Width Zero Fill 0",
1640 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
1641 Locale::getEnglish(),
1650 u
"0"); // see ICU-20844
1652 assertFormatDescending(
1653 u
"Integer Width Zero Fill 3",
1654 u
"integer-width/+000",
1655 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
1656 Locale::getEnglish(),
1667 assertFormatDescending(
1668 u
"Integer Width Max 3",
1669 u
"integer-width/##0",
1670 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
1671 Locale::getEnglish(),
1682 assertFormatDescending(
1683 u
"Integer Width Fixed 2",
1684 u
"integer-width/00",
1685 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1686 Locale::getEnglish(),
1698 u
"Integer Width Remove All A",
1699 u
"integer-width/00",
1700 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1706 u
"Integer Width Remove All B",
1707 u
"integer-width/00",
1708 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1714 u
"Integer Width Remove All B, Bytes Mode",
1715 u
"integer-width/00",
1716 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1718 // Note: this double produces all 17 significant digits
1719 10000000000000002000.0,
1723 void NumberFormatterApiTest::symbols() {
1724 assertFormatDescending(
1725 u
"French Symbols with Japanese Data 1",
1727 NumberFormatter::with().symbols(FRENCH_SYMBOLS
),
1740 u
"French Symbols with Japanese Data 2",
1742 NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS
),
1747 assertFormatDescending(
1748 u
"Latin Numbering System with Arabic Data",
1749 u
"currency/USD latin",
1750 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1762 assertFormatDescending(
1763 u
"Math Numbering System with French Data",
1764 u
"numbering-system/mathsanb",
1765 NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB
)),
1766 Locale::getFrench(),
1778 u
"Swiss Symbols (used in documentation)",
1780 NumberFormatter::with().symbols(SWISS_SYMBOLS
),
1781 Locale::getEnglish(),
1786 u
"Myanmar Symbols (used in documentation)",
1788 NumberFormatter::with().symbols(MYANMAR_SYMBOLS
),
1789 Locale::getEnglish(),
1791 u
"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
1793 // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
1796 u
"Currency symbol should precede number in ar with NS latn",
1797 u
"currency/USD latin",
1798 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1804 u
"Currency symbol should precede number in ar@numbers=latn",
1806 NumberFormatter::with().unit(USD
),
1807 Locale("ar@numbers=latn"),
1812 u
"Currency symbol should follow number in ar-EG with NS arab",
1814 NumberFormatter::with().unit(USD
),
1820 u
"Currency symbol should follow number in ar@numbers=arab",
1822 NumberFormatter::with().unit(USD
),
1823 Locale("ar@numbers=arab"),
1828 u
"NumberingSystem in API should win over @numbers keyword",
1829 u
"currency/USD latin",
1830 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).unit(USD
),
1831 Locale("ar@numbers=arab"),
1835 UErrorCode status
= U_ZERO_ERROR
;
1837 "NumberingSystem in API should win over @numbers keyword in reverse order",
1839 NumberFormatter::withLocale(Locale("ar@numbers=arab")).adoptSymbols(new NumberingSystem(LATN
))
1841 .formatDouble(12345.67, status
)
1844 DecimalFormatSymbols symbols
= SWISS_SYMBOLS
;
1845 UnlocalizedNumberFormatter f
= NumberFormatter::with().symbols(symbols
);
1846 symbols
.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol
, u
"!", status
);
1848 u
"Symbols object should be copied", nullptr, f
, Locale::getEnglish(), 12345.67, u
"12’345.67");
1851 u
"The last symbols setter wins",
1853 NumberFormatter::with().symbols(symbols
).adoptSymbols(new NumberingSystem(LATN
)),
1854 Locale::getEnglish(),
1859 u
"The last symbols setter wins",
1861 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN
)).symbols(symbols
),
1862 Locale::getEnglish(),
1867 // TODO: Enable if/when currency symbol override is added.
1868 //void NumberFormatterTest::symbolsOverride() {
1869 // DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
1870 // dfs.setCurrencySymbol("@");
1871 // dfs.setInternationalCurrencySymbol("foo");
1872 // assertFormatSingle(
1873 // u"Custom Short Currency Symbol",
1874 // NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
1875 // Locale::getEnglish(),
1880 void NumberFormatterApiTest::sign() {
1882 u
"Sign Auto Positive",
1884 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1885 Locale::getEnglish(),
1890 u
"Sign Auto Negative",
1892 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1893 Locale::getEnglish(),
1900 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO
),
1901 Locale::getEnglish(),
1906 u
"Sign Always Positive",
1908 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1909 Locale::getEnglish(),
1914 u
"Sign Always Negative",
1916 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1917 Locale::getEnglish(),
1922 u
"Sign Always Zero",
1924 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS
),
1925 Locale::getEnglish(),
1930 u
"Sign Never Positive",
1932 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1933 Locale::getEnglish(),
1938 u
"Sign Never Negative",
1940 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1941 Locale::getEnglish(),
1948 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER
),
1949 Locale::getEnglish(),
1954 u
"Sign Accounting Positive",
1955 u
"currency/USD sign-accounting",
1956 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1957 Locale::getEnglish(),
1962 u
"Sign Accounting Negative",
1963 u
"currency/USD sign-accounting",
1964 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1965 Locale::getEnglish(),
1970 u
"Sign Accounting Zero",
1971 u
"currency/USD sign-accounting",
1972 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
).unit(USD
),
1973 Locale::getEnglish(),
1978 u
"Sign Accounting-Always Positive",
1979 u
"currency/USD sign-accounting-always",
1980 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1981 Locale::getEnglish(),
1986 u
"Sign Accounting-Always Negative",
1987 u
"currency/USD sign-accounting-always",
1988 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1989 Locale::getEnglish(),
1994 u
"Sign Accounting-Always Zero",
1995 u
"currency/USD sign-accounting-always",
1996 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS
).unit(USD
),
1997 Locale::getEnglish(),
2002 u
"Sign Except-Zero Positive",
2003 u
"sign-except-zero",
2004 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
2005 Locale::getEnglish(),
2010 u
"Sign Except-Zero Negative",
2011 u
"sign-except-zero",
2012 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
2013 Locale::getEnglish(),
2018 u
"Sign Except-Zero Zero",
2019 u
"sign-except-zero",
2020 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO
),
2021 Locale::getEnglish(),
2026 u
"Sign Accounting-Except-Zero Positive",
2027 u
"currency/USD sign-accounting-except-zero",
2028 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
2029 Locale::getEnglish(),
2034 u
"Sign Accounting-Except-Zero Negative",
2035 u
"currency/USD sign-accounting-except-zero",
2036 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
2037 Locale::getEnglish(),
2042 u
"Sign Accounting-Except-Zero Zero",
2043 u
"currency/USD sign-accounting-except-zero",
2044 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO
).unit(USD
),
2045 Locale::getEnglish(),
2050 u
"Sign Accounting Negative Hidden",
2051 u
"currency/USD unit-width-hidden sign-accounting",
2052 NumberFormatter::with()
2053 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2055 .unitWidth(UNUM_UNIT_WIDTH_HIDDEN
),
2056 Locale::getEnglish(),
2061 u
"Sign Accounting Negative Narrow",
2062 u
"currency/USD unit-width-narrow sign-accounting",
2063 NumberFormatter::with()
2064 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2066 .unitWidth(UNUM_UNIT_WIDTH_NARROW
),
2067 Locale::getCanada(),
2072 u
"Sign Accounting Negative Short",
2073 u
"currency/USD sign-accounting",
2074 NumberFormatter::with()
2075 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2077 .unitWidth(UNUM_UNIT_WIDTH_SHORT
),
2078 Locale::getCanada(),
2080 u
"(US$444,444.00)");
2083 u
"Sign Accounting Negative Iso Code",
2084 u
"currency/USD unit-width-iso-code sign-accounting",
2085 NumberFormatter::with()
2086 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2088 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE
),
2089 Locale::getCanada(),
2091 u
"(USD 444,444.00)");
2093 // Note: CLDR does not provide an accounting pattern for long name currency.
2094 // We fall back to normal currency format. This may change in the future.
2096 u
"Sign Accounting Negative Full Name",
2097 u
"currency/USD unit-width-full-name sign-accounting",
2098 NumberFormatter::with()
2099 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING
)
2101 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2102 Locale::getCanada(),
2104 u
"-444,444.00 US dollars");
2107 void NumberFormatterApiTest::signCoverage() {
2108 // https://unicode-org.atlassian.net/browse/ICU-20708
2109 IcuTestErrorCode
status(*this, "signCoverage");
2110 const struct TestCase
{
2111 UNumberSignDisplay sign
;
2112 const char16_t* expectedStrings
[8];
2114 { UNUM_SIGN_AUTO
, { u
"-∞", u
"-1", u
"-0", u
"0", u
"1", u
"∞", u
"NaN", u
"-NaN" } },
2115 { UNUM_SIGN_ALWAYS
, { u
"-∞", u
"-1", u
"-0", u
"+0", u
"+1", u
"+∞", u
"+NaN", u
"-NaN" } },
2116 { UNUM_SIGN_NEVER
, { u
"∞", u
"1", u
"0", u
"0", u
"1", u
"∞", u
"NaN", u
"NaN" } },
2117 { UNUM_SIGN_EXCEPT_ZERO
, { u
"-∞", u
"-1", u
"-0", u
"0", u
"+1", u
"+∞", u
"NaN", u
"-NaN" } },
2119 double negNaN
= std::copysign(uprv_getNaN(), -0.0);
2120 const double inputs
[] = {
2121 -uprv_getInfinity(), -1, -0.0, 0, 1, uprv_getInfinity(), uprv_getNaN(), negNaN
2123 for (auto& cas
: cases
) {
2124 auto sign
= cas
.sign
;
2125 for (int32_t i
= 0; i
< UPRV_LENGTHOF(inputs
); i
++) {
2126 auto input
= inputs
[i
];
2127 auto expected
= cas
.expectedStrings
[i
];
2128 auto actual
= NumberFormatter::with()
2130 .locale(Locale::getUS())
2131 .formatDouble(input
, status
)
2134 DoubleToUnicodeString(input
) + " " + Int64ToUnicodeString(sign
),
2140 void NumberFormatterApiTest::decimal() {
2141 assertFormatDescending(
2144 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO
),
2145 Locale::getEnglish(),
2156 assertFormatDescending(
2157 u
"Decimal Always Shown",
2159 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS
),
2160 Locale::getEnglish(),
2172 void NumberFormatterApiTest::scale() {
2173 assertFormatDescending(
2176 NumberFormatter::with().scale(Scale::none()),
2177 Locale::getEnglish(),
2188 assertFormatDescending(
2189 u
"Multiplier Power of Ten",
2191 NumberFormatter::with().scale(Scale::powerOfTen(6)),
2192 Locale::getEnglish(),
2203 assertFormatDescending(
2204 u
"Multiplier Arbitrary Double",
2206 NumberFormatter::with().scale(Scale::byDouble(5.2)),
2207 Locale::getEnglish(),
2218 assertFormatDescending(
2219 u
"Multiplier Arbitrary BigDecimal",
2221 NumberFormatter::with().scale(Scale::byDecimal({"5.2", -1})),
2222 Locale::getEnglish(),
2233 assertFormatDescending(
2234 u
"Multiplier Arbitrary Double And Power Of Ten",
2236 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(5.2, 3)),
2237 Locale::getEnglish(),
2248 assertFormatDescending(
2251 NumberFormatter::with().scale(Scale::byDouble(0)),
2252 Locale::getEnglish(),
2264 u
"Multiplier Skeleton Scientific Notation and Percent",
2265 u
"percent scale/1E2",
2266 NumberFormatter::with().unit(NoUnit::percent()).scale(Scale::powerOfTen(2)),
2267 Locale::getEnglish(),
2272 u
"Negative Multiplier",
2274 NumberFormatter::with().scale(Scale::byDouble(-5.2)),
2275 Locale::getEnglish(),
2280 u
"Negative One Multiplier",
2282 NumberFormatter::with().scale(Scale::byDouble(-1)),
2283 Locale::getEnglish(),
2288 u
"Two-Type Multiplier with Overlap",
2290 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(100, 2)),
2291 Locale::getEnglish(),
2296 void NumberFormatterApiTest::locale() {
2297 // Coverage for the locale setters.
2298 UErrorCode status
= U_ZERO_ERROR
;
2299 UnicodeString actual
= NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status
)
2301 assertEquals("Locale withLocale()", u
"1\u202f234", actual
);
2304 void NumberFormatterApiTest::skeletonUserGuideExamples() {
2305 IcuTestErrorCode
status(*this, "skeletonUserGuideExamples");
2307 // Test the skeleton examples in userguide/format_parse/numbers/skeletons.md
2309 const char16_t* skeleton
;
2311 const char16_t* expected
;
2313 {u
"percent", 25, u
"25%"},
2314 {u
".00", 25, u
"25.00"},
2315 {u
"percent .00", 25, u
"25.00%"},
2316 {u
"scale/100", 0.3, u
"30"},
2317 {u
"percent scale/100", 0.3, u
"30%"},
2318 {u
"measure-unit/length-meter", 5, u
"5 m"},
2319 {u
"measure-unit/length-meter unit-width-full-name", 5, u
"5 meters"},
2320 {u
"currency/CAD", 10, u
"CA$10.00"},
2321 {u
"currency/CAD unit-width-narrow", 10, u
"$10.00"},
2322 {u
"compact-short", 5000, u
"5K"},
2323 {u
"compact-long", 5000, u
"5 thousand"},
2324 {u
"compact-short currency/CAD", 5000, u
"CA$5K"},
2325 {u
"", 5000, u
"5,000"},
2326 {u
"group-min2", 5000, u
"5000"},
2327 {u
"group-min2", 15000, u
"15,000"},
2328 {u
"sign-always", 60, u
"+60"},
2329 {u
"sign-always", 0, u
"+0"},
2330 {u
"sign-except-zero", 60, u
"+60"},
2331 {u
"sign-except-zero", 0, u
"0"},
2332 {u
"sign-accounting currency/CAD", -40, u
"(CA$40.00)"}
2335 for (const auto& cas
: cases
) {
2336 status
.setScope(cas
.skeleton
);
2337 FormattedNumber actual
= NumberFormatter::forSkeleton(cas
.skeleton
, status
)
2339 .formatDouble(cas
.input
, status
);
2340 assertEquals(cas
.skeleton
, cas
.expected
, actual
.toTempString(status
));
2341 status
.errIfFailureAndReset();
2345 void NumberFormatterApiTest::formatTypes() {
2346 UErrorCode status
= U_ZERO_ERROR
;
2347 LocalizedNumberFormatter formatter
= NumberFormatter::withLocale(Locale::getEnglish());
2350 assertEquals("Format double", "514.23", formatter
.formatDouble(514.23, status
).toString(status
));
2353 assertEquals("Format int64", "51,423", formatter
.formatDouble(51423L, status
).toString(status
));
2356 UnicodeString actual
= formatter
.formatDecimal("98765432123456789E1", status
).toString(status
);
2357 assertEquals("Format decNumber", u
"987,654,321,234,567,890", actual
);
2359 // Also test proper DecimalQuantity bytes storage when all digits are in the fraction.
2360 // The number needs to have exactly 40 digits, which is the size of the default buffer.
2361 // (issue discovered by the address sanitizer in C++)
2362 static const char* str
= "0.009876543210987654321098765432109876543211";
2363 actual
= formatter
.precision(Precision::unlimited()).formatDecimal(str
, status
).toString(status
);
2364 assertEquals("Format decNumber to 40 digits", str
, actual
);
2367 void NumberFormatterApiTest::fieldPositionLogic() {
2368 IcuTestErrorCode
status(*this, "fieldPositionLogic");
2370 const char16_t* message
= u
"Field position logic test";
2372 FormattedNumber fmtd
= assertFormatSingle(
2375 NumberFormatter::with(),
2376 Locale::getEnglish(),
2378 u
"-9,876,543,210.12");
2380 static const UFieldPosition expectedFieldPositions
[] = {
2381 // field, begin index, end index
2382 {UNUM_SIGN_FIELD
, 0, 1},
2383 {UNUM_GROUPING_SEPARATOR_FIELD
, 2, 3},
2384 {UNUM_GROUPING_SEPARATOR_FIELD
, 6, 7},
2385 {UNUM_GROUPING_SEPARATOR_FIELD
, 10, 11},
2386 {UNUM_INTEGER_FIELD
, 1, 14},
2387 {UNUM_DECIMAL_SEPARATOR_FIELD
, 14, 15},
2388 {UNUM_FRACTION_FIELD
, 15, 17}};
2390 assertNumberFieldPositions(
2393 expectedFieldPositions
,
2394 UPRV_LENGTHOF(expectedFieldPositions
));
2396 // Test the iteration functionality of nextFieldPosition
2397 FieldPosition actual
= {UNUM_GROUPING_SEPARATOR_FIELD
};
2399 while (fmtd
.nextFieldPosition(actual
, status
)) {
2400 UFieldPosition expected
= expectedFieldPositions
[i
++];
2402 UnicodeString(u
"Next for grouping, field, case #") + Int64ToUnicodeString(i
),
2406 UnicodeString(u
"Next for grouping, begin index, case #") + Int64ToUnicodeString(i
),
2407 expected
.beginIndex
,
2408 actual
.getBeginIndex());
2410 UnicodeString(u
"Next for grouping, end index, case #") + Int64ToUnicodeString(i
),
2412 actual
.getEndIndex());
2414 assertEquals(u
"Should have seen all grouping separators", 4, i
);
2416 // Make sure strings without fraction do not contain fraction field
2417 actual
= {UNUM_FRACTION_FIELD
};
2418 fmtd
= NumberFormatter::withLocale("en").formatInt(5, status
);
2419 assertFalse(u
"No fraction part in an integer", fmtd
.nextFieldPosition(actual
, status
));
2422 void NumberFormatterApiTest::fieldPositionCoverage() {
2423 IcuTestErrorCode
status(*this, "fieldPositionCoverage");
2426 const char16_t* message
= u
"Measure unit field position basic";
2427 FormattedNumber result
= assertFormatSingle(
2429 u
"measure-unit/temperature-fahrenheit",
2430 NumberFormatter::with().unit(FAHRENHEIT
),
2431 Locale::getEnglish(),
2434 static const UFieldPosition expectedFieldPositions
[] = {
2435 // field, begin index, end index
2436 {UNUM_INTEGER_FIELD
, 0, 2},
2437 {UNUM_MEASURE_UNIT_FIELD
, 2, 4}};
2438 assertNumberFieldPositions(
2441 expectedFieldPositions
,
2442 UPRV_LENGTHOF(expectedFieldPositions
));
2446 const char16_t* message
= u
"Measure unit field position with compound unit";
2447 FormattedNumber result
= assertFormatSingle(
2449 u
"measure-unit/temperature-fahrenheit per-measure-unit/duration-day",
2450 NumberFormatter::with().unit(FAHRENHEIT
).perUnit(DAY
),
2451 Locale::getEnglish(),
2454 static const UFieldPosition expectedFieldPositions
[] = {
2455 // field, begin index, end index
2456 {UNUM_INTEGER_FIELD
, 0, 2},
2457 // coverage for old enum:
2458 {DecimalFormat::kMeasureUnitField
, 2, 6}};
2459 assertNumberFieldPositions(
2462 expectedFieldPositions
,
2463 UPRV_LENGTHOF(expectedFieldPositions
));
2467 const char16_t* message
= u
"Measure unit field position with spaces";
2468 FormattedNumber result
= assertFormatSingle(
2470 u
"measure-unit/length-meter unit-width-full-name",
2471 NumberFormatter::with().unit(METER
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2472 Locale::getEnglish(),
2475 static const UFieldPosition expectedFieldPositions
[] = {
2476 // field, begin index, end index
2477 {UNUM_INTEGER_FIELD
, 0, 2},
2478 // note: field starts after the space
2479 {UNUM_MEASURE_UNIT_FIELD
, 3, 9}};
2480 assertNumberFieldPositions(
2483 expectedFieldPositions
,
2484 UPRV_LENGTHOF(expectedFieldPositions
));
2488 const char16_t* message
= u
"Measure unit field position with prefix and suffix";
2489 FormattedNumber result
= assertFormatSingle(
2491 u
"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
2492 NumberFormatter::with().unit(METER
).perUnit(SECOND
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2493 "ky", // locale with the interesting data
2495 u
"секундасына 68 метр");
2496 static const UFieldPosition expectedFieldPositions
[] = {
2497 // field, begin index, end index
2498 {UNUM_MEASURE_UNIT_FIELD
, 0, 11},
2499 {UNUM_INTEGER_FIELD
, 12, 14},
2500 {UNUM_MEASURE_UNIT_FIELD
, 15, 19}};
2501 assertNumberFieldPositions(
2504 expectedFieldPositions
,
2505 UPRV_LENGTHOF(expectedFieldPositions
));
2509 const char16_t* message
= u
"Measure unit field position with inner spaces";
2510 FormattedNumber result
= assertFormatSingle(
2512 u
"measure-unit/temperature-fahrenheit unit-width-full-name",
2513 NumberFormatter::with().unit(FAHRENHEIT
).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2514 "vi", // locale with the interesting data
2517 static const UFieldPosition expectedFieldPositions
[] = {
2518 // field, begin index, end index
2519 {UNUM_INTEGER_FIELD
, 0, 2},
2520 // Should trim leading/trailing spaces, but not inner spaces:
2521 {UNUM_MEASURE_UNIT_FIELD
, 3, 7}};
2522 assertNumberFieldPositions(
2525 expectedFieldPositions
,
2526 UPRV_LENGTHOF(expectedFieldPositions
));
2530 // Data: other{"{0} K"} == "\u200E{0} K"
2531 // If that data changes, try to find another example of a non-empty unit prefix/suffix
2532 // that is also all ignorables (whitespace and bidi control marks).
2533 const char16_t* message
= u
"Measure unit field position with fully ignorable prefix";
2534 FormattedNumber result
= assertFormatSingle(
2536 u
"measure-unit/temperature-kelvin",
2537 NumberFormatter::with().unit(KELVIN
),
2538 "fa", // locale with the interesting data
2541 static const UFieldPosition expectedFieldPositions
[] = {
2542 // field, begin index, end index
2543 {UNUM_INTEGER_FIELD
, 1, 3},
2544 {UNUM_MEASURE_UNIT_FIELD
, 4, 5}};
2545 assertNumberFieldPositions(
2548 expectedFieldPositions
,
2549 UPRV_LENGTHOF(expectedFieldPositions
));
2553 const char16_t* message
= u
"Compact field basic";
2554 FormattedNumber result
= assertFormatSingle(
2557 NumberFormatter::with().notation(Notation::compactShort()),
2561 static const UFieldPosition expectedFieldPositions
[] = {
2562 // field, begin index, end index
2563 {UNUM_INTEGER_FIELD
, 0, 2},
2564 {UNUM_COMPACT_FIELD
, 2, 3}};
2565 assertNumberFieldPositions(
2568 expectedFieldPositions
,
2569 UPRV_LENGTHOF(expectedFieldPositions
));
2573 const char16_t* message
= u
"Compact field with spaces";
2574 FormattedNumber result
= assertFormatSingle(
2577 NumberFormatter::with().notation(Notation::compactLong()),
2581 static const UFieldPosition expectedFieldPositions
[] = {
2582 // field, begin index, end index
2583 {UNUM_INTEGER_FIELD
, 0, 2},
2584 {UNUM_COMPACT_FIELD
, 3, 11}};
2585 assertNumberFieldPositions(
2588 expectedFieldPositions
,
2589 UPRV_LENGTHOF(expectedFieldPositions
));
2593 const char16_t* message
= u
"Compact field with inner space";
2594 FormattedNumber result
= assertFormatSingle(
2597 NumberFormatter::with().notation(Notation::compactLong()),
2598 "fil", // locale with interesting data
2601 static const UFieldPosition expectedFieldPositions
[] = {
2602 // field, begin index, end index
2603 {UNUM_INTEGER_FIELD
, 0, 1},
2604 {UNUM_COMPACT_FIELD
, 2, 9}};
2605 assertNumberFieldPositions(
2608 expectedFieldPositions
,
2609 UPRV_LENGTHOF(expectedFieldPositions
));
2613 const char16_t* message
= u
"Compact field with bidi mark";
2614 FormattedNumber result
= assertFormatSingle(
2617 NumberFormatter::with().notation(Notation::compactLong()),
2618 "he", // locale with interesting data
2621 static const UFieldPosition expectedFieldPositions
[] = {
2622 // field, begin index, end index
2623 {UNUM_INTEGER_FIELD
, 1, 2},
2624 {UNUM_COMPACT_FIELD
, 3, 6}};
2625 assertNumberFieldPositions(
2628 expectedFieldPositions
,
2629 UPRV_LENGTHOF(expectedFieldPositions
));
2633 const char16_t* message
= u
"Compact with currency fields";
2634 FormattedNumber result
= assertFormatSingle(
2636 u
"compact-short currency/USD",
2637 NumberFormatter::with().notation(Notation::compactShort()).unit(USD
),
2638 "sr_Latn", // locale with interesting data
2641 static const UFieldPosition expectedFieldPositions
[] = {
2642 // field, begin index, end index
2643 {UNUM_INTEGER_FIELD
, 0, 2},
2644 {UNUM_COMPACT_FIELD
, 3, 8},
2645 {UNUM_CURRENCY_FIELD
, 9, 12}};
2646 assertNumberFieldPositions(
2649 expectedFieldPositions
,
2650 UPRV_LENGTHOF(expectedFieldPositions
));
2654 const char16_t* message
= u
"Currency long name fields";
2655 FormattedNumber result
= assertFormatSingle(
2657 u
"currency/USD unit-width-full-name",
2658 NumberFormatter::with().unit(USD
)
2659 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME
),
2662 u
"12,345.00 US dollars");
2663 static const UFieldPosition expectedFieldPositions
[] = {
2664 // field, begin index, end index
2665 {UNUM_GROUPING_SEPARATOR_FIELD
, 2, 3},
2666 {UNUM_INTEGER_FIELD
, 0, 6},
2667 {UNUM_DECIMAL_SEPARATOR_FIELD
, 6, 7},
2668 {UNUM_FRACTION_FIELD
, 7, 9},
2669 {UNUM_CURRENCY_FIELD
, 10, 20}};
2670 assertNumberFieldPositions(
2673 expectedFieldPositions
,
2674 UPRV_LENGTHOF(expectedFieldPositions
));
2678 const char16_t* message
= u
"Compact with measure unit fields";
2679 FormattedNumber result
= assertFormatSingle(
2681 u
"compact-long measure-unit/length-meter unit-width-full-name",
2682 NumberFormatter::with().notation(Notation::compactLong())
2684 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME
),
2687 u
"65 thousand meters");
2688 static const UFieldPosition expectedFieldPositions
[] = {
2689 // field, begin index, end index
2690 {UNUM_INTEGER_FIELD
, 0, 2},
2691 {UNUM_COMPACT_FIELD
, 3, 11},
2692 {UNUM_MEASURE_UNIT_FIELD
, 12, 18}};
2693 assertNumberFieldPositions(
2696 expectedFieldPositions
,
2697 UPRV_LENGTHOF(expectedFieldPositions
));
2701 void NumberFormatterApiTest::toFormat() {
2702 IcuTestErrorCode
status(*this, "icuFormat");
2703 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale("fr")
2704 .precision(Precision::fixedFraction(3));
2705 LocalPointer
<Format
> format(lnf
.toFormat(status
), status
);
2706 FieldPosition
fpos(UNUM_DECIMAL_SEPARATOR_FIELD
);
2708 format
->format(514.23, sb
, fpos
, status
);
2709 assertEquals("Should correctly format number", u
"514,230", sb
);
2710 assertEquals("Should find decimal separator", 3, fpos
.getBeginIndex());
2711 assertEquals("Should find end of decimal separator", 4, fpos
.getEndIndex());
2713 "ICU Format should round-trip",
2714 lnf
.toSkeleton(status
),
2715 dynamic_cast<LocalizedNumberFormatterAsFormat
*>(format
.getAlias())->getNumberFormatter()
2716 .toSkeleton(status
));
2718 FieldPositionIterator fpi1
;
2719 lnf
.formatDouble(514.23, status
).getAllFieldPositions(fpi1
, status
);
2720 FieldPositionIterator fpi2
;
2721 format
->format(514.23, sb
.remove(), &fpi2
, status
);
2722 assertTrue("Should produce same field position iterator", fpi1
== fpi2
);
2725 void NumberFormatterApiTest::errors() {
2726 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale(Locale::getEnglish()).precision(
2727 Precision::fixedFraction(
2731 UErrorCode status
= U_ZERO_ERROR
;
2732 FormattedNumber fn
= lnf
.formatInt(1, status
);
2734 "Should fail in formatInt method with error code for rounding",
2735 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2739 status
= U_ZERO_ERROR
;
2740 fn
= lnf
.formatDouble(1.0, status
);
2742 "Should fail in formatDouble method with error code for rounding",
2743 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2746 // formatDecimal (decimal error)
2747 status
= U_ZERO_ERROR
;
2748 fn
= NumberFormatter::withLocale("en").formatDecimal("1x2", status
);
2750 "Should fail in formatDecimal method with error code for decimal number syntax",
2751 U_DECIMAL_NUMBER_SYNTAX_ERROR
,
2754 // formatDecimal (setting error)
2755 status
= U_ZERO_ERROR
;
2756 fn
= lnf
.formatDecimal("1.0", status
);
2758 "Should fail in formatDecimal method with error code for rounding",
2759 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2763 status
= U_ZERO_ERROR
;
2764 UnicodeString output
= lnf
.toSkeleton(status
);
2766 "Should fail on toSkeleton terminal method with correct error code",
2767 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2770 "Terminal toSkeleton on error object should be bogus",
2774 status
= U_ZERO_ERROR
;
2776 fn
.nextFieldPosition(fp
, status
);
2778 "Should fail on FieldPosition terminal method with correct error code",
2779 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2782 // FieldPositionIterator
2783 status
= U_ZERO_ERROR
;
2784 FieldPositionIterator fpi
;
2785 fn
.getAllFieldPositions(fpi
, status
);
2787 "Should fail on FieldPositoinIterator terminal method with correct error code",
2788 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2792 status
= U_ZERO_ERROR
;
2793 UnicodeStringAppendable
appendable(output
.remove());
2794 fn
.appendTo(appendable
, status
);
2796 "Should fail on Appendable terminal method with correct error code",
2797 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2801 status
= U_ZERO_ERROR
;
2802 output
= fn
.toString(status
);
2804 "Should fail on UnicodeString terminal method with correct error code",
2805 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2808 "Terminal UnicodeString on error object should be bogus",
2812 status
= U_ZERO_ERROR
;
2813 lnf
.copyErrorTo(status
);
2815 "Should fail since rounder is not legal with correct error code",
2816 U_NUMBER_ARG_OUTOFBOUNDS_ERROR
,
2820 void NumberFormatterApiTest::validRanges() {
2822 #define EXPECTED_MAX_INT_FRAC_SIG 999
2824 #define VALID_RANGE_ASSERT(status, method, lowerBound, argument) UPRV_BLOCK_MACRO_BEGIN { \
2825 UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \
2827 : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \
2829 UnicodeString(u"Incorrect status for " #method " on input ") \
2830 + Int64ToUnicodeString(argument), \
2833 } UPRV_BLOCK_MACRO_END
2835 #define VALID_RANGE_ONEARG(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
2836 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
2837 UErrorCode status = U_ZERO_ERROR; \
2838 NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \
2839 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2841 } UPRV_BLOCK_MACRO_END
2843 #define VALID_RANGE_TWOARGS(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
2844 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
2845 UErrorCode status = U_ZERO_ERROR; \
2846 /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \
2847 NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \
2848 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2849 status = U_ZERO_ERROR; \
2850 /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \
2851 NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \
2852 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
2853 /* Check that first argument must be less than or equal to second argument */ \
2854 NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \
2855 assertEquals("Incorrect status for " #method " on max < min input", \
2856 U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \
2859 } UPRV_BLOCK_MACRO_END
2861 VALID_RANGE_ONEARG(precision
, Precision::fixedFraction
, 0);
2862 VALID_RANGE_ONEARG(precision
, Precision::minFraction
, 0);
2863 VALID_RANGE_ONEARG(precision
, Precision::maxFraction
, 0);
2864 VALID_RANGE_TWOARGS(precision
, Precision::minMaxFraction
, 0);
2865 VALID_RANGE_ONEARG(precision
, Precision::fixedSignificantDigits
, 1);
2866 VALID_RANGE_ONEARG(precision
, Precision::minSignificantDigits
, 1);
2867 VALID_RANGE_ONEARG(precision
, Precision::maxSignificantDigits
, 1);
2868 VALID_RANGE_TWOARGS(precision
, Precision::minMaxSignificantDigits
, 1);
2869 VALID_RANGE_ONEARG(precision
, Precision::fixedFraction(1).withMinDigits
, 1);
2870 VALID_RANGE_ONEARG(precision
, Precision::fixedFraction(1).withMaxDigits
, 1);
2871 VALID_RANGE_ONEARG(notation
, Notation::scientific().withMinExponentDigits
, 1);
2872 VALID_RANGE_ONEARG(integerWidth
, IntegerWidth::zeroFillTo
, 0);
2873 VALID_RANGE_ONEARG(integerWidth
, IntegerWidth::zeroFillTo(0).truncateAt
, -1);
2876 void NumberFormatterApiTest::copyMove() {
2877 IcuTestErrorCode
status(*this, "copyMove");
2879 // Default constructors
2880 LocalizedNumberFormatter l1
;
2881 assertEquals("Initial behavior", u
"10", l1
.formatInt(10, status
).toString(status
), true);
2882 if (status
.errDataIfFailureAndReset()) { return; }
2883 assertEquals("Initial call count", 1, l1
.getCallCount());
2884 assertTrue("Initial compiled", l1
.getCompiled() == nullptr);
2887 l1
= NumberFormatter::withLocale("en").unit(NoUnit::percent()).threshold(3);
2888 assertEquals("Initial behavior", u
"10%", l1
.formatInt(10, status
).toString(status
));
2889 assertEquals("Initial call count", 1, l1
.getCallCount());
2890 assertTrue("Initial compiled", l1
.getCompiled() == nullptr);
2891 l1
.formatInt(123, status
);
2892 assertEquals("Still not compiled", 2, l1
.getCallCount());
2893 assertTrue("Still not compiled", l1
.getCompiled() == nullptr);
2894 l1
.formatInt(123, status
);
2895 assertEquals("Compiled", u
"10%", l1
.formatInt(10, status
).toString(status
));
2896 assertEquals("Compiled", INT32_MIN
, l1
.getCallCount());
2897 assertTrue("Compiled", l1
.getCompiled() != nullptr);
2900 LocalizedNumberFormatter l2
= l1
;
2901 assertEquals("[constructor] Copy behavior", u
"10%", l2
.formatInt(10, status
).toString(status
));
2902 assertEquals("[constructor] Copy should not have compiled state", 1, l2
.getCallCount());
2903 assertTrue("[constructor] Copy should not have compiled state", l2
.getCompiled() == nullptr);
2906 LocalizedNumberFormatter l3
= std::move(l1
);
2907 assertEquals("[constructor] Move behavior", u
"10%", l3
.formatInt(10, status
).toString(status
));
2908 assertEquals("[constructor] Move *should* have compiled state", INT32_MIN
, l3
.getCallCount());
2909 assertTrue("[constructor] Move *should* have compiled state", l3
.getCompiled() != nullptr);
2910 assertEquals("[constructor] Source should be reset after move", 0, l1
.getCallCount());
2911 assertTrue("[constructor] Source should be reset after move", l1
.getCompiled() == nullptr);
2913 // Reset l1 and l2 to check for macro-props copying for behavior testing
2914 // Make the test more interesting: also warm them up with a compiled formatter.
2915 l1
= NumberFormatter::withLocale("en");
2916 l1
.formatInt(1, status
);
2917 l1
.formatInt(1, status
);
2918 l1
.formatInt(1, status
);
2919 l2
= NumberFormatter::withLocale("en");
2920 l2
.formatInt(1, status
);
2921 l2
.formatInt(1, status
);
2922 l2
.formatInt(1, status
);
2926 assertEquals("[assignment] Copy behavior", u
"10%", l1
.formatInt(10, status
).toString(status
));
2927 assertEquals("[assignment] Copy should not have compiled state", 1, l1
.getCallCount());
2928 assertTrue("[assignment] Copy should not have compiled state", l1
.getCompiled() == nullptr);
2932 assertEquals("[assignment] Move behavior", u
"10%", l2
.formatInt(10, status
).toString(status
));
2933 assertEquals("[assignment] Move *should* have compiled state", INT32_MIN
, l2
.getCallCount());
2934 assertTrue("[assignment] Move *should* have compiled state", l2
.getCompiled() != nullptr);
2935 assertEquals("[assignment] Source should be reset after move", 0, l3
.getCallCount());
2936 assertTrue("[assignment] Source should be reset after move", l3
.getCompiled() == nullptr);
2938 // Coverage tests for UnlocalizedNumberFormatter
2939 UnlocalizedNumberFormatter u1
;
2940 assertEquals("Default behavior", u
"10", u1
.locale("en").formatInt(10, status
).toString(status
));
2941 u1
= u1
.unit(NoUnit::percent());
2942 assertEquals("Copy assignment", u
"10%", u1
.locale("en").formatInt(10, status
).toString(status
));
2943 UnlocalizedNumberFormatter u2
= u1
;
2944 assertEquals("Copy constructor", u
"10%", u2
.locale("en").formatInt(10, status
).toString(status
));
2945 UnlocalizedNumberFormatter u3
= std::move(u1
);
2946 assertEquals("Move constructor", u
"10%", u3
.locale("en").formatInt(10, status
).toString(status
));
2947 u1
= NumberFormatter::with();
2949 assertEquals("Move assignment", u
"10%", u1
.locale("en").formatInt(10, status
).toString(status
));
2951 // FormattedNumber move operators
2952 FormattedNumber result
= l1
.formatInt(10, status
);
2953 assertEquals("FormattedNumber move constructor", u
"10%", result
.toString(status
));
2954 result
= l1
.formatInt(20, status
);
2955 assertEquals("FormattedNumber move assignment", u
"20%", result
.toString(status
));
2958 void NumberFormatterApiTest::localPointerCAPI() {
2959 // NOTE: This is also the sample code in unumberformatter.h
2960 UErrorCode ec
= U_ZERO_ERROR
;
2963 LocalUNumberFormatterPointer
uformatter(unumf_openForSkeletonAndLocale(u
"percent", -1, "en", &ec
));
2964 LocalUFormattedNumberPointer
uresult(unumf_openResult(&ec
));
2965 if (!assertSuccess("", ec
, true, __FILE__
, __LINE__
)) { return; }
2967 // Format a decimal number:
2968 unumf_formatDecimal(uformatter
.getAlias(), "9.87E-3", -1, uresult
.getAlias(), &ec
);
2969 if (!assertSuccess("", ec
, true, __FILE__
, __LINE__
)) { return; }
2971 // Get the location of the percent sign:
2972 UFieldPosition ufpos
= {UNUM_PERCENT_FIELD
, 0, 0};
2973 unumf_resultNextFieldPosition(uresult
.getAlias(), &ufpos
, &ec
);
2974 assertEquals("Percent sign location within '0.00987%'", 7, ufpos
.beginIndex
);
2975 assertEquals("Percent sign location within '0.00987%'", 8, ufpos
.endIndex
);
2977 // No need to do any cleanup since we are using LocalPointer.
2980 void NumberFormatterApiTest::toObject() {
2981 IcuTestErrorCode
status(*this, "toObject");
2983 // const lvalue version
2985 LocalizedNumberFormatter lnf
= NumberFormatter::withLocale("en")
2986 .precision(Precision::fixedFraction(2));
2987 LocalPointer
<LocalizedNumberFormatter
> lnf2(lnf
.clone());
2988 assertFalse("should create successfully, const lvalue", lnf2
.isNull());
2989 assertEquals("object API test, const lvalue", u
"1,000.00",
2990 lnf2
->formatDouble(1000, status
).toString(status
));
2993 // rvalue reference version
2995 LocalPointer
<LocalizedNumberFormatter
> lnf(
2996 NumberFormatter::withLocale("en")
2997 .precision(Precision::fixedFraction(2))
2999 assertFalse("should create successfully, rvalue reference", lnf
.isNull());
3000 assertEquals("object API test, rvalue reference", u
"1,000.00",
3001 lnf
->formatDouble(1000, status
).toString(status
));
3004 // to std::unique_ptr via constructor
3006 std::unique_ptr
<LocalizedNumberFormatter
> lnf(
3007 NumberFormatter::withLocale("en")
3008 .precision(Precision::fixedFraction(2))
3010 assertTrue("should create successfully, unique_ptr", static_cast<bool>(lnf
));
3011 assertEquals("object API test, unique_ptr", u
"1,000.00",
3012 lnf
->formatDouble(1000, status
).toString(status
));
3015 // to std::unique_ptr via assignment
3017 std::unique_ptr
<LocalizedNumberFormatter
> lnf
=
3018 NumberFormatter::withLocale("en")
3019 .precision(Precision::fixedFraction(2))
3021 assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf
));
3022 assertEquals("object API test, unique_ptr B", u
"1,000.00",
3023 lnf
->formatDouble(1000, status
).toString(status
));
3026 // to LocalPointer via assignment
3028 LocalPointer
<UnlocalizedNumberFormatter
> f
=
3029 NumberFormatter::with().clone();
3032 // make sure no memory leaks
3034 NumberFormatter::with().clone();
3038 void NumberFormatterApiTest::toDecimalNumber() {
3039 IcuTestErrorCode
status(*this, "toDecimalNumber");
3040 FormattedNumber fn
= NumberFormatter::withLocale("bn-BD")
3041 .scale(Scale::powerOfTen(2))
3042 .precision(Precision::maxSignificantDigits(5))
3043 .formatDouble(9.87654321e12
, status
);
3044 assertEquals("Should have expected localized string result",
3045 u
"৯৮,৭৬,৫০,০০,০০,০০,০০০", fn
.toString(status
));
3046 assertEquals(u
"Should have expected toDecimalNumber string result",
3047 "9.8765E+14", fn
.toDecimalNumber
<std::string
>(status
).c_str());
3051 void NumberFormatterApiTest::assertFormatDescending(const char16_t* umessage
, const char16_t* uskeleton
,
3052 const UnlocalizedNumberFormatter
& f
, Locale locale
,
3055 va_start(args
, locale
);
3056 UnicodeString
message(TRUE
, umessage
, -1);
3057 static double inputs
[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
3058 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
3059 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
3060 IcuTestErrorCode
status(*this, "assertFormatDescending");
3061 status
.setScope(message
);
3062 UnicodeString expecteds
[10];
3063 for (int16_t i
= 0; i
< 9; i
++) {
3064 char16_t caseNumber
= u
'0' + i
;
3065 double d
= inputs
[i
];
3066 UnicodeString expected
= va_arg(args
, const char16_t*);
3067 expecteds
[i
] = expected
;
3068 UnicodeString actual1
= l1
.formatDouble(d
, status
).toString(status
);
3069 assertSuccess(message
+ u
": Unsafe Path: " + caseNumber
, status
);
3070 assertEquals(message
+ u
": Unsafe Path: " + caseNumber
, expected
, actual1
);
3071 UnicodeString actual2
= l2
.formatDouble(d
, status
).toString(status
);
3072 assertSuccess(message
+ u
": Safe Path: " + caseNumber
, status
);
3073 assertEquals(message
+ u
": Safe Path: " + caseNumber
, expected
, actual2
);
3075 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
3076 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
3077 // Only compare normalized skeletons: the tests need not provide the normalized forms.
3078 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
3079 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
3080 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
3081 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
3082 for (int32_t i
= 0; i
< 9; i
++) {
3083 double d
= inputs
[i
];
3084 UnicodeString actual3
= l3
.formatDouble(d
, status
).toString(status
);
3085 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + d
, expecteds
[i
], actual3
);
3088 assertUndefinedSkeleton(f
);
3092 void NumberFormatterApiTest::assertFormatDescendingBig(const char16_t* umessage
, const char16_t* uskeleton
,
3093 const UnlocalizedNumberFormatter
& f
, Locale locale
,
3096 va_start(args
, locale
);
3097 UnicodeString
message(TRUE
, umessage
, -1);
3098 static double inputs
[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
3099 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
3100 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
3101 IcuTestErrorCode
status(*this, "assertFormatDescendingBig");
3102 status
.setScope(message
);
3103 UnicodeString expecteds
[10];
3104 for (int16_t i
= 0; i
< 9; i
++) {
3105 char16_t caseNumber
= u
'0' + i
;
3106 double d
= inputs
[i
];
3107 UnicodeString expected
= va_arg(args
, const char16_t*);
3108 expecteds
[i
] = expected
;
3109 UnicodeString actual1
= l1
.formatDouble(d
, status
).toString(status
);
3110 assertSuccess(message
+ u
": Unsafe Path: " + caseNumber
, status
);
3111 assertEquals(message
+ u
": Unsafe Path: " + caseNumber
, expected
, actual1
);
3112 UnicodeString actual2
= l2
.formatDouble(d
, status
).toString(status
);
3113 assertSuccess(message
+ u
": Safe Path: " + caseNumber
, status
);
3114 assertEquals(message
+ u
": Safe Path: " + caseNumber
, expected
, actual2
);
3116 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
3117 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
3118 // Only compare normalized skeletons: the tests need not provide the normalized forms.
3119 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
3120 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
3121 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
3122 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
3123 for (int32_t i
= 0; i
< 9; i
++) {
3124 double d
= inputs
[i
];
3125 UnicodeString actual3
= l3
.formatDouble(d
, status
).toString(status
);
3126 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + d
, expecteds
[i
], actual3
);
3129 assertUndefinedSkeleton(f
);
3134 NumberFormatterApiTest::assertFormatSingle(const char16_t* umessage
, const char16_t* uskeleton
,
3135 const UnlocalizedNumberFormatter
& f
, Locale locale
,
3136 double input
, const UnicodeString
& expected
) {
3137 UnicodeString
message(TRUE
, umessage
, -1);
3138 const LocalizedNumberFormatter l1
= f
.threshold(0).locale(locale
); // no self-regulation
3139 const LocalizedNumberFormatter l2
= f
.threshold(1).locale(locale
); // all self-regulation
3140 IcuTestErrorCode
status(*this, "assertFormatSingle");
3141 status
.setScope(message
);
3142 FormattedNumber result1
= l1
.formatDouble(input
, status
);
3143 UnicodeString actual1
= result1
.toString(status
);
3144 assertSuccess(message
+ u
": Unsafe Path", status
);
3145 assertEquals(message
+ u
": Unsafe Path", expected
, actual1
);
3146 UnicodeString actual2
= l2
.formatDouble(input
, status
).toString(status
);
3147 assertSuccess(message
+ u
": Safe Path", status
);
3148 assertEquals(message
+ u
": Safe Path", expected
, actual2
);
3149 if (uskeleton
!= nullptr) { // if null, skeleton is declared as undefined.
3150 UnicodeString
skeleton(TRUE
, uskeleton
, -1);
3151 // Only compare normalized skeletons: the tests need not provide the normalized forms.
3152 // Use the normalized form to construct the testing formatter to ensure no loss of info.
3153 UnicodeString normalized
= NumberFormatter::forSkeleton(skeleton
, status
).toSkeleton(status
);
3154 assertEquals(message
+ ": Skeleton:", normalized
, f
.toSkeleton(status
));
3155 LocalizedNumberFormatter l3
= NumberFormatter::forSkeleton(normalized
, status
).locale(locale
);
3156 UnicodeString actual3
= l3
.formatDouble(input
, status
).toString(status
);
3157 assertEquals(message
+ ": Skeleton Path: '" + normalized
+ "': " + input
, expected
, actual3
);
3159 assertUndefinedSkeleton(f
);
3164 void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter
& f
) {
3165 UErrorCode status
= U_ZERO_ERROR
;
3166 UnicodeString skeleton
= f
.toSkeleton(status
);
3168 u
"Expect toSkeleton to fail, but passed, producing: " + skeleton
,
3169 U_UNSUPPORTED_ERROR
,
3173 void NumberFormatterApiTest::assertNumberFieldPositions(
3174 const char16_t* message
, const FormattedNumber
& formattedNumber
,
3175 const UFieldPosition
* expectedFieldPositions
, int32_t length
) {
3176 IcuTestErrorCode
status(*this, "assertNumberFieldPositions");
3178 // Check FormattedValue functions
3179 checkFormattedValue(
3181 static_cast<const FormattedValue
&>(formattedNumber
),
3182 formattedNumber
.toString(status
),
3183 UFIELD_CATEGORY_NUMBER
,
3184 expectedFieldPositions
,
3187 // Check FormattedNumber-specific functions
3188 UnicodeString baseMessage
= UnicodeString(message
) + u
": " + formattedNumber
.toString(status
) + u
": ";
3189 FieldPositionIterator fpi
;
3190 formattedNumber
.getAllFieldPositions(fpi
, status
);
3192 FieldPosition actual
;
3193 while (fpi
.next(actual
)) {
3194 UFieldPosition expected
= expectedFieldPositions
[i
++];
3196 baseMessage
+ UnicodeString(u
"Field, case #") + Int64ToUnicodeString(i
),
3200 baseMessage
+ UnicodeString(u
"Iterator, begin, case #") + Int64ToUnicodeString(i
),
3201 expected
.beginIndex
,
3202 actual
.getBeginIndex());
3204 baseMessage
+ UnicodeString(u
"Iterator, end, case #") + Int64ToUnicodeString(i
),
3206 actual
.getEndIndex());
3208 // Check for the first location of the field
3209 FieldPosition
actual2(expected
.field
);
3210 // Fast-forward the field to skip previous occurrences of the field:
3211 actual2
.setBeginIndex(expected
.beginIndex
);
3212 actual2
.setEndIndex(expected
.beginIndex
);
3213 UBool found
= formattedNumber
.nextFieldPosition(actual2
, status
);
3215 baseMessage
+ UnicodeString(u
"Next, found first, case #") + Int64ToUnicodeString(i
),
3219 baseMessage
+ UnicodeString(u
"Next, begin, case #") + Int64ToUnicodeString(i
),
3220 expected
.beginIndex
,
3221 actual2
.getBeginIndex());
3223 baseMessage
+ UnicodeString(u
"Next, end, case #") + Int64ToUnicodeString(i
),
3225 actual2
.getEndIndex());
3227 // The next position should be empty unless the field occurs again
3228 UBool occursAgain
= false;
3229 for (int32_t j
=i
; j
<length
; j
++) {
3230 if (expectedFieldPositions
[j
].field
== expected
.field
) {
3236 found
= formattedNumber
.nextFieldPosition(actual2
, status
);
3238 baseMessage
+ UnicodeString(u
"Next, found second, case #") + Int64ToUnicodeString(i
),
3243 assertEquals(baseMessage
+ u
"Should have seen every field position", length
, i
);
3247 #endif /* #if !UCONFIG_NO_FORMATTING */