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 "unicode/testlog.h"
15 #include "unicode/utypes.h"
16 #include "number_asformat.h"
17 #include "number_types.h"
18 #include "number_utils.h"
19 #include "number_utypes.h"
20 #include "number_microprops.h"
21 #include "numbertest.h"
23 using number::impl::UFormattedNumberData;
25 // Horrible workaround for the lack of a status code in the constructor...
26 // (Also affects numbertest_range.cpp)
27 UErrorCode globalNumberFormatterApiTestStatus = U_ZERO_ERROR;
29 NumberFormatterApiTest::NumberFormatterApiTest()
30 : NumberFormatterApiTest(globalNumberFormatterApiTestStatus) {
33 NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode& status)
34 : USD(u"USD", status),
44 FRENCH_SYMBOLS(Locale::getFrench(), status),
45 SWISS_SYMBOLS(Locale("de-CH"), status),
46 MYANMAR_SYMBOLS(Locale("my"), status) {
48 // Check for error on the first MeasureUnit in case there is no data
49 LocalPointer<MeasureUnit> unit(MeasureUnit::createMeter(status));
50 if (U_FAILURE(status)) {
51 dataerrln("%s %d status = %s", __FILE__, __LINE__, u_errorName(status));
56 METER_PER_SECOND = *LocalPointer<MeasureUnit>(MeasureUnit::createMeterPerSecond(status));
57 DAY = *LocalPointer<MeasureUnit>(MeasureUnit::createDay(status));
58 SQUARE_METER = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMeter(status));
59 FAHRENHEIT = *LocalPointer<MeasureUnit>(MeasureUnit::createFahrenheit(status));
60 SECOND = *LocalPointer<MeasureUnit>(MeasureUnit::createSecond(status));
61 POUND = *LocalPointer<MeasureUnit>(MeasureUnit::createPound(status));
62 POUND_FORCE = *LocalPointer<MeasureUnit>(MeasureUnit::createPoundForce(status));
63 SQUARE_MILE = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMile(status));
64 SQUARE_INCH = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareInch(status));
65 JOULE = *LocalPointer<MeasureUnit>(MeasureUnit::createJoule(status));
66 FURLONG = *LocalPointer<MeasureUnit>(MeasureUnit::createFurlong(status));
67 KELVIN = *LocalPointer<MeasureUnit>(MeasureUnit::createKelvin(status));
69 MATHSANB = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("mathsanb", status));
70 LATN = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("latn", status));
73 void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
75 logln("TestSuite NumberFormatterApiTest: ");
78 TESTCASE_AUTO(notationSimple);
79 TESTCASE_AUTO(notationScientific);
80 TESTCASE_AUTO(notationCompact);
81 TESTCASE_AUTO(unitMeasure);
82 TESTCASE_AUTO(unitCompoundMeasure);
83 TESTCASE_AUTO(unitArbitraryMeasureUnits);
84 TESTCASE_AUTO(unitSkeletons);
85 TESTCASE_AUTO(unitUsage);
86 TESTCASE_AUTO(unitUsageErrorCodes);
87 TESTCASE_AUTO(unitUsageSkeletons);
88 TESTCASE_AUTO(unitCurrency);
89 TESTCASE_AUTO(unitInflections);
90 TESTCASE_AUTO(unitGender);
91 TESTCASE_AUTO(unitPercent);
93 // Slow test: run in exhaustive mode only
94 TESTCASE_AUTO(percentParity);
96 TESTCASE_AUTO(roundingFraction);
97 TESTCASE_AUTO(roundingFigures);
98 TESTCASE_AUTO(roundingFractionFigures);
99 TESTCASE_AUTO(roundingOther);
100 TESTCASE_AUTO(grouping);
101 TESTCASE_AUTO(padding);
102 TESTCASE_AUTO(integerWidth);
103 TESTCASE_AUTO(symbols);
104 // TODO: Add this method if currency symbols override support is added.
105 //TESTCASE_AUTO(symbolsOverride);
107 TESTCASE_AUTO(signNearZero);
108 TESTCASE_AUTO(signCoverage);
109 TESTCASE_AUTO(decimal);
110 TESTCASE_AUTO(scale);
111 TESTCASE_AUTO(locale);
112 TESTCASE_AUTO(skeletonUserGuideExamples);
113 TESTCASE_AUTO(formatTypes);
114 TESTCASE_AUTO(fieldPositionLogic);
115 TESTCASE_AUTO(fieldPositionCoverage);
116 TESTCASE_AUTO(toFormat);
117 TESTCASE_AUTO(errors);
119 // Slow test: run in exhaustive mode only
120 // (somewhat slow to check all permutations of settings)
121 TESTCASE_AUTO(validRanges);
123 TESTCASE_AUTO(copyMove);
124 TESTCASE_AUTO(localPointerCAPI);
125 TESTCASE_AUTO(toObject);
126 TESTCASE_AUTO(toDecimalNumber);
127 TESTCASE_AUTO(microPropsInternals);
131 void NumberFormatterApiTest::notationSimple() {
132 assertFormatDescending(
136 NumberFormatter::with(),
137 Locale::getEnglish(),
148 assertFormatDescendingBig(
152 NumberFormatter::with().notation(Notation::simple()),
153 Locale::getEnglish(),
165 u"Basic with Negative Sign",
168 NumberFormatter::with(),
169 Locale::getEnglish(),
175 void NumberFormatterApiTest::notationScientific() {
176 assertFormatDescending(
180 NumberFormatter::with().notation(Notation::scientific()),
181 Locale::getEnglish(),
192 assertFormatDescending(
196 NumberFormatter::with().notation(Notation::engineering()),
197 Locale::getEnglish(),
208 assertFormatDescending(
209 u"Scientific sign always shown",
210 u"scientific/sign-always",
212 NumberFormatter::with().notation(
213 Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS)),
214 Locale::getEnglish(),
225 assertFormatDescending(
226 u"Scientific min exponent digits",
229 NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)),
230 Locale::getEnglish(),
242 u"Scientific Negative",
245 NumberFormatter::with().notation(Notation::scientific()),
246 Locale::getEnglish(),
251 u"Scientific Infinity",
254 NumberFormatter::with().notation(Notation::scientific()),
255 Locale::getEnglish(),
263 NumberFormatter::with().notation(Notation::scientific()),
264 Locale::getEnglish(),
269 void NumberFormatterApiTest::notationCompact() {
270 assertFormatDescending(
274 NumberFormatter::with().notation(Notation::compactShort()),
275 Locale::getEnglish(),
286 assertFormatDescending(
290 NumberFormatter::with().notation(Notation::compactLong()),
291 Locale::getEnglish(),
302 assertFormatDescending(
303 u"Compact Short Currency",
304 u"compact-short currency/USD",
306 NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
307 Locale::getEnglish(),
318 assertFormatDescending(
319 u"Compact Short with ISO Currency",
320 u"compact-short currency/USD unit-width-iso-code",
321 u"K currency/USD unit-width-iso-code",
322 NumberFormatter::with().notation(Notation::compactShort())
324 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
325 Locale::getEnglish(),
336 assertFormatDescending(
337 u"Compact Short with Long Name Currency",
338 u"compact-short currency/USD unit-width-full-name",
339 u"K currency/USD unit-width-full-name",
340 NumberFormatter::with().notation(Notation::compactShort())
342 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
343 Locale::getEnglish(),
351 u"0.0088 US dollars",
354 // Note: Most locales don't have compact long currency, so this currently falls back to short.
355 // This test case should be fixed when proper compact long currency patterns are added.
356 assertFormatDescending(
357 u"Compact Long Currency",
358 u"compact-long currency/USD",
360 NumberFormatter::with().notation(Notation::compactLong()).unit(USD),
361 Locale::getEnglish(),
362 u"$88K", // should be something like "$88 thousand"
372 // Note: Most locales don't have compact long currency, so this currently falls back to short.
373 // This test case should be fixed when proper compact long currency patterns are added.
374 assertFormatDescending(
375 u"Compact Long with ISO Currency",
376 u"compact-long currency/USD unit-width-iso-code",
377 u"KK currency/USD unit-width-iso-code",
378 NumberFormatter::with().notation(Notation::compactLong())
380 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
381 Locale::getEnglish(),
382 u"USD 88K", // should be something like "USD 88 thousand"
392 // TODO: This behavior could be improved and should be revisited.
393 assertFormatDescending(
394 u"Compact Long with Long Name Currency",
395 u"compact-long currency/USD unit-width-full-name",
396 u"KK currency/USD unit-width-full-name",
397 NumberFormatter::with().notation(Notation::compactLong())
399 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
400 Locale::getEnglish(),
401 u"88 thousand US dollars",
402 u"8.8 thousand US dollars",
408 u"0.0088 US dollars",
412 u"Compact Plural One",
415 NumberFormatter::with().notation(Notation::compactLong()),
416 Locale::createFromName("es"),
421 u"Compact Plural Other",
424 NumberFormatter::with().notation(Notation::compactLong()),
425 Locale::createFromName("es"),
430 u"Compact with Negative Sign",
433 NumberFormatter::with().notation(Notation::compactShort()),
434 Locale::getEnglish(),
442 NumberFormatter::with().notation(Notation::compactShort()),
443 Locale::getEnglish(),
451 NumberFormatter::with().notation(Notation::compactShort()),
452 Locale::getEnglish(),
460 NumberFormatter::with().notation(Notation::compactShort()),
461 Locale::getEnglish(),
469 NumberFormatter::with().notation(Notation::compactShort()),
470 Locale::getEnglish(),
478 NumberFormatter::with().notation(Notation::compactShort()),
479 Locale::getEnglish(),
484 u"Compact in zh-Hant-HK",
487 NumberFormatter::with().notation(Notation::compactShort()),
488 Locale("zh-Hant-HK"),
493 u"Compact in zh-Hant",
496 NumberFormatter::with().notation(Notation::compactShort()),
501 if (!logKnownIssue("21258", "StandardPlural cannot handle keywords 1, 0")) {
503 u"Compact with plural form =1 (ICU-21258)",
506 NumberFormatter::with().notation(Notation::compactLong()),
516 NumberFormatter::with().notation(Notation::compactShort()),
517 Locale::getEnglish(),
525 NumberFormatter::with().notation(Notation::compactShort()),
526 Locale::getEnglish(),
530 // NOTE: There is no API for compact custom data in C++
531 // and thus no "Compact Somali No Figure" test
534 void NumberFormatterApiTest::unitMeasure() {
535 IcuTestErrorCode status(*this, "unitMeasure()");
537 assertFormatDescending(
538 u"Meters Short and unit() method",
539 u"measure-unit/length-meter",
541 NumberFormatter::with().unit(MeasureUnit::getMeter()),
542 Locale::getEnglish(),
553 assertFormatDescending(
554 u"Meters Long and adoptUnit() method",
555 u"measure-unit/length-meter unit-width-full-name",
556 u"unit/meter unit-width-full-name",
557 NumberFormatter::with().adoptUnit(new MeasureUnit(METER))
558 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
559 Locale::getEnglish(),
570 assertFormatDescending(
571 u"Compact Meters Long",
572 u"compact-long measure-unit/length-meter unit-width-full-name",
573 u"KK unit/meter unit-width-full-name",
574 NumberFormatter::with().notation(Notation::compactLong())
576 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
577 Locale::getEnglish(),
578 u"88 thousand meters",
579 u"8.8 thousand meters",
588 assertFormatDescending(
592 NumberFormatter::with().unit(MeasureUnit::forIdentifier("hectometer", status)),
593 Locale::getEnglish(),
604 // TODO: Implement Measure in C++
605 // assertFormatSingleMeasure(
606 // u"Meters with Measure Input",
607 // NumberFormatter::with().unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
608 // Locale::getEnglish(),
609 // new Measure(5.43, new MeasureUnit(METER)),
612 // TODO: Implement Measure in C++
613 // assertFormatSingleMeasure(
614 // u"Measure format method takes precedence over fluent chain",
615 // NumberFormatter::with().unit(METER),
616 // Locale::getEnglish(),
617 // new Measure(5.43, USD),
621 u"Meters with Negative Sign",
622 u"measure-unit/length-meter",
624 NumberFormatter::with().unit(METER),
625 Locale::getEnglish(),
629 // The locale string "सान" appears only in brx.txt:
631 u"Interesting Data Fallback 1",
632 u"measure-unit/duration-day unit-width-full-name",
633 u"unit/day unit-width-full-name",
634 NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
635 Locale::createFromName("brx"),
639 // Requires following the alias from unitsNarrow to unitsShort:
641 u"Interesting Data Fallback 2",
642 u"measure-unit/duration-day unit-width-narrow",
643 u"unit/day unit-width-narrow",
644 NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
645 Locale::createFromName("brx"),
649 // en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
650 // requiring fallback to the root.
652 u"Interesting Data Fallback 3",
653 u"measure-unit/area-square-meter unit-width-narrow",
654 u"unit/square-meter unit-width-narrow",
655 NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
656 Locale::createFromName("en-GB"),
660 // Try accessing a narrow unit directly from root.
662 u"Interesting Data Fallback 4",
663 u"measure-unit/area-square-meter unit-width-narrow",
664 u"unit/square-meter unit-width-narrow",
665 NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
666 Locale::createFromName("root"),
670 // es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
671 // NOTE: This example is in the documentation.
673 u"Difference between Narrow and Short (Narrow Version)",
674 u"measure-unit/temperature-fahrenheit unit-width-narrow",
675 u"unit/fahrenheit unit-width-narrow",
676 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_NARROW),
682 u"Difference between Narrow and Short (Short Version)",
683 u"measure-unit/temperature-fahrenheit unit-width-short",
684 u"unit/fahrenheit unit-width-short",
685 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_SHORT),
691 u"MeasureUnit form without {0} in CLDR pattern",
692 u"measure-unit/temperature-kelvin unit-width-full-name",
693 u"unit/kelvin unit-width-full-name",
694 NumberFormatter::with().unit(KELVIN).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
700 u"MeasureUnit form without {0} in CLDR pattern and wide base form",
701 u"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
702 u"unit/kelvin .00000000000000000000 unit-width-full-name",
703 NumberFormatter::with().precision(Precision::fixedFraction(20))
705 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
711 u"Person unit not in short form",
712 u"measure-unit/duration-year-person unit-width-full-name",
713 u"unit/year-person unit-width-full-name",
714 NumberFormatter::with().unit(MeasureUnit::getYearPerson())
715 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
721 u"Hubble Constant - usually expressed in km/s/Mpc",
722 u"unit/kilometer-per-megaparsec-second",
723 u"unit/kilometer-per-megaparsec-second",
724 NumberFormatter::with().unit(MeasureUnit::forIdentifier("kilometer-per-second-per-megaparsec", status)),
726 74, // Approximate 2019-03-18 measurement
731 u"unit/yard-and-foot-and-inch",
732 u"unit/yard-and-foot-and-inch",
733 NumberFormatter::with()
734 .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status)),
737 "3 yd, 1 ft, 11.4 in");
740 u"Mixed unit, Scientific",
741 u"unit/yard-and-foot-and-inch E0",
742 u"unit/yard-and-foot-and-inch E0",
743 NumberFormatter::with()
744 .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status))
745 .notation(Notation::scientific()),
748 "3 yd, 1 ft, 1.14E1 in");
751 u"Mixed Unit (Narrow Version)",
752 u"unit/metric-ton-and-kilogram-and-gram unit-width-narrow",
753 u"unit/metric-ton-and-kilogram-and-gram unit-width-narrow",
754 NumberFormatter::with()
755 .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
756 .unitWidth(UNUM_UNIT_WIDTH_NARROW),
762 u"Mixed Unit (Short Version)",
763 u"unit/metric-ton-and-kilogram-and-gram unit-width-short",
764 u"unit/metric-ton-and-kilogram-and-gram unit-width-short",
765 NumberFormatter::with()
766 .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
767 .unitWidth(UNUM_UNIT_WIDTH_SHORT),
770 u"4 t, 285 kg, 710 g");
773 u"Mixed Unit (Full Name Version)",
774 u"unit/metric-ton-and-kilogram-and-gram unit-width-full-name",
775 u"unit/metric-ton-and-kilogram-and-gram unit-width-full-name",
776 NumberFormatter::with()
777 .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
778 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
781 u"4 metric tons, 285 kilograms, 710 grams");
783 assertFormatSingle(u"Mixed Unit (Not Sorted) [metric]", //
784 u"unit/gram-and-kilogram unit-width-full-name", //
785 u"unit/gram-and-kilogram unit-width-full-name", //
786 NumberFormatter::with() //
787 .unit(MeasureUnit::forIdentifier("gram-and-kilogram", status)) //
788 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), //
791 u"285.71 grams, 4 kilograms"); //
793 assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial]", //
794 u"unit/inch-and-yard-and-foot unit-width-full-name", //
795 u"unit/inch-and-yard-and-foot unit-width-full-name", //
796 NumberFormatter::with() //
797 .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
798 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), //
801 u"10.28556 inches, 4 yards, 0 feet"); //
803 assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full]", //
804 u"unit/inch-and-yard-and-foot unit-width-full-name", //
805 u"unit/inch-and-yard-and-foot unit-width-full-name", //
806 NumberFormatter::with() //
807 .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
808 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), //
811 u"1.88556 inches, 4 yards, 1 foot"); //
813 assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full integers]", //
814 u"unit/inch-and-yard-and-foot @# unit-width-full-name", //
815 u"unit/inch-and-yard-and-foot @# unit-width-full-name", //
816 NumberFormatter::with() //
817 .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
818 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME) //
819 .precision(Precision::maxSignificantDigits(2)), //
822 u"1 inch, 4 yards, 1 foot"); //
824 assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full] with `And` in the end", //
825 u"unit/inch-and-yard-and-foot unit-width-full-name", //
826 u"unit/inch-and-yard-and-foot unit-width-full-name", //
827 NumberFormatter::with() //
828 .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) //
829 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), //
832 u"1,88556\u00A0pouce, 4\u00A0yards et 1\u00A0pied"); //
834 assertFormatSingle(u"Mixed unit, Scientific [Not in Order]", //
835 u"unit/foot-and-inch-and-yard E0", //
836 u"unit/foot-and-inch-and-yard E0", //
837 NumberFormatter::with() //
838 .unit(MeasureUnit::forIdentifier("foot-and-inch-and-yard", status)) //
839 .notation(Notation::scientific()), //
842 "1 ft, 1.14E1 in, 3 yd"); //
845 u"Testing \"1 foot 12 inches\"",
846 u"unit/foot-and-inch @### unit-width-full-name",
847 u"unit/foot-and-inch @### unit-width-full-name",
848 NumberFormatter::with()
849 .unit(MeasureUnit::forIdentifier("foot-and-inch", status))
850 .precision(Precision::maxSignificantDigits(4))
851 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
854 u"2 feet, 0 inches");
857 u"Negative numbers: temperature",
858 u"measure-unit/temperature-celsius",
860 NumberFormatter::with().unit(MeasureUnit::forIdentifier("celsius", status)),
866 u"Negative numbers: time",
867 u"unit/hour-and-minute-and-second",
868 u"unit/hour-and-minute-and-second",
869 NumberFormatter::with().unit(MeasureUnit::forIdentifier("hour-and-minute-and-second", status)),
872 u"-1 Std., 14 Min. und 24 Sek.");
875 u"Zero out the unit field",
878 NumberFormatter::with().unit(KELVIN).unit(MeasureUnit()),
883 // TODO: desired behaviour for this "pathological" case?
884 // Since this is pointless, we don't test that its behaviour doesn't change.
885 // As of January 2021, the produced result has a missing sign: 23.5 Kelvin
886 // is "23 Kelvin and -272.65 degrees Celsius":
887 // assertFormatSingle(
888 // u"Meaningless: kelvin-and-celcius",
889 // u"unit/kelvin-and-celsius",
890 // u"unit/kelvin-and-celsius",
891 // NumberFormatter::with().unit(MeasureUnit::forIdentifier("kelvin-and-celsius", status)),
894 // u"23 K, 272.65°C");
896 if (uprv_getNaN() != 0.0) {
899 u"measure-unit/electric-ampere",
901 NumberFormatter::with().unit(MeasureUnit::getAmpere()),
908 u"measure-unit/temperature-celsius",
910 NumberFormatter::with().unit(MeasureUnit::forIdentifier("celsius", status)),
917 void NumberFormatterApiTest::unitCompoundMeasure() {
918 IcuTestErrorCode status(*this, "unitCompoundMeasure()");
920 assertFormatDescending(
921 u"Meters Per Second Short (unit that simplifies) and perUnit method",
922 u"measure-unit/length-meter per-measure-unit/duration-second",
923 u"unit/meter-per-second",
924 NumberFormatter::with().unit(METER).perUnit(SECOND),
925 Locale::getEnglish(),
936 assertFormatDescending(
937 u"Meters Per Second Short, built-in m/s",
938 u"measure-unit/speed-meter-per-second",
939 u"unit/meter-per-second",
940 NumberFormatter::with().unit(METER_PER_SECOND),
941 Locale::getEnglish(),
952 assertFormatDescending(
953 u"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method",
954 u"measure-unit/mass-pound per-measure-unit/area-square-mile",
955 u"unit/pound-per-square-mile",
956 NumberFormatter::with().unit(POUND).adoptPerUnit(new MeasureUnit(SQUARE_MILE)),
957 Locale::getEnglish(),
968 assertFormatDescending(
969 u"Joules Per Furlong Short (unit with no simplifications or special patterns)",
970 u"measure-unit/energy-joule per-measure-unit/length-furlong",
971 u"unit/joule-per-furlong",
972 NumberFormatter::with().unit(JOULE).perUnit(FURLONG),
973 Locale::getEnglish(),
984 assertFormatDescending(
985 u"Joules Per Furlong Short with unit identifier via API",
986 u"measure-unit/energy-joule per-measure-unit/length-furlong",
987 u"unit/joule-per-furlong",
988 NumberFormatter::with().unit(MeasureUnit::forIdentifier("joule-per-furlong", status)),
989 Locale::getEnglish(),
1000 assertFormatDescending(
1001 u"Pounds per Square Inch: composed",
1002 u"measure-unit/force-pound-force per-measure-unit/area-square-inch",
1003 u"unit/pound-force-per-square-inch",
1004 NumberFormatter::with().unit(POUND_FORCE).perUnit(SQUARE_INCH),
1005 Locale::getEnglish(),
1016 assertFormatDescending(
1017 u"Pounds per Square Inch: built-in",
1018 u"measure-unit/force-pound-force per-measure-unit/area-square-inch",
1019 u"unit/pound-force-per-square-inch",
1020 NumberFormatter::with().unit(MeasureUnit::getPoundPerSquareInch()),
1021 Locale::getEnglish(),
1033 u"m/s/s simplifies to m/s^2",
1034 u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
1035 u"unit/meter-per-square-second",
1036 NumberFormatter::with().unit(METER_PER_SECOND).perUnit(SECOND),
1042 u"Negative numbers: acceleration",
1043 u"measure-unit/acceleration-meter-per-square-second",
1044 u"unit/meter-per-second-second",
1045 NumberFormatter::with().unit(MeasureUnit::forIdentifier("meter-per-pow2-second", status)),
1048 u"-9,81 m/s\u00B2");
1050 // Testing the rejection of invalid specifications
1052 // If .unit() is not given a built-in type, .perUnit() is not allowed
1053 // (because .unit is now flexible enough to handle compound units,
1054 // .perUnit() is supported for backward compatibility).
1055 LocalizedNumberFormatter nf = NumberFormatter::with()
1056 .unit(MeasureUnit::forIdentifier("furlong-pascal", status))
1059 status.assertSuccess(); // Error is only returned once we try to format.
1060 FormattedNumber num = nf.formatDouble(2.4, status);
1061 if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
1062 errln(UnicodeString("Expected failure for unit/furlong-pascal per-unit/length-meter, got: \"") +
1063 nf.formatDouble(2.4, status).toString(status) + "\".");
1064 status.assertSuccess();
1067 // .perUnit() may only be passed a built-in type, or something that combines
1068 // to a built-in type together with .unit().
1069 MeasureUnit SQUARE_SECOND = MeasureUnit::forIdentifier("square-second", status);
1070 nf = NumberFormatter::with().unit(FURLONG).perUnit(SQUARE_SECOND).locale("en-GB");
1071 status.assertSuccess(); // Error is only returned once we try to format.
1072 num = nf.formatDouble(2.4, status);
1073 if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
1074 errln(UnicodeString("Expected failure, got: \"") +
1075 nf.formatDouble(2.4, status).toString(status) + "\".");
1076 status.assertSuccess();
1078 // As above, "square-second" is not a built-in type, however this time,
1079 // meter-per-square-second is a built-in type.
1081 u"meter per square-second works as a composed unit",
1082 u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
1083 u"unit/meter-per-square-second",
1084 NumberFormatter::with().unit(METER).perUnit(SQUARE_SECOND),
1090 void NumberFormatterApiTest::unitArbitraryMeasureUnits() {
1091 IcuTestErrorCode status(*this, "unitArbitraryMeasureUnits()");
1093 // TODO: fix after data bug is resolved? See CLDR-14510.
1094 // assertFormatSingle(
1095 // u"Binary unit prefix: kibibyte",
1096 // u"unit/kibibyte",
1097 // u"unit/kibibyte",
1098 // NumberFormatter::with().unit(MeasureUnit::forIdentifier("kibibyte", status)),
1104 u"Binary unit prefix: kibibyte full-name",
1105 u"unit/kibibyte unit-width-full-name",
1106 u"unit/kibibyte unit-width-full-name",
1107 NumberFormatter::with()
1108 .unit(MeasureUnit::forIdentifier("kibibyte", status))
1109 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1115 u"Binary unit prefix: kibibyte full-name",
1116 u"unit/kibibyte unit-width-full-name",
1117 u"unit/kibibyte unit-width-full-name",
1118 NumberFormatter::with()
1119 .unit(MeasureUnit::forIdentifier("kibibyte", status))
1120 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1126 u"Binary prefix for non-digital units: kibimeter",
1129 NumberFormatter::with()
1130 .unit(MeasureUnit::forIdentifier("kibimeter", status)),
1136 u"SI prefix falling back to root: microohm",
1139 NumberFormatter::with()
1140 .unit(MeasureUnit::forIdentifier("microohm", status)),
1146 u"de-CH fallback to de: microohm unit-width-full-name",
1147 u"unit/microohm unit-width-full-name",
1148 u"unit/microohm unit-width-full-name",
1149 NumberFormatter::with()
1150 .unit(MeasureUnit::forIdentifier("microohm", status))
1151 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1154 u"2.4\u00A0Mikroohm");
1157 u"No prefixes, 'times' pattern: joule-furlong",
1158 u"unit/joule-furlong",
1159 u"unit/joule-furlong",
1160 NumberFormatter::with()
1161 .unit(MeasureUnit::forIdentifier("joule-furlong", status)),
1167 u"No numeratorUnitString: per-second",
1170 NumberFormatter::with()
1171 .unit(MeasureUnit::forIdentifier("per-second", status)),
1177 u"No numeratorUnitString: per-second unit-width-full-name",
1178 u"unit/per-second unit-width-full-name",
1179 u"unit/per-second unit-width-full-name",
1180 NumberFormatter::with()
1181 .unit(MeasureUnit::forIdentifier("per-second", status))
1182 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1185 u"2.4 pro Sekunde");
1188 u"Prefix in the denominator: nanogram-per-picobarrel",
1189 u"unit/nanogram-per-picobarrel",
1190 u"unit/nanogram-per-picobarrel",
1191 NumberFormatter::with()
1192 .unit(MeasureUnit::forIdentifier("nanogram-per-picobarrel", status)),
1198 u"Prefix in the denominator: nanogram-per-picobarrel unit-width-full-name",
1199 u"unit/nanogram-per-picobarrel unit-width-full-name",
1200 u"unit/nanogram-per-picobarrel unit-width-full-name",
1201 NumberFormatter::with()
1202 .unit(MeasureUnit::forIdentifier("nanogram-per-picobarrel", status))
1203 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1206 u"2,4 nanograms per picobarrel");
1208 // Valid MeasureUnit, but unformattable, because we only have patterns for
1209 // pow2 and pow3 at this time:
1210 LocalizedNumberFormatter lnf = NumberFormatter::with()
1211 .unit(MeasureUnit::forIdentifier("pow4-mile", status))
1212 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
1214 lnf.formatInt(1, status);
1215 status.expectErrorAndReset(U_RESOURCE_TYPE_MISMATCH);
1218 u"kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1219 u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1220 u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1221 NumberFormatter::with()
1222 .unit(MeasureUnit::forIdentifier("kibijoule-foot-per-cubic-gigafurlong-square-second",
1224 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1227 u"2,4 kibijoule-feet per cubic gigafurlong-square second");
1230 u"kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1231 u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1232 u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name",
1233 NumberFormatter::with()
1234 .unit(MeasureUnit::forIdentifier("kibijoule-foot-per-cubic-gigafurlong-square-second",
1236 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1239 u"2.4\u00A0Kibijoule⋅Fuss pro Kubikgigafurlong⋅Quadratsekunde");
1241 // TODO(ICU-21504): We want to be able to format this, but "100-kilometer"
1242 // is not yet supported when it's not part of liter-per-100-kilometer:
1243 lnf = NumberFormatter::with()
1244 .unit(MeasureUnit::forIdentifier("kilowatt-hour-per-100-kilometer", status))
1245 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
1247 lnf.formatInt(1, status);
1248 status.expectErrorAndReset(U_UNSUPPORTED_ERROR);
1251 // TODO: merge these tests into numbertest_skeletons.cpp instead of here:
1252 void NumberFormatterApiTest::unitSkeletons() {
1253 const struct TestCase {
1255 const char16_t *inputSkeleton;
1256 const char16_t *normalizedSkeleton;
1258 {"old-form built-in compound unit", //
1259 u"measure-unit/speed-meter-per-second", //
1260 u"unit/meter-per-second"},
1262 {"old-form compound construction, converts to built-in", //
1263 u"measure-unit/length-meter per-measure-unit/duration-second", //
1264 u"unit/meter-per-second"},
1266 {"old-form compound construction which does not simplify to a built-in", //
1267 u"measure-unit/energy-joule per-measure-unit/length-meter", //
1268 u"unit/joule-per-meter"},
1270 {"old-form compound-compound ugliness resolves neatly", //
1271 u"measure-unit/speed-meter-per-second per-measure-unit/duration-second", //
1272 u"unit/meter-per-square-second"},
1274 {"short-form built-in units stick with the built-in", //
1275 u"unit/meter-per-second", //
1276 u"unit/meter-per-second"},
1278 {"short-form compound units stay as is", //
1279 u"unit/square-meter-per-square-meter", //
1280 u"unit/square-meter-per-square-meter"},
1282 {"short-form compound units stay as is", //
1283 u"unit/joule-per-furlong", //
1284 u"unit/joule-per-furlong"},
1286 {"short-form that doesn't consist of built-in units", //
1287 u"unit/hectometer-per-second", //
1288 u"unit/hectometer-per-second"},
1290 {"short-form that doesn't consist of built-in units", //
1291 u"unit/meter-per-hectosecond", //
1292 u"unit/meter-per-hectosecond"},
1294 {"percent compound skeletons handled correctly", //
1295 u"unit/percent-per-meter", //
1296 u"unit/percent-per-meter"},
1298 {"permille compound skeletons handled correctly", //
1299 u"measure-unit/concentr-permille per-measure-unit/length-meter", //
1300 u"unit/permille-per-meter"},
1302 {"percent simple unit is not actually considered a unit", //
1306 {"permille simple unit is not actually considered a unit", //
1307 u"measure-unit/concentr-permille", //
1310 {"Round-trip example from icu-units#35", //
1311 u"unit/kibijoule-per-furlong", //
1312 u"unit/kibijoule-per-furlong"},
1314 for (auto &cas : cases) {
1315 IcuTestErrorCode status(*this, cas.msg);
1316 auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status);
1317 if (status.errIfFailureAndReset("NumberFormatter::forSkeleton failed")) {
1321 UnicodeString(TRUE, cas.inputSkeleton, -1) + u" normalization", //
1322 cas.normalizedSkeleton, //
1323 nf.toSkeleton(status));
1324 status.errIfFailureAndReset("NumberFormatter::toSkeleton failed");
1327 const struct FailCase {
1329 const char16_t *inputSkeleton;
1330 UErrorCode expectedForSkelStatus;
1331 UErrorCode expectedToSkelStatus;
1333 {"Parsing measure-unit/* results in failure if not built-in unit",
1334 u"measure-unit/hectometer", //
1335 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1338 {"Parsing per-measure-unit/* results in failure if not built-in unit",
1339 u"measure-unit/meter per-measure-unit/hectosecond", //
1340 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1343 {"\"currency/EUR measure-unit/length-meter\" fails, conflicting skeleton.",
1344 u"currency/EUR measure-unit/length-meter", //
1345 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1348 {"\"measure-unit/length-meter currency/EUR\" fails, conflicting skeleton.",
1349 u"measure-unit/length-meter currency/EUR", //
1350 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1353 {"\"currency/EUR per-measure-unit/meter\" fails, conflicting skeleton.",
1354 u"currency/EUR per-measure-unit/length-meter", //
1355 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1358 for (auto &cas : failCases) {
1359 IcuTestErrorCode status(*this, cas.msg);
1360 auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status);
1361 if (status.expectErrorAndReset(cas.expectedForSkelStatus, cas.msg)) {
1364 nf.toSkeleton(status);
1365 status.expectErrorAndReset(cas.expectedToSkelStatus, cas.msg);
1368 IcuTestErrorCode status(*this, "unitSkeletons");
1370 ".unit(METER_PER_SECOND) normalization", //
1371 u"unit/meter-per-second", //
1372 NumberFormatter::with().unit(METER_PER_SECOND).toSkeleton(status));
1374 ".unit(METER).perUnit(SECOND) normalization", //
1375 u"unit/meter-per-second",
1376 NumberFormatter::with().unit(METER).perUnit(SECOND).toSkeleton(status));
1378 ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", //
1380 NumberFormatter::with()
1381 .unit(MeasureUnit::forIdentifier("hectometer", status))
1382 .toSkeleton(status));
1384 ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", //
1385 u"unit/meter-per-hectosecond",
1386 NumberFormatter::with()
1388 .perUnit(MeasureUnit::forIdentifier("hectosecond", status))
1389 .toSkeleton(status));
1391 status.assertSuccess();
1393 ".unit(CURRENCY) produces a currency/CURRENCY skeleton", //
1395 NumberFormatter::with().unit(GBP).toSkeleton(status));
1396 status.assertSuccess();
1397 // .unit(CURRENCY).perUnit(ANYTHING) is not supported.
1398 NumberFormatter::with().unit(GBP).perUnit(METER).toSkeleton(status);
1399 status.expectErrorAndReset(U_UNSUPPORTED_ERROR);
1402 void NumberFormatterApiTest::unitUsage() {
1403 IcuTestErrorCode status(*this, "unitUsage()");
1404 UnlocalizedNumberFormatter unloc_formatter;
1405 LocalizedNumberFormatter formatter;
1406 FormattedNumber formattedNum;
1407 UnicodeString uTestCase;
1409 status.assertSuccess();
1411 NumberFormatter::with().usage("road").locale(Locale::getEnglish()).formatInt(1, status);
1412 status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1414 unloc_formatter = NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter());
1416 uTestCase = u"unitUsage() en-ZA road";
1417 formatter = unloc_formatter.locale("en-ZA");
1418 formattedNum = formatter.formatDouble(321, status);
1419 status.errIfFailureAndReset("unitUsage() en-ZA road formatDouble");
1421 uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1422 MeasureUnit::getMeter() == formattedNum.getOutputUnit(status));
1423 assertEquals(uTestCase, "300 m", formattedNum.toString(status));
1425 static const UFieldPosition expectedFieldPositions[] = {
1426 {UNUM_INTEGER_FIELD, 0, 3},
1427 {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
1428 assertNumberFieldPositions(
1429 (uTestCase + u" field positions").getTerminatedBuffer(),
1431 expectedFieldPositions,
1432 UPRV_LENGTHOF(expectedFieldPositions));
1434 assertFormatDescendingBig(
1435 uTestCase.getTerminatedBuffer(),
1436 u"measure-unit/length-meter usage/road",
1437 u"unit/meter usage/road",
1442 u"876 km", // 6.5 rounds down, 7.5 rounds up.
1450 uTestCase = u"unitUsage() en-GB road";
1451 formatter = unloc_formatter.locale("en-GB");
1452 formattedNum = formatter.formatDouble(321, status);
1453 status.errIfFailureAndReset("unitUsage() en-GB road, formatDouble(...)");
1455 uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1456 MeasureUnit::getYard() == formattedNum.getOutputUnit(status));
1457 status.errIfFailureAndReset("unitUsage() en-GB road, getOutputUnit(...)");
1458 assertEquals(uTestCase, "350 yd", formattedNum.toString(status));
1459 status.errIfFailureAndReset("unitUsage() en-GB road, toString(...)");
1461 static const UFieldPosition expectedFieldPositions[] = {
1462 {UNUM_INTEGER_FIELD, 0, 3},
1463 {UNUM_MEASURE_UNIT_FIELD, 4, 6}};
1464 assertNumberFieldPositions(
1465 (uTestCase + u" field positions").getTerminatedBuffer(),
1467 expectedFieldPositions,
1468 UPRV_LENGTHOF(expectedFieldPositions));
1470 assertFormatDescendingBig(
1471 uTestCase.getTerminatedBuffer(),
1472 u"measure-unit/length-meter usage/road",
1473 u"unit/meter usage/road",
1486 uTestCase = u"unitUsage() en-US road";
1487 formatter = unloc_formatter.locale("en-US");
1488 formattedNum = formatter.formatDouble(321, status);
1489 status.errIfFailureAndReset("unitUsage() en-US road, formatDouble(...)");
1491 uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1492 MeasureUnit::getFoot() == formattedNum.getOutputUnit(status));
1493 status.errIfFailureAndReset("unitUsage() en-US road, getOutputUnit(...)");
1494 assertEquals(uTestCase, "1,050 ft", formattedNum.toString(status));
1495 status.errIfFailureAndReset("unitUsage() en-US road, toString(...)");
1497 static const UFieldPosition expectedFieldPositions[] = {
1498 {UNUM_GROUPING_SEPARATOR_FIELD, 1, 2},
1499 {UNUM_INTEGER_FIELD, 0, 5},
1500 {UNUM_MEASURE_UNIT_FIELD, 6, 8}};
1501 assertNumberFieldPositions(
1502 (uTestCase + u" field positions").getTerminatedBuffer(),
1504 expectedFieldPositions,
1505 UPRV_LENGTHOF(expectedFieldPositions));
1507 assertFormatDescendingBig(
1508 uTestCase.getTerminatedBuffer(),
1509 u"measure-unit/length-meter usage/road",
1510 u"unit/meter usage/road",
1523 unloc_formatter = NumberFormatter::with().usage("person").unit(MeasureUnit::getKilogram());
1524 uTestCase = u"unitUsage() en-GB person";
1525 formatter = unloc_formatter.locale("en-GB");
1526 formattedNum = formatter.formatDouble(80, status);
1527 status.errIfFailureAndReset("unitUsage() en-GB person formatDouble");
1529 uTestCase + ", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1530 MeasureUnit::forIdentifier("stone-and-pound", status) == formattedNum.getOutputUnit(status));
1531 status.errIfFailureAndReset("unitUsage() en-GB person - formattedNum.getOutputUnit(status)");
1532 assertEquals(uTestCase, "12 st, 8.4 lb", formattedNum.toString(status));
1533 status.errIfFailureAndReset("unitUsage() en-GB person, toString(...)");
1535 static const UFieldPosition expectedFieldPositions[] = {
1536 // // Desired output: TODO(icu-units#67)
1537 // {UNUM_INTEGER_FIELD, 0, 2},
1538 // {UNUM_MEASURE_UNIT_FIELD, 3, 5},
1539 // {ULISTFMT_LITERAL_FIELD, 5, 6},
1540 // {UNUM_INTEGER_FIELD, 7, 8},
1541 // {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
1542 // {UNUM_FRACTION_FIELD, 9, 10},
1543 // {UNUM_MEASURE_UNIT_FIELD, 11, 13}};
1545 // Current output: rather no fields than wrong fields
1546 {UNUM_INTEGER_FIELD, 7, 8},
1547 {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
1548 {UNUM_FRACTION_FIELD, 9, 10},
1550 assertNumberFieldPositions(
1551 (uTestCase + u" field positions").getTerminatedBuffer(),
1553 expectedFieldPositions,
1554 UPRV_LENGTHOF(expectedFieldPositions));
1556 assertFormatDescending(
1557 uTestCase.getTerminatedBuffer(),
1558 u"measure-unit/mass-kilogram usage/person",
1559 u"unit/kilogram usage/person",
1562 u"13,802 st, 7.2 lb",
1563 u"1,380 st, 3.5 lb",
1572 assertFormatDescending(
1573 uTestCase.getTerminatedBuffer(),
1574 u"usage/person unit-width-narrow measure-unit/mass-kilogram",
1575 u"usage/person unit-width-narrow unit/kilogram",
1576 unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_NARROW),
1588 assertFormatDescending(
1589 uTestCase.getTerminatedBuffer(),
1590 u"usage/person unit-width-short measure-unit/mass-kilogram",
1591 u"usage/person unit-width-short unit/kilogram",
1592 unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_SHORT),
1594 u"13,802 st, 7.2 lb",
1595 u"1,380 st, 3.5 lb",
1604 assertFormatDescending(
1605 uTestCase.getTerminatedBuffer(),
1606 u"usage/person unit-width-full-name measure-unit/mass-kilogram",
1607 u"usage/person unit-width-full-name unit/kilogram",
1608 unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1610 u"13,802 stone, 7.2 pounds",
1611 u"1,380 stone, 3.5 pounds",
1612 u"138 stone, 0.35 pounds",
1613 u"13 stone, 11 pounds",
1614 u"1 stone, 5.3 pounds",
1615 u"1 pound, 15 ounces",
1616 u"0 pounds, 3.1 ounces",
1617 u"0 pounds, 0.31 ounces",
1618 u"0 pounds, 0 ounces");
1620 assertFormatDescendingBig(
1621 u"Scientific notation with Usage: possible when using a reasonable Precision",
1622 u"scientific @### usage/default measure-unit/area-square-meter unit-width-full-name",
1623 u"scientific @### usage/default unit/square-meter unit-width-full-name",
1624 NumberFormatter::with()
1627 .notation(Notation::scientific())
1628 .precision(Precision::minMaxSignificantDigits(1, 4))
1629 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1631 u"8,765E1 square kilometres",
1632 u"8,765E0 square kilometres",
1633 u"8,765E1 hectares",
1634 u"8,765E0 hectares",
1635 u"8,765E3 square metres",
1636 u"8,765E2 square metres",
1637 u"8,765E1 square metres",
1638 u"8,765E0 square metres",
1639 u"0E0 square centimetres");
1642 u"Negative Infinity with Unit Preferences",
1643 u"measure-unit/area-acre usage/default",
1644 u"unit/acre usage/default",
1645 NumberFormatter::with().unit(MeasureUnit::getAcre()).usage("default"),
1646 Locale::getEnglish(),
1647 -uprv_getInfinity(),
1650 // // TODO(icu-units#131): do we care about NaN?
1651 // // TODO: on some platforms with MSVC, "-NaN sec" is returned.
1652 // assertFormatSingle(
1653 // u"NaN with Unit Preferences",
1654 // u"measure-unit/area-acre usage/default",
1655 // u"unit/acre usage/default",
1656 // NumberFormatter::with().unit(MeasureUnit::getAcre()).usage("default"),
1657 // Locale::getEnglish(),
1662 u"Negative numbers: minute-and-second",
1663 u"measure-unit/duration-second usage/media",
1664 u"unit/second usage/media",
1665 NumberFormatter::with().unit(SECOND).usage("media"),
1671 u"Negative numbers: media seconds",
1672 u"measure-unit/duration-second usage/media",
1673 u"unit/second usage/media",
1674 NumberFormatter::with().unit(SECOND).usage("media"),
1679 // // TODO: on some platforms with MSVC, "-NaN sec" is returned.
1680 // assertFormatSingle(
1681 // u"NaN minute-and-second",
1682 // u"measure-unit/duration-second usage/media",
1683 // u"unit/second usage/media",
1684 // NumberFormatter::with().unit(SECOND).usage("media"),
1690 u"NaN meter-and-centimeter",
1691 u"measure-unit/length-meter usage/person-height",
1692 u"unit/meter usage/person-height",
1693 NumberFormatter::with().unit(METER).usage("person-height"),
1699 u"Rounding Mode propagates: rounding down",
1700 u"usage/road measure-unit/length-centimeter rounding-mode-floor",
1701 u"usage/road unit/centimeter rounding-mode-floor",
1702 NumberFormatter::with()
1703 .unit(MeasureUnit::forIdentifier("centimeter", status))
1705 .roundingMode(UNUM_ROUND_FLOOR),
1711 u"Rounding Mode propagates: rounding up",
1712 u"usage/road measure-unit/length-centimeter rounding-mode-ceiling",
1713 u"usage/road unit/centimeter rounding-mode-ceiling",
1714 NumberFormatter::with()
1715 .unit(MeasureUnit::forIdentifier("centimeter", status))
1717 .roundingMode(UNUM_ROUND_CEILING),
1722 // Test calling `.usage("")` should unset the existing usage.
1723 // First: without usage
1724 assertFormatSingle(u"Rounding Mode propagates: rounding up",
1725 u"measure-unit/length-centimeter rounding-mode-ceiling",
1726 u"unit/centimeter rounding-mode-ceiling",
1727 NumberFormatter::with()
1728 .unit(MeasureUnit::forIdentifier("centimeter", status))
1729 .roundingMode(UNUM_ROUND_CEILING),
1734 // Second: with "road" usage
1735 assertFormatSingle(u"Rounding Mode propagates: rounding up",
1736 u"usage/road measure-unit/length-centimeter rounding-mode-ceiling",
1737 u"usage/road unit/centimeter rounding-mode-ceiling",
1738 NumberFormatter::with()
1739 .unit(MeasureUnit::forIdentifier("centimeter", status))
1741 .roundingMode(UNUM_ROUND_CEILING),
1746 // Third: with "road" usage, then the usage unsetted by calling .usage("")
1747 assertFormatSingle(u"Rounding Mode propagates: rounding up",
1748 u"measure-unit/length-centimeter rounding-mode-ceiling",
1749 u"unit/centimeter rounding-mode-ceiling",
1750 NumberFormatter::with()
1751 .unit(MeasureUnit::forIdentifier("centimeter", status))
1753 .roundingMode(UNUM_ROUND_CEILING)
1754 .usage(""), // unset
1759 // TODO(icu-units#38): improve unit testing coverage. E.g. add vehicle-fuel
1760 // triggering inversion conversion code. Test with 0 too, to see
1761 // divide-by-zero behaviour.
1764 void NumberFormatterApiTest::unitUsageErrorCodes() {
1765 IcuTestErrorCode status(*this, "unitUsageErrorCodes()");
1766 UnlocalizedNumberFormatter unloc_formatter;
1768 unloc_formatter = NumberFormatter::forSkeleton(u"unit/foobar", status);
1769 // This gives an error, because foobar is an invalid unit:
1770 status.expectErrorAndReset(U_NUMBER_SKELETON_SYNTAX_ERROR);
1772 unloc_formatter = NumberFormatter::forSkeleton(u"usage/foobar", status);
1773 // This does not give an error, because usage is not looked up yet.
1774 status.errIfFailureAndReset("Expected behaviour: no immediate error for invalid usage");
1775 unloc_formatter.locale("en-GB").formatInt(1, status);
1776 // Lacking a unit results in a failure. The skeleton is "incomplete", but we
1777 // support adding the unit via the fluent API, so it is not an error until
1778 // we build the formatting pipeline itself.
1779 status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1780 // Adding the unit as part of the fluent chain leads to success.
1781 unloc_formatter.unit(MeasureUnit::getMeter()).locale("en-GB").formatInt(1, status);
1782 status.assertSuccess();
1784 // Setting unit to the "base dimensionless unit" is like clearing unit.
1785 unloc_formatter = NumberFormatter::with().unit(MeasureUnit()).usage("default");
1786 // This does not give an error, because usage-vs-unit isn't resolved yet.
1787 status.errIfFailureAndReset("Expected behaviour: no immediate error for invalid unit");
1788 unloc_formatter.locale("en-GB").formatInt(1, status);
1789 status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1792 // Tests for the "skeletons" field in unitPreferenceData, as well as precision
1793 // and notation overrides.
1794 void NumberFormatterApiTest::unitUsageSkeletons() {
1795 IcuTestErrorCode status(*this, "unitUsageSkeletons()");
1798 u"Default >300m road preference skeletons round to 50m",
1799 u"usage/road measure-unit/length-meter",
1800 u"usage/road unit/meter",
1801 NumberFormatter::with().unit(METER).usage("road"),
1807 u"Precision can be overridden: override takes precedence",
1808 u"usage/road measure-unit/length-meter @#",
1809 u"usage/road unit/meter @#",
1810 NumberFormatter::with()
1813 .precision(Precision::maxSignificantDigits(2)),
1819 u"Compact notation with Usage: bizarre, but possible (short)",
1820 u"compact-short usage/road measure-unit/length-meter",
1821 u"compact-short usage/road unit/meter",
1822 NumberFormatter::with()
1825 .notation(Notation::compactShort()),
1831 u"Compact notation with Usage: bizarre, but possible (short, precision override)",
1832 u"compact-short usage/road measure-unit/length-meter @#",
1833 u"compact-short usage/road unit/meter @#",
1834 NumberFormatter::with()
1837 .notation(Notation::compactShort())
1838 .precision(Precision::maxSignificantDigits(2)),
1844 u"Compact notation with Usage: unusual but possible (long)",
1845 u"compact-long usage/road measure-unit/length-meter @#",
1846 u"compact-long usage/road unit/meter @#",
1847 NumberFormatter::with()
1850 .notation(Notation::compactLong())
1851 .precision(Precision::maxSignificantDigits(2)),
1854 u"990 thousand km");
1857 u"Compact notation with Usage: unusual but possible (long, precision override)",
1858 u"compact-long usage/road measure-unit/length-meter @#",
1859 u"compact-long usage/road unit/meter @#",
1860 NumberFormatter::with()
1863 .notation(Notation::compactLong())
1864 .precision(Precision::maxSignificantDigits(2)),
1867 u"990 thousand km");
1870 u"Scientific notation, not recommended, requires precision override for road",
1871 u"scientific usage/road measure-unit/length-meter",
1872 u"scientific usage/road unit/meter",
1873 NumberFormatter::with().unit(METER).usage("road").notation(Notation::scientific()),
1876 // Rounding to the nearest "50" is not exponent-adjusted in scientific notation:
1880 u"Scientific notation with Usage: possible when using a reasonable Precision",
1881 u"scientific usage/road measure-unit/length-meter @###",
1882 u"scientific usage/road unit/meter @###",
1883 NumberFormatter::with()
1886 .notation(Notation::scientific())
1887 .precision(Precision::maxSignificantDigits(4)),
1889 321.45, // 0.45 rounds down, 0.55 rounds up.
1893 u"Scientific notation with Usage: possible when using a reasonable Precision",
1894 u"scientific usage/default measure-unit/length-astronomical-unit unit-width-full-name",
1895 u"scientific usage/default unit/astronomical-unit unit-width-full-name",
1896 NumberFormatter::with()
1897 .unit(MeasureUnit::forIdentifier("astronomical-unit", status))
1899 .notation(Notation::scientific())
1900 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1903 u"1,5E28 kilometres");
1905 status.assertSuccess();
1908 void NumberFormatterApiTest::unitCurrency() {
1909 assertFormatDescending(
1913 NumberFormatter::with().unit(GBP),
1914 Locale::getEnglish(),
1925 assertFormatDescending(
1927 u"currency/GBP unit-width-iso-code",
1928 u"currency/GBP unit-width-iso-code",
1929 NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
1930 Locale::getEnglish(),
1941 assertFormatDescending(
1942 u"Currency Long Name",
1943 u"currency/GBP unit-width-full-name",
1944 u"currency/GBP unit-width-full-name",
1945 NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1946 Locale::getEnglish(),
1947 u"87,650.00 British pounds",
1948 u"8,765.00 British pounds",
1949 u"876.50 British pounds",
1950 u"87.65 British pounds",
1951 u"8.76 British pounds",
1952 u"0.88 British pounds",
1953 u"0.09 British pounds",
1954 u"0.01 British pounds",
1955 u"0.00 British pounds");
1957 assertFormatDescending(
1959 u"currency/GBP unit-width-hidden",
1960 u"currency/GBP unit-width-hidden",
1961 NumberFormatter::with().unit(GBP).unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
1962 Locale::getEnglish(),
1973 // TODO: Implement Measure in C++
1974 // assertFormatSingleMeasure(
1975 // u"Currency with CurrencyAmount Input",
1976 // NumberFormatter::with(),
1977 // Locale::getEnglish(),
1978 // new CurrencyAmount(5.43, GBP),
1981 // TODO: Enable this test when DecimalFormat wrapper is done.
1982 // assertFormatSingle(
1983 // u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat(
1984 // PatternStringParser.parseToProperties("0 ¤¤¤"),
1985 // DecimalFormatSymbols.getInstance(Locale::getEnglish()),
1986 // null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds");
1989 u"Currency with Negative Sign",
1992 NumberFormatter::with().unit(GBP),
1993 Locale::getEnglish(),
1997 // The full currency symbol is not shown in NARROW format.
1998 // NOTE: This example is in the documentation.
2000 u"Currency Difference between Narrow and Short (Narrow Version)",
2001 u"currency/USD unit-width-narrow",
2002 u"currency/USD unit-width-narrow",
2003 NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_NARROW),
2009 u"Currency Difference between Narrow and Short (Short Version)",
2010 u"currency/USD unit-width-short",
2011 u"currency/USD unit-width-short",
2012 NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2018 u"Currency Difference between Formal and Short (Formal Version)",
2019 u"currency/TWD unit-width-formal",
2020 u"currency/TWD unit-width-formal",
2021 NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_FORMAL),
2027 u"Currency Difference between Formal and Short (Short Version)",
2028 u"currency/TWD unit-width-short",
2029 u"currency/TWD unit-width-short",
2030 NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2036 u"Currency Difference between Variant and Short (Formal Version)",
2037 u"currency/TRY unit-width-variant",
2038 u"currency/TRY unit-width-variant",
2039 NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_VARIANT),
2045 u"Currency Difference between Variant and Short (Short Version)",
2046 u"currency/TRY unit-width-short",
2047 u"currency/TRY unit-width-short",
2048 NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2054 u"Currency-dependent format (Control)",
2055 u"currency/USD unit-width-short",
2056 u"currency/USD unit-width-short",
2057 NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2063 u"Currency-dependent format (Test)",
2064 u"currency/ESP unit-width-short",
2065 u"currency/ESP unit-width-short",
2066 NumberFormatter::with().unit(ESP).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2072 u"Currency-dependent symbols (Control)",
2073 u"currency/USD unit-width-short",
2074 u"currency/USD unit-width-short",
2075 NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2080 // NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero-
2081 // width space), and they set the decimal separator to the $ symbol.
2083 u"Currency-dependent symbols (Test Short)",
2084 u"currency/PTE unit-width-short",
2085 u"currency/PTE unit-width-short",
2086 NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_SHORT),
2089 u"444,444$55 \u200B");
2092 u"Currency-dependent symbols (Test Narrow)",
2093 u"currency/PTE unit-width-narrow",
2094 u"currency/PTE unit-width-narrow",
2095 NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_NARROW),
2098 u"444,444$55 \u200B");
2101 u"Currency-dependent symbols (Test ISO Code)",
2102 u"currency/PTE unit-width-iso-code",
2103 u"currency/PTE unit-width-iso-code",
2104 NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
2110 u"Plural form depending on visible digits (ICU-20499)",
2111 u"currency/RON unit-width-full-name",
2112 u"currency/RON unit-width-full-name",
2113 NumberFormatter::with().unit(RON).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2116 u"24,00 lei românești");
2119 u"Currency spacing in suffix (ICU-20954)",
2122 NumberFormatter::with().unit(CNY),
2128 void NumberFormatterApiTest::runUnitInflectionsTestCases(UnlocalizedNumberFormatter unf,
2129 UnicodeString skeleton,
2130 const UnitInflectionTestCase *cases,
2132 IcuTestErrorCode &status) {
2133 for (int32_t i = 0; i < numCases; i++) {
2134 UnitInflectionTestCase t = cases[i];
2135 status.assertSuccess();
2136 MeasureUnit mu = MeasureUnit::forIdentifier(t.unitIdentifier, status);
2137 if (status.errIfFailureAndReset("MeasureUnit::forIdentifier(\"%s\", ...) failed",
2138 t.unitIdentifier)) {
2141 UnicodeString skelString = UnicodeString("unit/") + t.unitIdentifier + u" " + skeleton;
2143 if (t.unitDisplayCase == nullptr || t.unitDisplayCase[0] == 0) {
2144 unf = unf.unit(mu).unitDisplayCase("");
2145 skel = skelString.getTerminatedBuffer();
2147 unf = unf.unit(mu).unitDisplayCase(t.unitDisplayCase);
2148 // No skeleton support for unitDisplayCase yet.
2151 assertFormatSingle((UnicodeString("Unit: \"") + t.unitIdentifier + ("\", \"") + skeleton +
2152 u"\", locale=\"" + t.locale + u"\", case=\"" +
2153 (t.unitDisplayCase ? t.unitDisplayCase : "") + u"\", value=" + t.value)
2154 .getTerminatedBuffer(),
2155 skel, skel, unf, Locale(t.locale), t.value, t.expected);
2156 status.assertSuccess();
2160 void NumberFormatterApiTest::unitInflections() {
2161 IcuTestErrorCode status(*this, "unitInflections");
2163 UnlocalizedNumberFormatter unf;
2164 const UChar *skeleton;
2166 // Simple inflected form test - test case based on the example in CLDR's
2167 // grammaticalFeatures.xml
2168 unf = NumberFormatter::with().unitWidth(UNUM_UNIT_WIDTH_FULL_NAME);
2169 skeleton = u"unit-width-full-name";
2170 const UnitInflectionTestCase percentCases[] = {
2171 {"percent", "ru", nullptr, 10, u"10 процентов"}, // many
2172 {"percent", "ru", "genitive", 10, u"10 процентов"}, // many
2173 {"percent", "ru", nullptr, 33, u"33 процента"}, // few
2174 {"percent", "ru", "genitive", 33, u"33 процентов"}, // few
2175 {"percent", "ru", nullptr, 1, u"1 процент"}, // one
2176 {"percent", "ru", "genitive", 1, u"1 процента"}, // one
2178 runUnitInflectionsTestCases(unf, skeleton, percentCases, UPRV_LENGTHOF(percentCases), status);
2181 // General testing of inflection rules
2182 unf = NumberFormatter::with().unitWidth(UNUM_UNIT_WIDTH_FULL_NAME);
2183 skeleton = u"unit-width-full-name";
2184 const UnitInflectionTestCase testCases[] = {
2185 // Check up on the basic values that the compound patterns below are
2187 {"meter", "de", nullptr, 1, u"1 Meter"},
2188 {"meter", "de", "genitive", 1, u"1 Meters"},
2189 {"meter", "de", nullptr, 2, u"2 Meter"},
2190 {"meter", "de", "dative", 2, u"2 Metern"},
2191 {"mile", "de", nullptr, 1, u"1 Meile"},
2192 {"mile", "de", nullptr, 2, u"2 Meilen"},
2193 {"day", "de", nullptr, 1, u"1 Tag"},
2194 {"day", "de", "genitive", 1, u"1 Tages"},
2195 {"day", "de", nullptr, 2, u"2 Tage"},
2196 {"day", "de", "dative", 2, u"2 Tagen"},
2197 {"decade", "de", nullptr, 1, u"1\u00A0Jahrzehnt"},
2198 {"decade", "de", nullptr, 2, u"2\u00A0Jahrzehnte"},
2200 // Testing de "per" rules:
2201 // <deriveComponent feature="case" structure="per" value0="compound" value1="accusative"/>
2202 // <deriveComponent feature="plural" structure="per" value0="compound" value1="one"/>
2203 // per-patterns use accusative, but since the accusative form
2204 // matches the nominative form, we're not effectively testing value1
2205 // in the "case & per" rule above.
2207 // We have a perUnitPattern for "day" in de, so "per" rules are not
2208 // applied for these:
2209 {"meter-per-day", "de", nullptr, 1, u"1 Meter pro Tag"},
2210 {"meter-per-day", "de", "genitive", 1, u"1 Meters pro Tag"},
2211 {"meter-per-day", "de", nullptr, 2, u"2 Meter pro Tag"},
2212 {"meter-per-day", "de", "dative", 2, u"2 Metern pro Tag"},
2214 // testing code path that falls back to "root" grammaticalFeatures
2215 // but does not inflect:
2216 {"meter-per-day", "af", nullptr, 1, u"1 meter per dag"},
2217 {"meter-per-day", "af", "dative", 1, u"1 meter per dag"},
2219 // Decade does not have a perUnitPattern at this time (CLDR 39 / ICU
2220 // 69), so we can use it to test for selection of correct plural form.
2221 // - Note: fragile test cases, these cases will break when
2222 // whitespace is more consistently applied.
2223 {"parsec-per-decade", "de", nullptr, 1, u"1\u00A0Parsec pro Jahrzehnt"},
2224 {"parsec-per-decade", "de", "genitive", 1, u"1 Parsec pro Jahrzehnt"},
2225 {"parsec-per-decade", "de", nullptr, 2, u"2\u00A0Parsec pro Jahrzehnt"},
2226 {"parsec-per-decade", "de", "dative", 2, u"2 Parsec pro Jahrzehnt"},
2228 // Testing de "times", "power" and "prefix" rules:
2230 // <deriveComponent feature="plural" structure="times" value0="one" value1="compound"/>
2231 // <deriveComponent feature="case" structure="times" value0="nominative" value1="compound"/>
2233 // <deriveComponent feature="plural" structure="prefix" value0="one" value1="compound"/>
2234 // <deriveComponent feature="case" structure="prefix" value0="nominative" value1="compound"/>
2236 // Prefixes in German don't change with plural or case, so these
2237 // tests can't test value0 of the following two rules:
2238 // <deriveComponent feature="plural" structure="power" value0="one" value1="compound"/>
2239 // <deriveComponent feature="case" structure="power" value0="nominative" value1="compound"/>
2240 {"square-decimeter-dekameter", "de", nullptr, 1, u"1 Quadratdezimeter⋅Dekameter"},
2241 {"square-decimeter-dekameter", "de", "genitive", 1, u"1 Quadratdezimeter⋅Dekameters"},
2242 {"square-decimeter-dekameter", "de", nullptr, 2, u"2 Quadratdezimeter⋅Dekameter"},
2243 {"square-decimeter-dekameter", "de", "dative", 2, u"2 Quadratdezimeter⋅Dekametern"},
2244 // Feminine "Meile" better demonstrates singular-vs-plural form:
2245 {"cubic-mile-dekamile", "de", nullptr, 1, u"1 Kubikmeile⋅Dekameile"},
2246 {"cubic-mile-dekamile", "de", nullptr, 2, u"2 Kubikmeile⋅Dekameilen"},
2248 // French handles plural "times" and "power" structures differently:
2249 // plural form impacts all "numerator" units (denominator remains
2250 // singular like German), and "pow2" prefixes have different forms
2251 // <deriveComponent feature="plural" structure="times" value0="compound" value1="compound"/>
2252 // <deriveComponent feature="plural" structure="power" value0="compound" value1="compound"/>
2253 {"square-decimeter-square-second", "fr", nullptr, 1, u"1\u00A0décimètre carré-seconde carrée"},
2254 {"square-decimeter-square-second", "fr", nullptr, 2, u"2\u00A0décimètres carrés-secondes carrées"},
2256 runUnitInflectionsTestCases(unf, skeleton, testCases, UPRV_LENGTHOF(testCases), status);
2259 // Testing inflection of mixed units:
2260 unf = NumberFormatter::with().unitWidth(UNUM_UNIT_WIDTH_FULL_NAME);
2261 skeleton = u"unit-width-full-name";
2262 const UnitInflectionTestCase testCases[] = {
2263 {"meter", "de", nullptr, 1, u"1 Meter"},
2264 {"meter", "de", "genitive", 1, u"1 Meters"},
2265 {"meter", "de", "dative", 2, u"2 Metern"},
2266 {"centimeter", "de", nullptr, 1, u"1 Zentimeter"},
2267 {"centimeter", "de", "genitive", 1, u"1 Zentimeters"},
2268 {"centimeter", "de", "dative", 10, u"10 Zentimetern"},
2269 // TODO(CLDR-14502): check that these inflections are correct, and
2270 // whether CLDR needs any rules for them (presumably CLDR spec
2271 // should mention it, if it's a consistent rule):
2272 {"meter-and-centimeter", "de", nullptr, 1.01, u"1 Meter, 1 Zentimeter"},
2273 {"meter-and-centimeter", "de", "genitive", 1.01, u"1 Meters, 1 Zentimeters"},
2274 {"meter-and-centimeter", "de", "genitive", 1.1, u"1 Meters, 10 Zentimeter"},
2275 {"meter-and-centimeter", "de", "dative", 1.1, u"1 Meter, 10 Zentimetern"},
2276 {"meter-and-centimeter", "de", "dative", 2.1, u"2 Metern, 10 Zentimetern"},
2278 runUnitInflectionsTestCases(unf, skeleton, testCases, UPRV_LENGTHOF(testCases),
2281 // TODO: add a usage case that selects between preferences with different
2282 // genders (e.g. year, month, day, hour).
2283 // TODO: look at "↑↑↑" cases: check that inheritance is done right.
2286 void NumberFormatterApiTest::unitGender() {
2287 IcuTestErrorCode status(*this, "unitGender");
2289 const struct TestCase {
2291 const char *unitIdentifier;
2292 const char *expectedGender;
2294 {"de", "meter", "masculine"},
2295 {"de", "second", "feminine"},
2296 {"de", "minute", "feminine"},
2297 {"de", "hour", "feminine"},
2298 {"de", "day", "masculine"},
2299 {"de", "year", "neuter"},
2300 {"fr", "meter", "masculine"},
2301 {"fr", "second", "feminine"},
2302 {"fr", "minute", "feminine"},
2303 {"fr", "hour", "feminine"},
2304 {"fr", "day", "masculine"},
2305 // grammaticalFeatures deriveCompound "per" rule takes the gender of the
2307 {"de", "meter-per-hour", "masculine"},
2308 {"fr", "meter-per-hour", "masculine"},
2309 {"af", "meter-per-hour", ""}, // ungendered language
2310 // French "times" takes gender from first value, German takes the
2311 // second. Prefix and power does not have impact on gender for these
2313 {"de", "square-decimeter-square-second", "feminine"},
2314 {"fr", "square-decimeter-square-second", "masculine"},
2315 // TODO(ICU-21494): determine whether list genders behave as follows,
2316 // and implement proper getListGender support (covering more than just
2318 // // gender rule for lists of people: de "neutral", fr "maleTaints"
2319 // {"de", "day-and-hour-and-minute", "neuter"},
2320 // {"de", "hour-and-minute", "feminine"},
2321 // {"fr", "day-and-hour-and-minute", "masculine"},
2322 // {"fr", "hour-and-minute", "feminine"},
2324 LocalizedNumberFormatter formatter;
2326 for (const TestCase &t : cases) {
2327 formatter = NumberFormatter::with()
2328 .unit(MeasureUnit::forIdentifier(t.unitIdentifier, status))
2329 .locale(Locale(t.locale));
2330 fn = formatter.formatDouble(1.1, status);
2331 assertEquals(UnicodeString("Testing gender with default width, unit: ") + t.unitIdentifier +
2332 ", locale: " + t.locale,
2333 t.expectedGender, fn.getGender(status));
2334 status.assertSuccess();
2336 formatter = NumberFormatter::with()
2337 .unit(MeasureUnit::forIdentifier(t.unitIdentifier, status))
2338 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
2339 .locale(Locale(t.locale));
2340 fn = formatter.formatDouble(1.1, status);
2341 assertEquals(UnicodeString("Testing gender with UNUM_UNIT_WIDTH_FULL_NAME, unit: ") +
2342 t.unitIdentifier + ", locale: " + t.locale,
2343 t.expectedGender, fn.getGender(status));
2344 status.assertSuccess();
2347 // Make sure getGender does not return garbage for genderless languages
2348 formatter = NumberFormatter::with().locale(Locale::getEnglish());
2349 fn = formatter.formatDouble(1.1, status);
2350 status.assertSuccess();
2351 assertEquals("getGender for a genderless language", "", fn.getGender(status));
2354 void NumberFormatterApiTest::unitPercent() {
2355 assertFormatDescending(
2359 NumberFormatter::with().unit(NoUnit::percent()),
2360 Locale::getEnglish(),
2371 assertFormatDescending(
2375 NumberFormatter::with().unit(NoUnit::permille()),
2376 Locale::getEnglish(),
2391 NumberFormatter::with().unit(NoUnit::base()),
2392 Locale::getEnglish(),
2397 u"Percent with Negative Sign",
2400 NumberFormatter::with().unit(NoUnit::percent()),
2401 Locale::getEnglish(),
2406 assertFormatDescendingBig(
2408 u"compact-short percent",
2410 NumberFormatter::with()
2411 .notation(Notation::compactShort())
2412 .unit(NoUnit::percent()),
2413 Locale::getEnglish(),
2425 assertFormatDescendingBig(
2426 u"Compact Percent with Scale",
2427 u"compact-short percent scale/100",
2429 NumberFormatter::with()
2430 .notation(Notation::compactShort())
2431 .unit(NoUnit::percent())
2432 .scale(Scale::powerOfTen(2)),
2433 Locale::getEnglish(),
2445 assertFormatDescendingBig(
2446 u"Compact Percent Long Name",
2447 u"compact-short percent unit-width-full-name",
2448 u"K % unit-width-full-name",
2449 NumberFormatter::with()
2450 .notation(Notation::compactShort())
2451 .unit(NoUnit::percent())
2452 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2453 Locale::getEnglish(),
2466 u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",
2467 u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",
2468 NumberFormatter::with()
2469 .unit(MeasureUnit::getMeter())
2470 .perUnit(MeasureUnit::getPercent())
2471 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2472 Locale::getEnglish(),
2474 u"50 meters per percent");
2477 void NumberFormatterApiTest::percentParity() {
2478 IcuTestErrorCode status(*this, "percentParity");
2479 UnlocalizedNumberFormatter uNoUnitPercent = NumberFormatter::with().unit(NoUnit::percent());
2480 UnlocalizedNumberFormatter uNoUnitPermille = NumberFormatter::with().unit(NoUnit::permille());
2481 UnlocalizedNumberFormatter uMeasurePercent = NumberFormatter::with().unit(MeasureUnit::getPercent());
2482 UnlocalizedNumberFormatter uMeasurePermille = NumberFormatter::with().unit(MeasureUnit::getPermille());
2484 int32_t localeCount;
2485 auto locales = Locale::getAvailableLocales(localeCount);
2486 for (int32_t i=0; i<localeCount; i++) {
2487 auto& locale = locales[i];
2488 UnicodeString sNoUnitPercent = uNoUnitPercent.locale(locale)
2489 .formatDouble(50, status).toString(status);
2490 UnicodeString sNoUnitPermille = uNoUnitPermille.locale(locale)
2491 .formatDouble(50, status).toString(status);
2492 UnicodeString sMeasurePercent = uMeasurePercent.locale(locale)
2493 .formatDouble(50, status).toString(status);
2494 UnicodeString sMeasurePermille = uMeasurePermille.locale(locale)
2495 .formatDouble(50, status).toString(status);
2497 assertEquals(u"Percent, locale " + UnicodeString(locale.getName()),
2498 sNoUnitPercent, sMeasurePercent);
2499 assertEquals(u"Permille, locale " + UnicodeString(locale.getName()),
2500 sNoUnitPermille, sMeasurePermille);
2504 void NumberFormatterApiTest::roundingFraction() {
2505 assertFormatDescending(
2507 u"precision-integer",
2509 NumberFormatter::with().precision(Precision::integer()),
2510 Locale::getEnglish(),
2521 assertFormatDescending(
2525 NumberFormatter::with().precision(Precision::fixedFraction(3)),
2526 Locale::getEnglish(),
2537 assertFormatDescending(
2541 NumberFormatter::with().precision(Precision::minFraction(1)),
2542 Locale::getEnglish(),
2553 assertFormatDescending(
2557 NumberFormatter::with().precision(Precision::maxFraction(1)),
2558 Locale::getEnglish(),
2569 assertFormatDescending(
2570 u"Min/Max Fraction",
2573 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)),
2574 Locale::getEnglish(),
2589 NumberFormatter::with().precision(Precision::fixedFraction(2)
2590 .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE)),
2591 Locale::getEnglish(),
2599 NumberFormatter::with().precision(Precision::fixedFraction(2)
2600 .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE)),
2601 Locale::getEnglish(),
2606 void NumberFormatterApiTest::roundingFigures() {
2608 u"Fixed Significant",
2611 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
2612 Locale::getEnglish(),
2617 u"Fixed Significant Rounding",
2620 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
2621 Locale::getEnglish(),
2626 u"Fixed Significant Zero",
2629 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
2630 Locale::getEnglish(),
2638 NumberFormatter::with().precision(Precision::minSignificantDigits(2)),
2639 Locale::getEnglish(),
2647 NumberFormatter::with().precision(Precision::maxSignificantDigits(4)),
2648 Locale::getEnglish(),
2653 u"Min/Max Significant",
2656 NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)),
2657 Locale::getEnglish(),
2662 u"Fixed Significant on zero with lots of integer width",
2663 u"@ integer-width/+000",
2665 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
2666 .integerWidth(IntegerWidth::zeroFillTo(3)),
2667 Locale::getEnglish(),
2672 u"Fixed Significant on zero with zero integer width",
2673 u"@ integer-width/*",
2674 u"@ integer-width/+",
2675 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
2676 .integerWidth(IntegerWidth::zeroFillTo(0)),
2677 Locale::getEnglish(),
2682 void NumberFormatterApiTest::roundingFractionFigures() {
2683 assertFormatDescending(
2684 u"Basic Significant", // for comparison
2687 NumberFormatter::with().precision(Precision::maxSignificantDigits(2)),
2688 Locale::getEnglish(),
2699 assertFormatDescending(
2700 u"FracSig minMaxFrac minSig",
2703 NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)),
2704 Locale::getEnglish(),
2710 u"0.876", // minSig beats maxFrac
2711 u"0.0876", // minSig beats maxFrac
2712 u"0.00876", // minSig beats maxFrac
2715 assertFormatDescending(
2716 u"FracSig minMaxFrac maxSig A",
2719 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)),
2720 Locale::getEnglish(),
2721 u"88,000.0", // maxSig beats maxFrac
2722 u"8,800.0", // maxSig beats maxFrac
2723 u"880.0", // maxSig beats maxFrac
2724 u"88.0", // maxSig beats maxFrac
2725 u"8.8", // maxSig beats maxFrac
2726 u"0.88", // maxSig beats maxFrac
2731 assertFormatDescending(
2732 u"FracSig minMaxFrac maxSig B",
2735 NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)),
2736 Locale::getEnglish(),
2737 u"88,000.00", // maxSig beats maxFrac
2738 u"8,800.00", // maxSig beats maxFrac
2739 u"880.00", // maxSig beats maxFrac
2740 u"88.00", // maxSig beats maxFrac
2741 u"8.80", // maxSig beats maxFrac
2748 u"FracSig with trailing zeros A",
2751 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
2752 Locale::getEnglish(),
2757 u"FracSig with trailing zeros B",
2760 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
2761 Locale::getEnglish(),
2765 assertFormatDescending(
2766 u"FracSig withSignificantDigits RELAXED",
2767 u"precision-integer/@#r",
2769 NumberFormatter::with().precision(Precision::maxFraction(0)
2770 .withSignificantDigits(1, 2, UNUM_ROUNDING_PRIORITY_RELAXED)),
2771 Locale::getEnglish(),
2782 assertFormatDescending(
2783 u"FracSig withSignificantDigits STRICT",
2784 u"precision-integer/@#s",
2786 NumberFormatter::with().precision(Precision::maxFraction(0)
2787 .withSignificantDigits(1, 2, UNUM_ROUNDING_PRIORITY_STRICT)),
2788 Locale::getEnglish(),
2800 u"FracSig withSignificantDigits Trailing Zeros RELAXED",
2803 NumberFormatter::with().precision(Precision::fixedFraction(1)
2804 .withSignificantDigits(3, 3, UNUM_ROUNDING_PRIORITY_RELAXED)),
2805 Locale::getEnglish(),
2809 // Trailing zeros are always retained:
2811 u"FracSig withSignificantDigits Trailing Zeros STRICT",
2814 NumberFormatter::with().precision(Precision::fixedFraction(1)
2815 .withSignificantDigits(3, 3, UNUM_ROUNDING_PRIORITY_STRICT)),
2816 Locale::getEnglish(),
2821 u"FracSig withSignificantDigits at rounding boundary",
2822 u"precision-integer/@@@s",
2824 NumberFormatter::with().precision(Precision::fixedFraction(0)
2825 .withSignificantDigits(3, 3, UNUM_ROUNDING_PRIORITY_STRICT)),
2826 Locale::getEnglish(),
2831 u"FracSig with Trailing Zero Display",
2834 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)
2835 .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE)),
2836 Locale::getEnglish(),
2841 void NumberFormatterApiTest::roundingOther() {
2842 assertFormatDescending(
2844 u"precision-unlimited",
2846 NumberFormatter::with().precision(Precision::unlimited()),
2847 Locale::getEnglish(),
2858 assertFormatDescending(
2860 u"precision-increment/0.5",
2861 u"precision-increment/0.5",
2862 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)),
2863 Locale::getEnglish(),
2874 assertFormatDescending(
2875 u"Increment with Min Fraction",
2876 u"precision-increment/0.50",
2877 u"precision-increment/0.50",
2878 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)),
2879 Locale::getEnglish(),
2890 assertFormatDescending(
2891 u"Strange Increment",
2892 u"precision-increment/3.140",
2893 u"precision-increment/3.140",
2894 NumberFormatter::with().precision(Precision::increment(3.14).withMinFraction(3)),
2895 Locale::getEnglish(),
2906 assertFormatDescending(
2907 u"Increment Resolving to Power of 10",
2908 u"precision-increment/0.010",
2909 u"precision-increment/0.010",
2910 NumberFormatter::with().precision(Precision::increment(0.01).withMinFraction(3)),
2911 Locale::getEnglish(),
2922 assertFormatDescending(
2923 u"Currency Standard",
2924 u"currency/CZK precision-currency-standard",
2925 u"currency/CZK precision-currency-standard",
2926 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD))
2928 Locale::getEnglish(),
2939 assertFormatDescending(
2941 u"currency/CZK precision-currency-cash",
2942 u"currency/CZK precision-currency-cash",
2943 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
2945 Locale::getEnglish(),
2956 assertFormatDescending(
2957 u"Currency Standard with Trailing Zero Display",
2958 u"currency/CZK precision-currency-standard/w",
2959 u"currency/CZK precision-currency-standard/w",
2960 NumberFormatter::with().precision(
2961 Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD)
2962 .trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE))
2964 Locale::getEnglish(),
2975 assertFormatDescending(
2976 u"Currency Cash with Nickel Rounding",
2977 u"currency/CAD precision-currency-cash",
2978 u"currency/CAD precision-currency-cash",
2979 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
2981 Locale::getEnglish(),
2992 assertFormatDescending(
2993 u"Currency not in top-level fluent chain",
2994 u"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
2996 NumberFormatter::with().precision(
2997 Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH).withCurrency(CZK)),
2998 Locale::getEnglish(),
3009 // NOTE: Other tests cover the behavior of the other rounding modes.
3010 assertFormatDescending(
3011 u"Rounding Mode CEILING",
3012 u"precision-integer rounding-mode-ceiling",
3013 u". rounding-mode-ceiling",
3014 NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING),
3015 Locale::getEnglish(),
3027 u"ICU-20974 Double.MIN_NORMAL",
3030 NumberFormatter::with().notation(Notation::scientific()),
3031 Locale::getEnglish(),
3035 #ifndef DBL_TRUE_MIN
3036 #define DBL_TRUE_MIN 4.9E-324
3039 // Note: this behavior is intentionally different from Java; see
3040 // https://github.com/google/double-conversion/issues/126
3042 u"ICU-20974 Double.MIN_VALUE",
3045 NumberFormatter::with().notation(Notation::scientific()),
3046 Locale::getEnglish(),
3051 void NumberFormatterApiTest::grouping() {
3052 assertFormatDescendingBig(
3053 u"Western Grouping",
3056 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
3057 Locale::getEnglish(),
3068 assertFormatDescendingBig(
3072 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
3084 assertFormatDescendingBig(
3085 u"Western Grouping, Min 2",
3088 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
3089 Locale::getEnglish(),
3100 assertFormatDescendingBig(
3101 u"Indic Grouping, Min 2",
3104 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
3116 assertFormatDescendingBig(
3120 NumberFormatter::with().grouping(UNUM_GROUPING_OFF),
3132 assertFormatDescendingBig(
3133 u"Indic locale with THOUSANDS grouping",
3136 NumberFormatter::with().grouping(UNUM_GROUPING_THOUSANDS),
3148 // NOTE: Polish is interesting because it has minimumGroupingDigits=2 in locale data
3149 // (Most locales have either 1 or 2)
3150 // If this test breaks due to data changes, find another locale that has minimumGroupingDigits.
3151 assertFormatDescendingBig(
3155 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
3167 assertFormatDescendingBig(
3168 u"Polish Grouping, Min 2",
3171 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
3183 assertFormatDescendingBig(
3184 u"Polish Grouping, Always",
3185 u"group-on-aligned",
3187 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED),
3199 // NOTE: Bulgarian is interesting because it has no grouping in the default currency format.
3200 // If this test breaks due to data changes, find another locale that has no default grouping.
3201 assertFormatDescendingBig(
3202 u"Bulgarian Currency Grouping",
3203 u"currency/USD group-auto",
3205 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO).unit(USD),
3207 u"87650000,00 щ.д.",
3217 assertFormatDescendingBig(
3218 u"Bulgarian Currency Grouping, Always",
3219 u"currency/USD group-on-aligned",
3221 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED).unit(USD),
3223 u"87 650 000,00 щ.д.",
3224 u"8 765 000,00 щ.д.",
3234 macros.grouper = Grouper(4, 1, 3, UNUM_GROUPING_COUNT);
3235 assertFormatDescendingBig(
3236 u"Custom Grouping via Internal API",
3239 NumberFormatter::with().macros(macros),
3240 Locale::getEnglish(),
3252 void NumberFormatterApiTest::padding() {
3253 assertFormatDescending(
3257 NumberFormatter::with().padding(Padder::none()),
3258 Locale::getEnglish(),
3269 assertFormatDescending(
3273 NumberFormatter::with().padding(
3275 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
3276 Locale::getEnglish(),
3287 assertFormatDescending(
3288 u"Padding with code points",
3291 NumberFormatter::with().padding(
3293 0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
3294 Locale::getEnglish(),
3305 assertFormatDescending(
3306 u"Padding with wide digits",
3309 NumberFormatter::with().padding(
3311 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX))
3312 .adoptSymbols(new NumberingSystem(MATHSANB)),
3313 Locale::getEnglish(),
3324 assertFormatDescending(
3325 u"Padding with currency spacing",
3328 NumberFormatter::with().padding(
3330 '*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX))
3332 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
3333 Locale::getEnglish(),
3345 u"Pad Before Prefix",
3348 NumberFormatter::with().padding(
3350 '*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX)),
3351 Locale::getEnglish(),
3356 u"Pad After Prefix",
3359 NumberFormatter::with().padding(
3361 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
3362 Locale::getEnglish(),
3367 u"Pad Before Suffix",
3370 NumberFormatter::with().padding(
3372 '*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX)).unit(NoUnit::percent()),
3373 Locale::getEnglish(),
3378 u"Pad After Suffix",
3381 NumberFormatter::with().padding(
3383 '*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX)).unit(NoUnit::percent()),
3384 Locale::getEnglish(),
3389 u"Currency Spacing with Zero Digit Padding Broken",
3392 NumberFormatter::with().padding(
3394 '0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX))
3396 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
3397 Locale::getEnglish(),
3399 u"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
3402 void NumberFormatterApiTest::integerWidth() {
3403 assertFormatDescending(
3404 u"Integer Width Default",
3405 u"integer-width/+0",
3407 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
3408 Locale::getEnglish(),
3419 assertFormatDescending(
3420 u"Integer Width Zero Fill 0",
3423 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
3424 Locale::getEnglish(),
3433 u"0"); // see ICU-20844
3435 assertFormatDescending(
3436 u"Integer Width Zero Fill 3",
3437 u"integer-width/+000",
3439 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
3440 Locale::getEnglish(),
3451 assertFormatDescending(
3452 u"Integer Width Max 3",
3453 u"integer-width/##0",
3454 u"integer-width/##0",
3455 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
3456 Locale::getEnglish(),
3467 assertFormatDescending(
3468 u"Integer Width Fixed 2",
3469 u"integer-width/00",
3470 u"integer-width/00",
3471 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
3472 Locale::getEnglish(),
3483 assertFormatDescending(
3484 u"Integer Width Compact",
3485 u"compact-short integer-width/000",
3486 u"compact-short integer-width/000",
3487 NumberFormatter::with()
3488 .notation(Notation::compactShort())
3489 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
3490 Locale::getEnglish(),
3501 assertFormatDescending(
3502 u"Integer Width Scientific",
3503 u"scientific integer-width/000",
3504 u"scientific integer-width/000",
3505 NumberFormatter::with()
3506 .notation(Notation::scientific())
3507 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
3508 Locale::getEnglish(),
3519 assertFormatDescending(
3520 u"Integer Width Engineering",
3521 u"engineering integer-width/000",
3522 u"engineering integer-width/000",
3523 NumberFormatter::with()
3524 .notation(Notation::engineering())
3525 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
3526 Locale::getEnglish(),
3538 u"Integer Width Remove All A",
3539 u"integer-width/00",
3540 u"integer-width/00",
3541 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
3547 u"Integer Width Remove All B",
3548 u"integer-width/00",
3549 u"integer-width/00",
3550 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
3556 u"Integer Width Remove All B, Bytes Mode",
3557 u"integer-width/00",
3558 u"integer-width/00",
3559 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
3561 // Note: this double produces all 17 significant digits
3562 10000000000000002000.0,
3566 void NumberFormatterApiTest::symbols() {
3567 assertFormatDescending(
3568 u"French Symbols with Japanese Data 1",
3571 NumberFormatter::with().symbols(FRENCH_SYMBOLS),
3584 u"French Symbols with Japanese Data 2",
3587 NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS),
3592 assertFormatDescending(
3593 u"Latin Numbering System with Arabic Data",
3594 u"currency/USD latin",
3595 u"currency/USD latin",
3596 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
3608 assertFormatDescending(
3609 u"Math Numbering System with French Data",
3610 u"numbering-system/mathsanb",
3611 u"numbering-system/mathsanb",
3612 NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB)),
3613 Locale::getFrench(),
3625 u"Swiss Symbols (used in documentation)",
3628 NumberFormatter::with().symbols(SWISS_SYMBOLS),
3629 Locale::getEnglish(),
3634 u"Myanmar Symbols (used in documentation)",
3637 NumberFormatter::with().symbols(MYANMAR_SYMBOLS),
3638 Locale::getEnglish(),
3640 u"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
3642 // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
3645 u"Currency symbol should precede number in ar with NS latn",
3646 u"currency/USD latin",
3647 u"currency/USD latin",
3648 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
3654 u"Currency symbol should precede number in ar@numbers=latn",
3657 NumberFormatter::with().unit(USD),
3658 Locale("ar@numbers=latn"),
3663 u"Currency symbol should follow number in ar-EG with NS arab",
3666 NumberFormatter::with().unit(USD),
3672 u"Currency symbol should follow number in ar@numbers=arab",
3675 NumberFormatter::with().unit(USD),
3676 Locale("ar@numbers=arab"),
3681 u"NumberingSystem in API should win over @numbers keyword",
3682 u"currency/USD latin",
3683 u"currency/USD latin",
3684 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
3685 Locale("ar@numbers=arab"),
3689 UErrorCode status = U_ZERO_ERROR;
3691 "NumberingSystem in API should win over @numbers keyword in reverse order",
3693 NumberFormatter::withLocale(Locale("ar@numbers=arab")).adoptSymbols(new NumberingSystem(LATN))
3695 .formatDouble(12345.67, status)
3698 DecimalFormatSymbols symbols = SWISS_SYMBOLS;
3699 UnlocalizedNumberFormatter f = NumberFormatter::with().symbols(symbols);
3700 symbols.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol, u"!", status);
3702 u"Symbols object should be copied",
3706 Locale::getEnglish(),
3711 u"The last symbols setter wins",
3714 NumberFormatter::with().symbols(symbols).adoptSymbols(new NumberingSystem(LATN)),
3715 Locale::getEnglish(),
3720 u"The last symbols setter wins",
3723 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).symbols(symbols),
3724 Locale::getEnglish(),
3729 // TODO: Enable if/when currency symbol override is added.
3730 //void NumberFormatterTest::symbolsOverride() {
3731 // DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
3732 // dfs.setCurrencySymbol("@");
3733 // dfs.setInternationalCurrencySymbol("foo");
3734 // assertFormatSingle(
3735 // u"Custom Short Currency Symbol",
3736 // NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
3737 // Locale::getEnglish(),
3742 void NumberFormatterApiTest::sign() {
3744 u"Sign Auto Positive",
3747 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
3748 Locale::getEnglish(),
3753 u"Sign Auto Negative",
3756 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
3757 Locale::getEnglish(),
3765 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
3766 Locale::getEnglish(),
3771 u"Sign Always Positive",
3774 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
3775 Locale::getEnglish(),
3780 u"Sign Always Negative",
3783 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
3784 Locale::getEnglish(),
3789 u"Sign Always Zero",
3792 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
3793 Locale::getEnglish(),
3798 u"Sign Never Positive",
3801 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
3802 Locale::getEnglish(),
3807 u"Sign Never Negative",
3810 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
3811 Locale::getEnglish(),
3819 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
3820 Locale::getEnglish(),
3825 u"Sign Accounting Positive",
3826 u"currency/USD sign-accounting",
3828 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
3829 Locale::getEnglish(),
3834 u"Sign Accounting Negative",
3835 u"currency/USD sign-accounting",
3837 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
3838 Locale::getEnglish(),
3843 u"Sign Accounting Zero",
3844 u"currency/USD sign-accounting",
3846 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
3847 Locale::getEnglish(),
3852 u"Sign Accounting-Always Positive",
3853 u"currency/USD sign-accounting-always",
3854 u"currency/USD ()!",
3855 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
3856 Locale::getEnglish(),
3861 u"Sign Accounting-Always Negative",
3862 u"currency/USD sign-accounting-always",
3863 u"currency/USD ()!",
3864 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
3865 Locale::getEnglish(),
3870 u"Sign Accounting-Always Zero",
3871 u"currency/USD sign-accounting-always",
3872 u"currency/USD ()!",
3873 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
3874 Locale::getEnglish(),
3879 u"Sign Except-Zero Positive",
3880 u"sign-except-zero",
3882 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
3883 Locale::getEnglish(),
3888 u"Sign Except-Zero Negative",
3889 u"sign-except-zero",
3891 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
3892 Locale::getEnglish(),
3897 u"Sign Except-Zero Zero",
3898 u"sign-except-zero",
3900 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
3901 Locale::getEnglish(),
3906 u"Sign Accounting-Except-Zero Positive",
3907 u"currency/USD sign-accounting-except-zero",
3908 u"currency/USD ()?",
3909 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
3910 Locale::getEnglish(),
3915 u"Sign Accounting-Except-Zero Negative",
3916 u"currency/USD sign-accounting-except-zero",
3917 u"currency/USD ()?",
3918 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
3919 Locale::getEnglish(),
3924 u"Sign Accounting-Except-Zero Zero",
3925 u"currency/USD sign-accounting-except-zero",
3926 u"currency/USD ()?",
3927 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
3928 Locale::getEnglish(),
3933 u"Sign Negative Positive",
3936 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEGATIVE),
3937 Locale::getEnglish(),
3942 u"Sign Negative Negative",
3945 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEGATIVE),
3946 Locale::getEnglish(),
3951 u"Sign Negative Negative Zero",
3954 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEGATIVE),
3955 Locale::getEnglish(),
3960 u"Sign Accounting-Negative Positive",
3961 u"currency/USD sign-accounting-negative",
3962 u"currency/USD ()-",
3963 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_NEGATIVE).unit(USD),
3964 Locale::getEnglish(),
3969 u"Sign Accounting-Negative Negative",
3970 u"currency/USD sign-accounting-negative",
3971 u"currency/USD ()-",
3972 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_NEGATIVE).unit(USD),
3973 Locale::getEnglish(),
3978 u"Sign Accounting-Negative Negative Zero",
3979 u"currency/USD sign-accounting-negative",
3980 u"currency/USD ()-",
3981 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_NEGATIVE).unit(USD),
3982 Locale::getEnglish(),
3987 u"Sign Accounting Negative Hidden",
3988 u"currency/USD unit-width-hidden sign-accounting",
3989 u"currency/USD unit-width-hidden ()",
3990 NumberFormatter::with()
3991 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3993 .unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
3994 Locale::getEnglish(),
3999 u"Sign Accounting Negative Narrow",
4000 u"currency/USD unit-width-narrow sign-accounting",
4001 u"currency/USD unit-width-narrow ()",
4002 NumberFormatter::with()
4003 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
4005 .unitWidth(UNUM_UNIT_WIDTH_NARROW),
4006 Locale::getCanada(),
4011 u"Sign Accounting Negative Short",
4012 u"currency/USD sign-accounting",
4014 NumberFormatter::with()
4015 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
4017 .unitWidth(UNUM_UNIT_WIDTH_SHORT),
4018 Locale::getCanada(),
4020 u"(US$444,444.00)");
4023 u"Sign Accounting Negative Iso Code",
4024 u"currency/USD unit-width-iso-code sign-accounting",
4025 u"currency/USD unit-width-iso-code ()",
4026 NumberFormatter::with()
4027 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
4029 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
4030 Locale::getCanada(),
4032 u"(USD 444,444.00)");
4034 // Note: CLDR does not provide an accounting pattern for long name currency.
4035 // We fall back to normal currency format. This may change in the future.
4037 u"Sign Accounting Negative Full Name",
4038 u"currency/USD unit-width-full-name sign-accounting",
4039 u"currency/USD unit-width-full-name ()",
4040 NumberFormatter::with()
4041 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
4043 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
4044 Locale::getCanada(),
4046 u"-444,444.00 US dollars");
4049 void NumberFormatterApiTest::signNearZero() {
4050 // https://unicode-org.atlassian.net/browse/ICU-20709
4051 IcuTestErrorCode status(*this, "signNearZero");
4052 const struct TestCase {
4053 UNumberSignDisplay sign;
4055 const char16_t* expected;
4057 { UNUM_SIGN_AUTO, 1.1, u"1" },
4058 { UNUM_SIGN_AUTO, 0.9, u"1" },
4059 { UNUM_SIGN_AUTO, 0.1, u"0" },
4060 { UNUM_SIGN_AUTO, -0.1, u"-0" }, // interesting case
4061 { UNUM_SIGN_AUTO, -0.9, u"-1" },
4062 { UNUM_SIGN_AUTO, -1.1, u"-1" },
4063 { UNUM_SIGN_ALWAYS, 1.1, u"+1" },
4064 { UNUM_SIGN_ALWAYS, 0.9, u"+1" },
4065 { UNUM_SIGN_ALWAYS, 0.1, u"+0" },
4066 { UNUM_SIGN_ALWAYS, -0.1, u"-0" },
4067 { UNUM_SIGN_ALWAYS, -0.9, u"-1" },
4068 { UNUM_SIGN_ALWAYS, -1.1, u"-1" },
4069 { UNUM_SIGN_EXCEPT_ZERO, 1.1, u"+1" },
4070 { UNUM_SIGN_EXCEPT_ZERO, 0.9, u"+1" },
4071 { UNUM_SIGN_EXCEPT_ZERO, 0.1, u"0" }, // interesting case
4072 { UNUM_SIGN_EXCEPT_ZERO, -0.1, u"0" }, // interesting case
4073 { UNUM_SIGN_EXCEPT_ZERO, -0.9, u"-1" },
4074 { UNUM_SIGN_EXCEPT_ZERO, -1.1, u"-1" },
4075 { UNUM_SIGN_NEGATIVE, 1.1, u"1" },
4076 { UNUM_SIGN_NEGATIVE, 0.9, u"1" },
4077 { UNUM_SIGN_NEGATIVE, 0.1, u"0" },
4078 { UNUM_SIGN_NEGATIVE, -0.1, u"0" }, // interesting case
4079 { UNUM_SIGN_NEGATIVE, -0.9, u"-1" },
4080 { UNUM_SIGN_NEGATIVE, -1.1, u"-1" },
4082 for (auto& cas : cases) {
4083 auto sign = cas.sign;
4084 auto input = cas.input;
4085 auto expected = cas.expected;
4086 auto actual = NumberFormatter::with()
4088 .precision(Precision::integer())
4089 .locale(Locale::getUS())
4090 .formatDouble(input, status)
4093 DoubleToUnicodeString(input) + " @ SignDisplay " + Int64ToUnicodeString(sign),
4098 void NumberFormatterApiTest::signCoverage() {
4099 // https://unicode-org.atlassian.net/browse/ICU-20708
4100 IcuTestErrorCode status(*this, "signCoverage");
4101 const struct TestCase {
4102 UNumberSignDisplay sign;
4103 const char16_t* expectedStrings[8];
4105 { UNUM_SIGN_AUTO, { u"-∞", u"-1", u"-0", u"0", u"1", u"∞", u"NaN", u"-NaN" } },
4106 { UNUM_SIGN_ALWAYS, { u"-∞", u"-1", u"-0", u"+0", u"+1", u"+∞", u"+NaN", u"-NaN" } },
4107 { UNUM_SIGN_NEVER, { u"∞", u"1", u"0", u"0", u"1", u"∞", u"NaN", u"NaN" } },
4108 { UNUM_SIGN_EXCEPT_ZERO, { u"-∞", u"-1", u"0", u"0", u"+1", u"+∞", u"NaN", u"NaN" } },
4110 double negNaN = std::copysign(uprv_getNaN(), -0.0);
4111 const double inputs[] = {
4112 -uprv_getInfinity(), -1, -0.0, 0, 1, uprv_getInfinity(), uprv_getNaN(), negNaN
4114 for (auto& cas : cases) {
4115 auto sign = cas.sign;
4116 for (int32_t i = 0; i < UPRV_LENGTHOF(inputs); i++) {
4117 auto input = inputs[i];
4118 auto expected = cas.expectedStrings[i];
4119 auto actual = NumberFormatter::with()
4121 .locale(Locale::getUS())
4122 .formatDouble(input, status)
4125 DoubleToUnicodeString(input) + " " + Int64ToUnicodeString(sign),
4131 void NumberFormatterApiTest::decimal() {
4132 assertFormatDescending(
4136 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO),
4137 Locale::getEnglish(),
4148 assertFormatDescending(
4149 u"Decimal Always Shown",
4152 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS),
4153 Locale::getEnglish(),
4165 void NumberFormatterApiTest::scale() {
4166 assertFormatDescending(
4170 NumberFormatter::with().scale(Scale::none()),
4171 Locale::getEnglish(),
4182 assertFormatDescending(
4183 u"Multiplier Power of Ten",
4186 NumberFormatter::with().scale(Scale::powerOfTen(6)),
4187 Locale::getEnglish(),
4198 assertFormatDescending(
4199 u"Multiplier Arbitrary Double",
4202 NumberFormatter::with().scale(Scale::byDouble(5.2)),
4203 Locale::getEnglish(),
4214 assertFormatDescending(
4215 u"Multiplier Arbitrary BigDecimal",
4218 NumberFormatter::with().scale(Scale::byDecimal({"5.2", -1})),
4219 Locale::getEnglish(),
4230 assertFormatDescending(
4231 u"Multiplier Arbitrary Double And Power Of Ten",
4234 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(5.2, 3)),
4235 Locale::getEnglish(),
4246 assertFormatDescending(
4250 NumberFormatter::with().scale(Scale::byDouble(0)),
4251 Locale::getEnglish(),
4263 u"Multiplier Skeleton Scientific Notation and Percent",
4264 u"percent scale/1E2",
4266 NumberFormatter::with().unit(NoUnit::percent()).scale(Scale::powerOfTen(2)),
4267 Locale::getEnglish(),
4272 u"Negative Multiplier",
4275 NumberFormatter::with().scale(Scale::byDouble(-5.2)),
4276 Locale::getEnglish(),
4281 u"Negative One Multiplier",
4284 NumberFormatter::with().scale(Scale::byDouble(-1)),
4285 Locale::getEnglish(),
4290 u"Two-Type Multiplier with Overlap",
4293 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(100, 2)),
4294 Locale::getEnglish(),
4299 void NumberFormatterApiTest::locale() {
4300 // Coverage for the locale setters.
4301 UErrorCode status = U_ZERO_ERROR;
4302 UnicodeString actual = NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status)
4304 assertEquals("Locale withLocale()", u"1\u202f234", actual);
4307 void NumberFormatterApiTest::skeletonUserGuideExamples() {
4308 IcuTestErrorCode status(*this, "skeletonUserGuideExamples");
4310 // Test the skeleton examples in userguide/format_parse/numbers/skeletons.md
4312 const char16_t* skeleton;
4313 const char16_t* conciseSkeleton;
4315 const char16_t* expected;
4317 {u"percent", u"%", 25, u"25%"},
4318 {u".00", u".00", 25, u"25.00"},
4319 {u"percent .00", u"% .00", 25, u"25.00%"},
4320 {u"scale/100", u"scale/100", 0.3, u"30"},
4321 {u"percent scale/100", u"%x100", 0.3, u"30%"},
4322 {u"measure-unit/length-meter", u"unit/meter", 5, u"5 m"},
4323 {u"measure-unit/length-meter unit-width-full-name", u"unit/meter unit-width-full-name", 5, u"5 meters"},
4324 {u"currency/CAD", u"currency/CAD", 10, u"CA$10.00"},
4325 {u"currency/CAD unit-width-narrow", u"currency/CAD unit-width-narrow", 10, u"$10.00"},
4326 {u"compact-short", u"K", 5000, u"5K"},
4327 {u"compact-long", u"KK", 5000, u"5 thousand"},
4328 {u"compact-short currency/CAD", u"K currency/CAD", 5000, u"CA$5K"},
4329 {u"", u"", 5000, u"5,000"},
4330 {u"group-min2", u",?", 5000, u"5000"},
4331 {u"group-min2", u",?", 15000, u"15,000"},
4332 {u"sign-always", u"+!", 60, u"+60"},
4333 {u"sign-always", u"+!", 0, u"+0"},
4334 {u"sign-except-zero", u"+?", 60, u"+60"},
4335 {u"sign-except-zero", u"+?", 0, u"0"},
4336 {u"sign-accounting currency/CAD", u"() currency/CAD", -40, u"(CA$40.00)"}
4339 for (const auto& cas : cases) {
4340 status.setScope(cas.skeleton);
4341 FormattedNumber actual = NumberFormatter::forSkeleton(cas.skeleton, status)
4343 .formatDouble(cas.input, status);
4344 assertEquals(cas.skeleton, cas.expected, actual.toTempString(status));
4345 status.errIfFailureAndReset();
4346 FormattedNumber actualConcise = NumberFormatter::forSkeleton(cas.conciseSkeleton, status)
4348 .formatDouble(cas.input, status);
4349 assertEquals(cas.conciseSkeleton, cas.expected, actualConcise.toTempString(status));
4350 status.errIfFailureAndReset();
4354 void NumberFormatterApiTest::formatTypes() {
4355 UErrorCode status = U_ZERO_ERROR;
4356 LocalizedNumberFormatter formatter = NumberFormatter::withLocale(Locale::getEnglish());
4359 assertEquals("Format double", "514.23", formatter.formatDouble(514.23, status).toString(status));
4362 assertEquals("Format int64", "51,423", formatter.formatDouble(51423L, status).toString(status));
4365 UnicodeString actual = formatter.formatDecimal("98765432123456789E1", status).toString(status);
4366 assertEquals("Format decNumber", u"987,654,321,234,567,890", actual);
4368 // Also test proper DecimalQuantity bytes storage when all digits are in the fraction.
4369 // The number needs to have exactly 40 digits, which is the size of the default buffer.
4370 // (issue discovered by the address sanitizer in C++)
4371 static const char* str = "0.009876543210987654321098765432109876543211";
4372 actual = formatter.precision(Precision::unlimited()).formatDecimal(str, status).toString(status);
4373 assertEquals("Format decNumber to 40 digits", str, actual);
4376 void NumberFormatterApiTest::fieldPositionLogic() {
4377 IcuTestErrorCode status(*this, "fieldPositionLogic");
4379 const char16_t* message = u"Field position logic test";
4381 FormattedNumber fmtd = assertFormatSingle(
4385 NumberFormatter::with(),
4386 Locale::getEnglish(),
4388 u"-9,876,543,210.12");
4390 static const UFieldPosition expectedFieldPositions[] = {
4391 // field, begin index, end index
4392 {UNUM_SIGN_FIELD, 0, 1},
4393 {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
4394 {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
4395 {UNUM_GROUPING_SEPARATOR_FIELD, 10, 11},
4396 {UNUM_INTEGER_FIELD, 1, 14},
4397 {UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
4398 {UNUM_FRACTION_FIELD, 15, 17}};
4400 assertNumberFieldPositions(
4403 expectedFieldPositions,
4404 UPRV_LENGTHOF(expectedFieldPositions));
4406 // Test the iteration functionality of nextFieldPosition
4407 ConstrainedFieldPosition actual;
4408 actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD);
4410 while (fmtd.nextPosition(actual, status)) {
4411 UFieldPosition expected = expectedFieldPositions[i++];
4413 UnicodeString(u"Next for grouping, field, case #") + Int64ToUnicodeString(i),
4417 UnicodeString(u"Next for grouping, begin index, case #") + Int64ToUnicodeString(i),
4418 expected.beginIndex,
4421 UnicodeString(u"Next for grouping, end index, case #") + Int64ToUnicodeString(i),
4425 assertEquals(u"Should have seen all grouping separators", 4, i);
4427 // Make sure strings without fraction do not contain fraction field
4429 actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD);
4430 fmtd = NumberFormatter::withLocale("en").formatInt(5, status);
4431 assertFalse(u"No fraction part in an integer", fmtd.nextPosition(actual, status));
4434 void NumberFormatterApiTest::fieldPositionCoverage() {
4435 IcuTestErrorCode status(*this, "fieldPositionCoverage");
4438 const char16_t* message = u"Measure unit field position basic";
4439 FormattedNumber result = assertFormatSingle(
4441 u"measure-unit/temperature-fahrenheit",
4443 NumberFormatter::with().unit(FAHRENHEIT),
4444 Locale::getEnglish(),
4447 static const UFieldPosition expectedFieldPositions[] = {
4448 // field, begin index, end index
4449 {UNUM_INTEGER_FIELD, 0, 2},
4450 {UNUM_MEASURE_UNIT_FIELD, 2, 4}};
4451 assertNumberFieldPositions(
4454 expectedFieldPositions,
4455 UPRV_LENGTHOF(expectedFieldPositions));
4459 const char16_t* message = u"Measure unit field position with compound unit";
4460 FormattedNumber result = assertFormatSingle(
4462 u"measure-unit/temperature-fahrenheit per-measure-unit/duration-day",
4463 u"unit/fahrenheit-per-day",
4464 NumberFormatter::with().unit(FAHRENHEIT).perUnit(DAY),
4465 Locale::getEnglish(),
4468 static const UFieldPosition expectedFieldPositions[] = {
4469 // field, begin index, end index
4470 {UNUM_INTEGER_FIELD, 0, 2},
4471 // coverage for old enum:
4472 {DecimalFormat::kMeasureUnitField, 2, 6}};
4473 assertNumberFieldPositions(
4476 expectedFieldPositions,
4477 UPRV_LENGTHOF(expectedFieldPositions));
4481 const char16_t* message = u"Measure unit field position with spaces";
4482 FormattedNumber result = assertFormatSingle(
4484 u"measure-unit/length-meter unit-width-full-name",
4485 u"unit/meter unit-width-full-name",
4486 NumberFormatter::with().unit(METER).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
4487 Locale::getEnglish(),
4490 static const UFieldPosition expectedFieldPositions[] = {
4491 // field, begin index, end index
4492 {UNUM_INTEGER_FIELD, 0, 2},
4493 // note: field starts after the space
4494 {UNUM_MEASURE_UNIT_FIELD, 3, 9}};
4495 assertNumberFieldPositions(
4498 expectedFieldPositions,
4499 UPRV_LENGTHOF(expectedFieldPositions));
4503 const char16_t* message = u"Measure unit field position with prefix and suffix, composed m/s";
4504 FormattedNumber result = assertFormatSingle(
4506 u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
4507 u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
4508 NumberFormatter::with().unit(METER).perUnit(SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
4509 "ky", // locale with the interesting data
4511 u"секундасына 68 метр");
4512 static const UFieldPosition expectedFieldPositions[] = {
4513 // field, begin index, end index
4514 {UNUM_MEASURE_UNIT_FIELD, 0, 11},
4515 {UNUM_INTEGER_FIELD, 12, 14},
4516 {UNUM_MEASURE_UNIT_FIELD, 15, 19}};
4517 assertNumberFieldPositions(
4520 expectedFieldPositions,
4521 UPRV_LENGTHOF(expectedFieldPositions));
4525 const char16_t* message = u"Measure unit field position with prefix and suffix, built-in m/s";
4526 FormattedNumber result = assertFormatSingle(
4528 u"measure-unit/speed-meter-per-second unit-width-full-name",
4529 u"unit/meter-per-second unit-width-full-name",
4530 NumberFormatter::with().unit(METER_PER_SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
4531 "ky", // locale with the interesting data
4533 u"секундасына 68 метр");
4534 static const UFieldPosition expectedFieldPositions[] = {
4535 // field, begin index, end index
4536 {UNUM_MEASURE_UNIT_FIELD, 0, 11},
4537 {UNUM_INTEGER_FIELD, 12, 14},
4538 {UNUM_MEASURE_UNIT_FIELD, 15, 19}};
4539 assertNumberFieldPositions(
4542 expectedFieldPositions,
4543 UPRV_LENGTHOF(expectedFieldPositions));
4547 const char16_t* message = u"Measure unit field position with inner spaces";
4548 FormattedNumber result = assertFormatSingle(
4550 u"measure-unit/temperature-fahrenheit unit-width-full-name",
4551 u"unit/fahrenheit unit-width-full-name",
4552 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
4553 "vi", // locale with the interesting data
4556 static const UFieldPosition expectedFieldPositions[] = {
4557 // field, begin index, end index
4558 {UNUM_INTEGER_FIELD, 0, 2},
4559 // Should trim leading/trailing spaces, but not inner spaces:
4560 {UNUM_MEASURE_UNIT_FIELD, 3, 7}};
4561 assertNumberFieldPositions(
4564 expectedFieldPositions,
4565 UPRV_LENGTHOF(expectedFieldPositions));
4569 // Data: other{"{0} K"} == "\u200E{0} K"
4570 // If that data changes, try to find another example of a non-empty unit prefix/suffix
4571 // that is also all ignorables (whitespace and bidi control marks).
4572 const char16_t* message = u"Measure unit field position with fully ignorable prefix";
4573 FormattedNumber result = assertFormatSingle(
4575 u"measure-unit/temperature-kelvin",
4577 NumberFormatter::with().unit(KELVIN),
4578 "fa", // locale with the interesting data
4581 static const UFieldPosition expectedFieldPositions[] = {
4582 // field, begin index, end index
4583 {UNUM_INTEGER_FIELD, 1, 3},
4584 {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
4585 assertNumberFieldPositions(
4588 expectedFieldPositions,
4589 UPRV_LENGTHOF(expectedFieldPositions));
4593 const char16_t* message = u"Compact field basic";
4594 FormattedNumber result = assertFormatSingle(
4598 NumberFormatter::with().notation(Notation::compactShort()),
4602 static const UFieldPosition expectedFieldPositions[] = {
4603 // field, begin index, end index
4604 {UNUM_INTEGER_FIELD, 0, 2},
4605 {UNUM_COMPACT_FIELD, 2, 3}};
4606 assertNumberFieldPositions(
4609 expectedFieldPositions,
4610 UPRV_LENGTHOF(expectedFieldPositions));
4614 const char16_t* message = u"Compact field with spaces";
4615 FormattedNumber result = assertFormatSingle(
4619 NumberFormatter::with().notation(Notation::compactLong()),
4623 static const UFieldPosition expectedFieldPositions[] = {
4624 // field, begin index, end index
4625 {UNUM_INTEGER_FIELD, 0, 2},
4626 {UNUM_COMPACT_FIELD, 3, 11}};
4627 assertNumberFieldPositions(
4630 expectedFieldPositions,
4631 UPRV_LENGTHOF(expectedFieldPositions));
4635 const char16_t* message = u"Compact field with inner space";
4636 FormattedNumber result = assertFormatSingle(
4640 NumberFormatter::with().notation(Notation::compactLong()),
4641 "fil", // locale with interesting data
4644 static const UFieldPosition expectedFieldPositions[] = {
4645 // field, begin index, end index
4646 {UNUM_INTEGER_FIELD, 0, 1},
4647 {UNUM_COMPACT_FIELD, 2, 9}};
4648 assertNumberFieldPositions(
4651 expectedFieldPositions,
4652 UPRV_LENGTHOF(expectedFieldPositions));
4656 const char16_t* message = u"Compact field with bidi mark";
4657 FormattedNumber result = assertFormatSingle(
4661 NumberFormatter::with().notation(Notation::compactLong()),
4662 "he", // locale with interesting data
4665 static const UFieldPosition expectedFieldPositions[] = {
4666 // field, begin index, end index
4667 {UNUM_INTEGER_FIELD, 1, 2},
4668 {UNUM_COMPACT_FIELD, 3, 6}};
4669 assertNumberFieldPositions(
4672 expectedFieldPositions,
4673 UPRV_LENGTHOF(expectedFieldPositions));
4677 const char16_t* message = u"Compact with currency fields";
4678 FormattedNumber result = assertFormatSingle(
4680 u"compact-short currency/USD",
4682 NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
4683 "sr_Latn", // locale with interesting data
4686 static const UFieldPosition expectedFieldPositions[] = {
4687 // field, begin index, end index
4688 {UNUM_INTEGER_FIELD, 0, 2},
4689 {UNUM_COMPACT_FIELD, 3, 8},
4690 {UNUM_CURRENCY_FIELD, 9, 12}};
4691 assertNumberFieldPositions(
4694 expectedFieldPositions,
4695 UPRV_LENGTHOF(expectedFieldPositions));
4699 const char16_t* message = u"Currency long name fields";
4700 FormattedNumber result = assertFormatSingle(
4702 u"currency/USD unit-width-full-name",
4703 u"currency/USD unit-width-full-name",
4704 NumberFormatter::with().unit(USD)
4705 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
4708 u"12,345.00 US dollars");
4709 static const UFieldPosition expectedFieldPositions[] = {
4710 // field, begin index, end index
4711 {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
4712 {UNUM_INTEGER_FIELD, 0, 6},
4713 {UNUM_DECIMAL_SEPARATOR_FIELD, 6, 7},
4714 {UNUM_FRACTION_FIELD, 7, 9},
4715 {UNUM_CURRENCY_FIELD, 10, 20}};
4716 assertNumberFieldPositions(
4719 expectedFieldPositions,
4720 UPRV_LENGTHOF(expectedFieldPositions));
4724 const char16_t* message = u"Compact with measure unit fields";
4725 FormattedNumber result = assertFormatSingle(
4727 u"compact-long measure-unit/length-meter unit-width-full-name",
4728 u"KK unit/meter unit-width-full-name",
4729 NumberFormatter::with().notation(Notation::compactLong())
4731 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
4734 u"65 thousand meters");
4735 static const UFieldPosition expectedFieldPositions[] = {
4736 // field, begin index, end index
4737 {UNUM_INTEGER_FIELD, 0, 2},
4738 {UNUM_COMPACT_FIELD, 3, 11},
4739 {UNUM_MEASURE_UNIT_FIELD, 12, 18}};
4740 assertNumberFieldPositions(
4743 expectedFieldPositions,
4744 UPRV_LENGTHOF(expectedFieldPositions));
4748 void NumberFormatterApiTest::toFormat() {
4749 IcuTestErrorCode status(*this, "icuFormat");
4750 LocalizedNumberFormatter lnf = NumberFormatter::withLocale("fr")
4751 .precision(Precision::fixedFraction(3));
4752 LocalPointer<Format> format(lnf.toFormat(status), status);
4753 FieldPosition fpos(UNUM_DECIMAL_SEPARATOR_FIELD);
4755 format->format(514.23, sb, fpos, status);
4756 assertEquals("Should correctly format number", u"514,230", sb);
4757 assertEquals("Should find decimal separator", 3, fpos.getBeginIndex());
4758 assertEquals("Should find end of decimal separator", 4, fpos.getEndIndex());
4760 "ICU Format should round-trip",
4761 lnf.toSkeleton(status),
4762 dynamic_cast<LocalizedNumberFormatterAsFormat*>(format.getAlias())->getNumberFormatter()
4763 .toSkeleton(status));
4765 UFormattedNumberData result;
4766 result.quantity.setToDouble(514.23);
4767 lnf.formatImpl(&result, status);
4768 FieldPositionIterator fpi1;
4770 FieldPositionIteratorHandler fpih(&fpi1, status);
4771 result.getAllFieldPositions(fpih, status);
4774 FieldPositionIterator fpi2;
4775 format->format(514.23, sb.remove(), &fpi2, status);
4777 assertTrue("Should produce same field position iterator", fpi1 == fpi2);
4780 void NumberFormatterApiTest::errors() {
4781 LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).precision(
4782 Precision::fixedFraction(
4786 UErrorCode status = U_ZERO_ERROR;
4787 FormattedNumber fn = lnf.formatInt(1, status);
4789 "Should fail in formatInt method with error code for rounding",
4790 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4794 status = U_ZERO_ERROR;
4795 fn = lnf.formatDouble(1.0, status);
4797 "Should fail in formatDouble method with error code for rounding",
4798 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4801 // formatDecimal (decimal error)
4802 status = U_ZERO_ERROR;
4803 fn = NumberFormatter::withLocale("en").formatDecimal("1x2", status);
4805 "Should fail in formatDecimal method with error code for decimal number syntax",
4806 U_DECIMAL_NUMBER_SYNTAX_ERROR,
4809 // formatDecimal (setting error)
4810 status = U_ZERO_ERROR;
4811 fn = lnf.formatDecimal("1.0", status);
4813 "Should fail in formatDecimal method with error code for rounding",
4814 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4818 status = U_ZERO_ERROR;
4819 UnicodeString output = lnf.toSkeleton(status);
4821 "Should fail on toSkeleton terminal method with correct error code",
4822 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4825 "Terminal toSkeleton on error object should be bogus",
4828 // FieldPosition (constrained category)
4829 status = U_ZERO_ERROR;
4830 ConstrainedFieldPosition fp;
4831 fp.constrainCategory(UFIELD_CATEGORY_NUMBER);
4832 fn.nextPosition(fp, status);
4834 "Should fail on FieldPosition terminal method with correct error code",
4835 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4838 // FieldPositionIterator (no constraints)
4839 status = U_ZERO_ERROR;
4841 fn.nextPosition(fp, status);
4843 "Should fail on FieldPositoinIterator terminal method with correct error code",
4844 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4848 status = U_ZERO_ERROR;
4849 UnicodeStringAppendable appendable(output.remove());
4850 fn.appendTo(appendable, status);
4852 "Should fail on Appendable terminal method with correct error code",
4853 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4857 status = U_ZERO_ERROR;
4858 output = fn.toString(status);
4860 "Should fail on UnicodeString terminal method with correct error code",
4861 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4864 "Terminal UnicodeString on error object should be bogus",
4868 status = U_ZERO_ERROR;
4869 lnf.copyErrorTo(status);
4871 "Should fail since rounder is not legal with correct error code",
4872 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4876 void NumberFormatterApiTest::validRanges() {
4878 #define EXPECTED_MAX_INT_FRAC_SIG 999
4880 #define VALID_RANGE_ASSERT(status, method, lowerBound, argument) UPRV_BLOCK_MACRO_BEGIN { \
4881 UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \
4883 : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \
4885 UnicodeString(u"Incorrect status for " #method " on input ") \
4886 + Int64ToUnicodeString(argument), \
4889 } UPRV_BLOCK_MACRO_END
4891 #define VALID_RANGE_ONEARG(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
4892 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
4893 UErrorCode status = U_ZERO_ERROR; \
4894 NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \
4895 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
4897 } UPRV_BLOCK_MACRO_END
4899 #define VALID_RANGE_TWOARGS(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
4900 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
4901 UErrorCode status = U_ZERO_ERROR; \
4902 /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \
4903 NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \
4904 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
4905 status = U_ZERO_ERROR; \
4906 /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \
4907 NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \
4908 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
4909 /* Check that first argument must be less than or equal to second argument */ \
4910 NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \
4911 assertEquals("Incorrect status for " #method " on max < min input", \
4912 U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \
4915 } UPRV_BLOCK_MACRO_END
4917 VALID_RANGE_ONEARG(precision, Precision::fixedFraction, 0);
4918 VALID_RANGE_ONEARG(precision, Precision::minFraction, 0);
4919 VALID_RANGE_ONEARG(precision, Precision::maxFraction, 0);
4920 VALID_RANGE_TWOARGS(precision, Precision::minMaxFraction, 0);
4921 VALID_RANGE_ONEARG(precision, Precision::fixedSignificantDigits, 1);
4922 VALID_RANGE_ONEARG(precision, Precision::minSignificantDigits, 1);
4923 VALID_RANGE_ONEARG(precision, Precision::maxSignificantDigits, 1);
4924 VALID_RANGE_TWOARGS(precision, Precision::minMaxSignificantDigits, 1);
4925 VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMinDigits, 1);
4926 VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMaxDigits, 1);
4927 VALID_RANGE_ONEARG(notation, Notation::scientific().withMinExponentDigits, 1);
4928 VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo, 0);
4929 VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo(0).truncateAt, -1);
4932 void NumberFormatterApiTest::copyMove() {
4933 IcuTestErrorCode status(*this, "copyMove");
4935 // Default constructors
4936 LocalizedNumberFormatter l1;
4937 assertEquals("Initial behavior", u"10", l1.formatInt(10, status).toString(status), true);
4938 if (status.errDataIfFailureAndReset()) { return; }
4939 assertEquals("Initial call count", 1, l1.getCallCount());
4940 assertTrue("Initial compiled", l1.getCompiled() == nullptr);
4943 l1 = NumberFormatter::withLocale("en").unit(NoUnit::percent()).threshold(3);
4944 assertEquals("Initial behavior", u"10%", l1.formatInt(10, status).toString(status));
4945 assertEquals("Initial call count", 1, l1.getCallCount());
4946 assertTrue("Initial compiled", l1.getCompiled() == nullptr);
4947 l1.formatInt(123, status);
4948 assertEquals("Still not compiled", 2, l1.getCallCount());
4949 assertTrue("Still not compiled", l1.getCompiled() == nullptr);
4950 l1.formatInt(123, status);
4951 assertEquals("Compiled", u"10%", l1.formatInt(10, status).toString(status));
4952 assertEquals("Compiled", INT32_MIN, l1.getCallCount());
4953 assertTrue("Compiled", l1.getCompiled() != nullptr);
4956 LocalizedNumberFormatter l2 = l1;
4957 assertEquals("[constructor] Copy behavior", u"10%", l2.formatInt(10, status).toString(status));
4958 assertEquals("[constructor] Copy should not have compiled state", 1, l2.getCallCount());
4959 assertTrue("[constructor] Copy should not have compiled state", l2.getCompiled() == nullptr);
4962 LocalizedNumberFormatter l3 = std::move(l1);
4963 assertEquals("[constructor] Move behavior", u"10%", l3.formatInt(10, status).toString(status));
4964 assertEquals("[constructor] Move *should* have compiled state", INT32_MIN, l3.getCallCount());
4965 assertTrue("[constructor] Move *should* have compiled state", l3.getCompiled() != nullptr);
4966 assertEquals("[constructor] Source should be reset after move", 0, l1.getCallCount());
4967 assertTrue("[constructor] Source should be reset after move", l1.getCompiled() == nullptr);
4969 // Reset l1 and l2 to check for macro-props copying for behavior testing
4970 // Make the test more interesting: also warm them up with a compiled formatter.
4971 l1 = NumberFormatter::withLocale("en");
4972 l1.formatInt(1, status);
4973 l1.formatInt(1, status);
4974 l1.formatInt(1, status);
4975 l2 = NumberFormatter::withLocale("en");
4976 l2.formatInt(1, status);
4977 l2.formatInt(1, status);
4978 l2.formatInt(1, status);
4982 assertEquals("[assignment] Copy behavior", u"10%", l1.formatInt(10, status).toString(status));
4983 assertEquals("[assignment] Copy should not have compiled state", 1, l1.getCallCount());
4984 assertTrue("[assignment] Copy should not have compiled state", l1.getCompiled() == nullptr);
4988 assertEquals("[assignment] Move behavior", u"10%", l2.formatInt(10, status).toString(status));
4989 assertEquals("[assignment] Move *should* have compiled state", INT32_MIN, l2.getCallCount());
4990 assertTrue("[assignment] Move *should* have compiled state", l2.getCompiled() != nullptr);
4991 assertEquals("[assignment] Source should be reset after move", 0, l3.getCallCount());
4992 assertTrue("[assignment] Source should be reset after move", l3.getCompiled() == nullptr);
4994 // Coverage tests for UnlocalizedNumberFormatter
4995 UnlocalizedNumberFormatter u1;
4996 assertEquals("Default behavior", u"10", u1.locale("en").formatInt(10, status).toString(status));
4997 u1 = u1.unit(NoUnit::percent());
4998 assertEquals("Copy assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
4999 UnlocalizedNumberFormatter u2 = u1;
5000 assertEquals("Copy constructor", u"10%", u2.locale("en").formatInt(10, status).toString(status));
5001 UnlocalizedNumberFormatter u3 = std::move(u1);
5002 assertEquals("Move constructor", u"10%", u3.locale("en").formatInt(10, status).toString(status));
5003 u1 = NumberFormatter::with();
5005 assertEquals("Move assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
5007 // FormattedNumber move operators
5008 FormattedNumber result = l1.formatInt(10, status);
5009 assertEquals("FormattedNumber move constructor", u"10%", result.toString(status));
5010 result = l1.formatInt(20, status);
5011 assertEquals("FormattedNumber move assignment", u"20%", result.toString(status));
5014 void NumberFormatterApiTest::localPointerCAPI() {
5015 // NOTE: This is also the sample code in unumberformatter.h
5016 UErrorCode ec = U_ZERO_ERROR;
5019 LocalUNumberFormatterPointer uformatter(unumf_openForSkeletonAndLocale(u"percent", -1, "en", &ec));
5020 LocalUFormattedNumberPointer uresult(unumf_openResult(&ec));
5021 if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
5023 // Format a decimal number:
5024 unumf_formatDecimal(uformatter.getAlias(), "9.87E-3", -1, uresult.getAlias(), &ec);
5025 if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
5027 // Get the location of the percent sign:
5028 UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
5029 unumf_resultNextFieldPosition(uresult.getAlias(), &ufpos, &ec);
5030 assertEquals("Percent sign location within '0.00987%'", 7, ufpos.beginIndex);
5031 assertEquals("Percent sign location within '0.00987%'", 8, ufpos.endIndex);
5033 // No need to do any cleanup since we are using LocalPointer.
5036 void NumberFormatterApiTest::toObject() {
5037 IcuTestErrorCode status(*this, "toObject");
5039 // const lvalue version
5041 LocalizedNumberFormatter lnf = NumberFormatter::withLocale("en")
5042 .precision(Precision::fixedFraction(2));
5043 LocalPointer<LocalizedNumberFormatter> lnf2(lnf.clone());
5044 assertFalse("should create successfully, const lvalue", lnf2.isNull());
5045 assertEquals("object API test, const lvalue", u"1,000.00",
5046 lnf2->formatDouble(1000, status).toString(status));
5049 // rvalue reference version
5051 LocalPointer<LocalizedNumberFormatter> lnf(
5052 NumberFormatter::withLocale("en")
5053 .precision(Precision::fixedFraction(2))
5055 assertFalse("should create successfully, rvalue reference", lnf.isNull());
5056 assertEquals("object API test, rvalue reference", u"1,000.00",
5057 lnf->formatDouble(1000, status).toString(status));
5060 // to std::unique_ptr via constructor
5062 std::unique_ptr<LocalizedNumberFormatter> lnf(
5063 NumberFormatter::withLocale("en")
5064 .precision(Precision::fixedFraction(2))
5066 assertTrue("should create successfully, unique_ptr", static_cast<bool>(lnf));
5067 assertEquals("object API test, unique_ptr", u"1,000.00",
5068 lnf->formatDouble(1000, status).toString(status));
5071 // to std::unique_ptr via assignment
5073 std::unique_ptr<LocalizedNumberFormatter> lnf =
5074 NumberFormatter::withLocale("en")
5075 .precision(Precision::fixedFraction(2))
5077 assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf));
5078 assertEquals("object API test, unique_ptr B", u"1,000.00",
5079 lnf->formatDouble(1000, status).toString(status));
5082 // to LocalPointer via assignment
5084 LocalPointer<UnlocalizedNumberFormatter> f =
5085 NumberFormatter::with().clone();
5088 // make sure no memory leaks
5090 NumberFormatter::with().clone();
5094 void NumberFormatterApiTest::toDecimalNumber() {
5095 IcuTestErrorCode status(*this, "toDecimalNumber");
5096 FormattedNumber fn = NumberFormatter::withLocale("bn-BD")
5097 .scale(Scale::powerOfTen(2))
5098 .precision(Precision::maxSignificantDigits(5))
5099 .formatDouble(9.87654321e12, status);
5100 assertEquals("Should have expected localized string result",
5101 u"৯৮,৭৬,৫০,০০,০০,০০,০০০", fn.toString(status));
5102 assertEquals(u"Should have expected toDecimalNumber string result",
5103 "9.8765E+14", fn.toDecimalNumber<std::string>(status).c_str());
5106 void NumberFormatterApiTest::microPropsInternals() {
5107 // Verify copy construction and assignment operators.
5108 int64_t testValues[2] = {4, 61};
5111 assertEquals("capacity", 2, mp.mixedMeasures.getCapacity());
5112 mp.mixedMeasures[0] = testValues[0];
5113 mp.mixedMeasures[1] = testValues[1];
5114 MicroProps copyConstructed(mp);
5115 MicroProps copyAssigned;
5116 int64_t *resizeResult = mp.mixedMeasures.resize(4, 4);
5117 assertTrue("Resize success", resizeResult != NULL);
5120 assertTrue("MicroProps success status", U_SUCCESS(mp.mixedMeasures.status));
5121 assertTrue("Copy Constructed success status", U_SUCCESS(copyConstructed.mixedMeasures.status));
5122 assertTrue("Copy Assigned success status", U_SUCCESS(copyAssigned.mixedMeasures.status));
5123 assertEquals("Original values[0]", testValues[0], mp.mixedMeasures[0]);
5124 assertEquals("Original values[1]", testValues[1], mp.mixedMeasures[1]);
5125 assertEquals("Copy Constructed[0]", testValues[0], copyConstructed.mixedMeasures[0]);
5126 assertEquals("Copy Constructed[1]", testValues[1], copyConstructed.mixedMeasures[1]);
5127 assertEquals("Copy Assigned[0]", testValues[0], copyAssigned.mixedMeasures[0]);
5128 assertEquals("Copy Assigned[1]", testValues[1], copyAssigned.mixedMeasures[1]);
5129 assertEquals("Original capacity", 4, mp.mixedMeasures.getCapacity());
5130 assertEquals("Copy Constructed capacity", 2, copyConstructed.mixedMeasures.getCapacity());
5131 assertEquals("Copy Assigned capacity", 4, copyAssigned.mixedMeasures.getCapacity());
5134 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
5135 * `conciseSkeleton` against the normalized version of `uskeleton` - this does
5136 * not round-trip uskeleton itself.
5138 * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
5140 * If `uskeleton` is nullptr, toSkeleton is expected to return an
5141 * U_UNSUPPORTED_ERROR.
5143 void NumberFormatterApiTest::assertFormatDescending(
5144 const char16_t* umessage,
5145 const char16_t* uskeleton,
5146 const char16_t* conciseSkeleton,
5147 const UnlocalizedNumberFormatter& f,
5151 va_start(args, locale);
5152 UnicodeString message(TRUE, umessage, -1);
5153 static double inputs[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
5154 const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
5155 const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
5156 IcuTestErrorCode status(*this, "assertFormatDescending");
5157 status.setScope(message);
5158 UnicodeString expecteds[10];
5159 for (int16_t i = 0; i < 9; i++) {
5160 char16_t caseNumber = u'0' + i;
5161 double d = inputs[i];
5162 UnicodeString expected = va_arg(args, const char16_t*);
5163 expecteds[i] = expected;
5164 UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
5165 assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
5166 assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
5167 UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
5168 assertSuccess(message + u": Safe Path: " + caseNumber, status);
5169 assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
5171 if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
5172 UnicodeString skeleton(TRUE, uskeleton, -1);
5173 // Only compare normalized skeletons: the tests need not provide the normalized forms.
5174 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
5175 UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
5176 assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
5177 LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
5178 for (int32_t i = 0; i < 9; i++) {
5179 double d = inputs[i];
5180 UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
5181 assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
5183 // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
5184 // If the concise skeleton starts with '~', disable the round-trip check.
5185 bool shouldRoundTrip = true;
5186 if (conciseSkeleton[0] == u'~') {
5188 shouldRoundTrip = false;
5190 LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
5191 if (shouldRoundTrip) {
5192 assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
5194 for (int32_t i = 0; i < 9; i++) {
5195 double d = inputs[i];
5196 UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
5197 assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
5200 assertUndefinedSkeleton(f);
5204 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
5205 * `conciseSkeleton` against the normalized version of `uskeleton` - this does
5206 * not round-trip uskeleton itself.
5208 * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
5210 * If `uskeleton` is nullptr, toSkeleton is expected to return an
5211 * U_UNSUPPORTED_ERROR.
5213 void NumberFormatterApiTest::assertFormatDescendingBig(
5214 const char16_t* umessage,
5215 const char16_t* uskeleton,
5216 const char16_t* conciseSkeleton,
5217 const UnlocalizedNumberFormatter& f,
5221 va_start(args, locale);
5222 UnicodeString message(TRUE, umessage, -1);
5223 static double inputs[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
5224 const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
5225 const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
5226 IcuTestErrorCode status(*this, "assertFormatDescendingBig");
5227 status.setScope(message);
5228 UnicodeString expecteds[10];
5229 for (int16_t i = 0; i < 9; i++) {
5230 char16_t caseNumber = u'0' + i;
5231 double d = inputs[i];
5232 UnicodeString expected = va_arg(args, const char16_t*);
5233 expecteds[i] = expected;
5234 UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
5235 assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
5236 assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
5237 UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
5238 assertSuccess(message + u": Safe Path: " + caseNumber, status);
5239 assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
5241 if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
5242 UnicodeString skeleton(TRUE, uskeleton, -1);
5243 // Only compare normalized skeletons: the tests need not provide the normalized forms.
5244 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
5245 UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
5246 assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
5247 LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
5248 for (int32_t i = 0; i < 9; i++) {
5249 double d = inputs[i];
5250 UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
5251 assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
5253 // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
5254 // If the concise skeleton starts with '~', disable the round-trip check.
5255 bool shouldRoundTrip = true;
5256 if (conciseSkeleton[0] == u'~') {
5258 shouldRoundTrip = false;
5260 LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
5261 if (shouldRoundTrip) {
5262 assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
5264 for (int32_t i = 0; i < 9; i++) {
5265 double d = inputs[i];
5266 UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
5267 assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
5270 assertUndefinedSkeleton(f);
5274 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
5275 * `conciseSkeleton` against the normalized version of `uskeleton` - this does
5276 * not round-trip uskeleton itself.
5278 * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
5280 * If `uskeleton` is nullptr, toSkeleton is expected to return an
5281 * U_UNSUPPORTED_ERROR.
5284 NumberFormatterApiTest::assertFormatSingle(
5285 const char16_t* umessage,
5286 const char16_t* uskeleton,
5287 const char16_t* conciseSkeleton,
5288 const UnlocalizedNumberFormatter& f,
5291 const UnicodeString& expected) {
5292 UnicodeString message(TRUE, umessage, -1);
5293 const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
5294 const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
5295 IcuTestErrorCode status(*this, "assertFormatSingle");
5296 status.setScope(message);
5297 FormattedNumber result1 = l1.formatDouble(input, status);
5298 UnicodeString actual1 = result1.toString(status);
5299 assertSuccess(message + u": Unsafe Path", status);
5300 assertEquals(message + u": Unsafe Path", expected, actual1);
5301 UnicodeString actual2 = l2.formatDouble(input, status).toString(status);
5302 assertSuccess(message + u": Safe Path", status);
5303 assertEquals(message + u": Safe Path", expected, actual2);
5304 if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
5305 UnicodeString skeleton(TRUE, uskeleton, -1);
5306 // Only compare normalized skeletons: the tests need not provide the normalized forms.
5307 // Use the normalized form to construct the testing formatter to ensure no loss of info.
5308 UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
5309 assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
5310 LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
5311 UnicodeString actual3 = l3.formatDouble(input, status).toString(status);
5312 assertEquals(message + ": Skeleton Path: '" + normalized + "': " + input, expected, actual3);
5313 // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
5314 // If the concise skeleton starts with '~', disable the round-trip check.
5315 bool shouldRoundTrip = true;
5316 if (conciseSkeleton[0] == u'~') {
5318 shouldRoundTrip = false;
5320 LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
5321 if (shouldRoundTrip) {
5322 assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
5324 UnicodeString actual4 = l4.formatDouble(input, status).toString(status);
5325 assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + input, expected, actual4);
5327 assertUndefinedSkeleton(f);
5332 void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter& f) {
5333 UErrorCode status = U_ZERO_ERROR;
5334 UnicodeString skeleton = f.toSkeleton(status);
5336 u"Expect toSkeleton to fail, but passed, producing: " + skeleton,
5337 U_UNSUPPORTED_ERROR,
5341 void NumberFormatterApiTest::assertNumberFieldPositions(
5342 const char16_t* message,
5343 const FormattedNumber& formattedNumber,
5344 const UFieldPosition* expectedFieldPositions,
5346 IcuTestErrorCode status(*this, "assertNumberFieldPositions");
5348 // Check FormattedValue functions
5349 checkFormattedValue(
5351 static_cast<const FormattedValue&>(formattedNumber),
5352 formattedNumber.toString(status),
5353 UFIELD_CATEGORY_NUMBER,
5354 expectedFieldPositions,
5359 #endif /* #if !UCONFIG_NO_FORMATTING */