• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 #include "charstr.h"
9 #include <cstdarg>
10 #include <cmath>
11 #include <memory>
12 #include "unicode/unum.h"
13 #include "unicode/numberformatter.h"
14 #include "number_asformat.h"
15 #include "number_types.h"
16 #include "number_utils.h"
17 #include "numbertest.h"
18 #include "unicode/utypes.h"
19 #include "number_utypes.h"
20 
21 using number::impl::UFormattedNumberData;
22 
23 // Horrible workaround for the lack of a status code in the constructor...
24 // (Also affects numbertest_range.cpp)
25 UErrorCode globalNumberFormatterApiTestStatus = U_ZERO_ERROR;
26 
NumberFormatterApiTest()27 NumberFormatterApiTest::NumberFormatterApiTest()
28         : NumberFormatterApiTest(globalNumberFormatterApiTestStatus) {
29 }
30 
NumberFormatterApiTest(UErrorCode & status)31 NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode& status)
32         : USD(u"USD", status),
33           GBP(u"GBP", status),
34           CZK(u"CZK", status),
35           CAD(u"CAD", status),
36           ESP(u"ESP", status),
37           PTE(u"PTE", status),
38           RON(u"RON", status),
39           CNY(u"CNY", status),
40           FRENCH_SYMBOLS(Locale::getFrench(), status),
41           SWISS_SYMBOLS(Locale("de-CH"), status),
42           MYANMAR_SYMBOLS(Locale("my"), status) {
43 
44     // Check for error on the first MeasureUnit in case there is no data
45     LocalPointer<MeasureUnit> unit(MeasureUnit::createMeter(status));
46     if (U_FAILURE(status)) {
47         dataerrln("%s %d status = %s", __FILE__, __LINE__, u_errorName(status));
48         return;
49     }
50     METER = *unit;
51 
52     DAY = *LocalPointer<MeasureUnit>(MeasureUnit::createDay(status));
53     SQUARE_METER = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMeter(status));
54     FAHRENHEIT = *LocalPointer<MeasureUnit>(MeasureUnit::createFahrenheit(status));
55     SECOND = *LocalPointer<MeasureUnit>(MeasureUnit::createSecond(status));
56     POUND = *LocalPointer<MeasureUnit>(MeasureUnit::createPound(status));
57     SQUARE_MILE = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMile(status));
58     JOULE = *LocalPointer<MeasureUnit>(MeasureUnit::createJoule(status));
59     FURLONG = *LocalPointer<MeasureUnit>(MeasureUnit::createFurlong(status));
60     KELVIN = *LocalPointer<MeasureUnit>(MeasureUnit::createKelvin(status));
61 
62     MATHSANB = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("mathsanb", status));
63     LATN = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("latn", status));
64 }
65 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)66 void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
67     if (exec) {
68         logln("TestSuite NumberFormatterApiTest: ");
69     }
70     TESTCASE_AUTO_BEGIN;
71         TESTCASE_AUTO(notationSimple);
72         TESTCASE_AUTO(notationScientific);
73         TESTCASE_AUTO(notationCompact);
74         TESTCASE_AUTO(unitMeasure);
75         TESTCASE_AUTO(unitCompoundMeasure);
76         TESTCASE_AUTO(unitCurrency);
77         TESTCASE_AUTO(unitPercent);
78         if (!quick) {
79             // Slow test: run in exhaustive mode only
80             TESTCASE_AUTO(percentParity);
81         }
82         TESTCASE_AUTO(roundingFraction);
83         TESTCASE_AUTO(roundingFigures);
84         TESTCASE_AUTO(roundingFractionFigures);
85         TESTCASE_AUTO(roundingOther);
86         TESTCASE_AUTO(grouping);
87         TESTCASE_AUTO(padding);
88         TESTCASE_AUTO(integerWidth);
89         TESTCASE_AUTO(symbols);
90         // TODO: Add this method if currency symbols override support is added.
91         //TESTCASE_AUTO(symbolsOverride);
92         TESTCASE_AUTO(sign);
93         TESTCASE_AUTO(signNearZero);
94         TESTCASE_AUTO(signCoverage);
95         TESTCASE_AUTO(decimal);
96         TESTCASE_AUTO(scale);
97         TESTCASE_AUTO(locale);
98         TESTCASE_AUTO(skeletonUserGuideExamples);
99         TESTCASE_AUTO(formatTypes);
100         TESTCASE_AUTO(fieldPositionLogic);
101         TESTCASE_AUTO(fieldPositionCoverage);
102         TESTCASE_AUTO(toFormat);
103         TESTCASE_AUTO(errors);
104         if (!quick) {
105             // Slow test: run in exhaustive mode only
106             // (somewhat slow to check all permutations of settings)
107             TESTCASE_AUTO(validRanges);
108         }
109         TESTCASE_AUTO(copyMove);
110         TESTCASE_AUTO(localPointerCAPI);
111         TESTCASE_AUTO(toObject);
112         TESTCASE_AUTO(toDecimalNumber);
113     TESTCASE_AUTO_END;
114 }
115 
notationSimple()116 void NumberFormatterApiTest::notationSimple() {
117     assertFormatDescending(
118             u"Basic",
119             u"",
120             u"",
121             NumberFormatter::with(),
122             Locale::getEnglish(),
123             u"87,650",
124             u"8,765",
125             u"876.5",
126             u"87.65",
127             u"8.765",
128             u"0.8765",
129             u"0.08765",
130             u"0.008765",
131             u"0");
132 
133     assertFormatDescendingBig(
134             u"Big Simple",
135             u"notation-simple",
136             u"",
137             NumberFormatter::with().notation(Notation::simple()),
138             Locale::getEnglish(),
139             u"87,650,000",
140             u"8,765,000",
141             u"876,500",
142             u"87,650",
143             u"8,765",
144             u"876.5",
145             u"87.65",
146             u"8.765",
147             u"0");
148 
149     assertFormatSingle(
150             u"Basic with Negative Sign",
151             u"",
152             u"",
153             NumberFormatter::with(),
154             Locale::getEnglish(),
155             -9876543.21,
156             u"-9,876,543.21");
157 }
158 
159 
notationScientific()160 void NumberFormatterApiTest::notationScientific() {
161     assertFormatDescending(
162             u"Scientific",
163             u"scientific",
164             u"E0",
165             NumberFormatter::with().notation(Notation::scientific()),
166             Locale::getEnglish(),
167             u"8.765E4",
168             u"8.765E3",
169             u"8.765E2",
170             u"8.765E1",
171             u"8.765E0",
172             u"8.765E-1",
173             u"8.765E-2",
174             u"8.765E-3",
175             u"0E0");
176 
177     assertFormatDescending(
178             u"Engineering",
179             u"engineering",
180             u"EE0",
181             NumberFormatter::with().notation(Notation::engineering()),
182             Locale::getEnglish(),
183             u"87.65E3",
184             u"8.765E3",
185             u"876.5E0",
186             u"87.65E0",
187             u"8.765E0",
188             u"876.5E-3",
189             u"87.65E-3",
190             u"8.765E-3",
191             u"0E0");
192 
193     assertFormatDescending(
194             u"Scientific sign always shown",
195             u"scientific/sign-always",
196             u"E+!0",
197             NumberFormatter::with().notation(
198                     Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS)),
199             Locale::getEnglish(),
200             u"8.765E+4",
201             u"8.765E+3",
202             u"8.765E+2",
203             u"8.765E+1",
204             u"8.765E+0",
205             u"8.765E-1",
206             u"8.765E-2",
207             u"8.765E-3",
208             u"0E+0");
209 
210     assertFormatDescending(
211             u"Scientific min exponent digits",
212             u"scientific/*ee",
213             u"E00",
214             NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)),
215             Locale::getEnglish(),
216             u"8.765E04",
217             u"8.765E03",
218             u"8.765E02",
219             u"8.765E01",
220             u"8.765E00",
221             u"8.765E-01",
222             u"8.765E-02",
223             u"8.765E-03",
224             u"0E00");
225 
226     assertFormatSingle(
227             u"Scientific Negative",
228             u"scientific",
229             u"E0",
230             NumberFormatter::with().notation(Notation::scientific()),
231             Locale::getEnglish(),
232             -1000000,
233             u"-1E6");
234 
235     assertFormatSingle(
236             u"Scientific Infinity",
237             u"scientific",
238             u"E0",
239             NumberFormatter::with().notation(Notation::scientific()),
240             Locale::getEnglish(),
241             -uprv_getInfinity(),
242             u"-∞");
243 
244     assertFormatSingle(
245             u"Scientific NaN",
246             u"scientific",
247             u"E0",
248             NumberFormatter::with().notation(Notation::scientific()),
249             Locale::getEnglish(),
250             uprv_getNaN(),
251             u"NaN");
252 }
253 
notationCompact()254 void NumberFormatterApiTest::notationCompact() {
255     assertFormatDescending(
256             u"Compact Short",
257             u"compact-short",
258             u"K",
259             NumberFormatter::with().notation(Notation::compactShort()),
260             Locale::getEnglish(),
261             u"88K",
262             u"8.8K",
263             u"876",
264             u"88",
265             u"8.8",
266             u"0.88",
267             u"0.088",
268             u"0.0088",
269             u"0");
270 
271     assertFormatDescending(
272             u"Compact Long",
273             u"compact-long",
274             u"KK",
275             NumberFormatter::with().notation(Notation::compactLong()),
276             Locale::getEnglish(),
277             u"88 thousand",
278             u"8.8 thousand",
279             u"876",
280             u"88",
281             u"8.8",
282             u"0.88",
283             u"0.088",
284             u"0.0088",
285             u"0");
286 
287     assertFormatDescending(
288             u"Compact Short Currency",
289             u"compact-short currency/USD",
290             u"K currency/USD",
291             NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
292             Locale::getEnglish(),
293             u"$88K",
294             u"$8.8K",
295             u"$876",
296             u"$88",
297             u"$8.8",
298             u"$0.88",
299             u"$0.088",
300             u"$0.0088",
301             u"$0");
302 
303     assertFormatDescending(
304             u"Compact Short with ISO Currency",
305             u"compact-short currency/USD unit-width-iso-code",
306             u"K currency/USD unit-width-iso-code",
307             NumberFormatter::with().notation(Notation::compactShort())
308                     .unit(USD)
309                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
310             Locale::getEnglish(),
311             u"USD 88K",
312             u"USD 8.8K",
313             u"USD 876",
314             u"USD 88",
315             u"USD 8.8",
316             u"USD 0.88",
317             u"USD 0.088",
318             u"USD 0.0088",
319             u"USD 0");
320 
321     assertFormatDescending(
322             u"Compact Short with Long Name Currency",
323             u"compact-short currency/USD unit-width-full-name",
324             u"K currency/USD unit-width-full-name",
325             NumberFormatter::with().notation(Notation::compactShort())
326                     .unit(USD)
327                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
328             Locale::getEnglish(),
329             u"88K US dollars",
330             u"8.8K US dollars",
331             u"876 US dollars",
332             u"88 US dollars",
333             u"8.8 US dollars",
334             u"0.88 US dollars",
335             u"0.088 US dollars",
336             u"0.0088 US dollars",
337             u"0 US dollars");
338 
339     // Note: Most locales don't have compact long currency, so this currently falls back to short.
340     // This test case should be fixed when proper compact long currency patterns are added.
341     assertFormatDescending(
342             u"Compact Long Currency",
343             u"compact-long currency/USD",
344             u"KK currency/USD",
345             NumberFormatter::with().notation(Notation::compactLong()).unit(USD),
346             Locale::getEnglish(),
347             u"$88K", // should be something like "$88 thousand"
348             u"$8.8K",
349             u"$876",
350             u"$88",
351             u"$8.8",
352             u"$0.88",
353             u"$0.088",
354             u"$0.0088",
355             u"$0");
356 
357     // Note: Most locales don't have compact long currency, so this currently falls back to short.
358     // This test case should be fixed when proper compact long currency patterns are added.
359     assertFormatDescending(
360             u"Compact Long with ISO Currency",
361             u"compact-long currency/USD unit-width-iso-code",
362             u"KK currency/USD unit-width-iso-code",
363             NumberFormatter::with().notation(Notation::compactLong())
364                     .unit(USD)
365                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
366             Locale::getEnglish(),
367             u"USD 88K", // should be something like "USD 88 thousand"
368             u"USD 8.8K",
369             u"USD 876",
370             u"USD 88",
371             u"USD 8.8",
372             u"USD 0.88",
373             u"USD 0.088",
374             u"USD 0.0088",
375             u"USD 0");
376 
377     // TODO: This behavior could be improved and should be revisited.
378     assertFormatDescending(
379             u"Compact Long with Long Name Currency",
380             u"compact-long currency/USD unit-width-full-name",
381             u"KK currency/USD unit-width-full-name",
382             NumberFormatter::with().notation(Notation::compactLong())
383                     .unit(USD)
384                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
385             Locale::getEnglish(),
386             u"88 thousand US dollars",
387             u"8.8 thousand US dollars",
388             u"876 US dollars",
389             u"88 US dollars",
390             u"8.8 US dollars",
391             u"0.88 US dollars",
392             u"0.088 US dollars",
393             u"0.0088 US dollars",
394             u"0 US dollars");
395 
396     assertFormatSingle(
397             u"Compact Plural One",
398             u"compact-long",
399             u"KK",
400             NumberFormatter::with().notation(Notation::compactLong()),
401             Locale::createFromName("es"),
402             1000000,
403             u"1 millón");
404 
405     assertFormatSingle(
406             u"Compact Plural Other",
407             u"compact-long",
408             u"KK",
409             NumberFormatter::with().notation(Notation::compactLong()),
410             Locale::createFromName("es"),
411             2000000,
412             u"2 millones");
413 
414     assertFormatSingle(
415             u"Compact with Negative Sign",
416             u"compact-short",
417             u"K",
418             NumberFormatter::with().notation(Notation::compactShort()),
419             Locale::getEnglish(),
420             -9876543.21,
421             u"-9.9M");
422 
423     assertFormatSingle(
424             u"Compact Rounding",
425             u"compact-short",
426             u"K",
427             NumberFormatter::with().notation(Notation::compactShort()),
428             Locale::getEnglish(),
429             990000,
430             u"990K");
431 
432     assertFormatSingle(
433             u"Compact Rounding",
434             u"compact-short",
435             u"K",
436             NumberFormatter::with().notation(Notation::compactShort()),
437             Locale::getEnglish(),
438             999000,
439             u"999K");
440 
441     assertFormatSingle(
442             u"Compact Rounding",
443             u"compact-short",
444             u"K",
445             NumberFormatter::with().notation(Notation::compactShort()),
446             Locale::getEnglish(),
447             999900,
448             u"1M");
449 
450     assertFormatSingle(
451             u"Compact Rounding",
452             u"compact-short",
453             u"K",
454             NumberFormatter::with().notation(Notation::compactShort()),
455             Locale::getEnglish(),
456             9900000,
457             u"9.9M");
458 
459     assertFormatSingle(
460             u"Compact Rounding",
461             u"compact-short",
462             u"K",
463             NumberFormatter::with().notation(Notation::compactShort()),
464             Locale::getEnglish(),
465             9990000,
466             u"10M");
467 
468     assertFormatSingle(
469             u"Compact in zh-Hant-HK",
470             u"compact-short",
471             u"K",
472             NumberFormatter::with().notation(Notation::compactShort()),
473             Locale("zh-Hant-HK"),
474             1e7,
475             u"10M");
476 
477     assertFormatSingle(
478             u"Compact in zh-Hant",
479             u"compact-short",
480             u"K",
481             NumberFormatter::with().notation(Notation::compactShort()),
482             Locale("zh-Hant"),
483             1e7,
484             u"1000\u842C");
485 
486     assertFormatSingle(
487             u"Compact Infinity",
488             u"compact-short",
489             u"K",
490             NumberFormatter::with().notation(Notation::compactShort()),
491             Locale::getEnglish(),
492             -uprv_getInfinity(),
493             u"-∞");
494 
495     assertFormatSingle(
496             u"Compact NaN",
497             u"compact-short",
498             u"K",
499             NumberFormatter::with().notation(Notation::compactShort()),
500             Locale::getEnglish(),
501             uprv_getNaN(),
502             u"NaN");
503 
504     // NOTE: There is no API for compact custom data in C++
505     // and thus no "Compact Somali No Figure" test
506 }
507 
unitMeasure()508 void NumberFormatterApiTest::unitMeasure() {
509     assertFormatDescending(
510             u"Meters Short and unit() method",
511             u"measure-unit/length-meter",
512             u"unit/meter",
513             NumberFormatter::with().unit(MeasureUnit::getMeter()),
514             Locale::getEnglish(),
515             u"87,650 m",
516             u"8,765 m",
517             u"876.5 m",
518             u"87.65 m",
519             u"8.765 m",
520             u"0.8765 m",
521             u"0.08765 m",
522             u"0.008765 m",
523             u"0 m");
524 
525     assertFormatDescending(
526             u"Meters Long and adoptUnit() method",
527             u"measure-unit/length-meter unit-width-full-name",
528             u"unit/meter unit-width-full-name",
529             NumberFormatter::with().adoptUnit(new MeasureUnit(METER))
530                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
531             Locale::getEnglish(),
532             u"87,650 meters",
533             u"8,765 meters",
534             u"876.5 meters",
535             u"87.65 meters",
536             u"8.765 meters",
537             u"0.8765 meters",
538             u"0.08765 meters",
539             u"0.008765 meters",
540             u"0 meters");
541 
542     assertFormatDescending(
543             u"Compact Meters Long",
544             u"compact-long measure-unit/length-meter unit-width-full-name",
545             u"KK unit/meter unit-width-full-name",
546             NumberFormatter::with().notation(Notation::compactLong())
547                     .unit(METER)
548                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
549             Locale::getEnglish(),
550             u"88 thousand meters",
551             u"8.8 thousand meters",
552             u"876 meters",
553             u"88 meters",
554             u"8.8 meters",
555             u"0.88 meters",
556             u"0.088 meters",
557             u"0.0088 meters",
558             u"0 meters");
559 
560 //    TODO: Implement Measure in C++
561 //    assertFormatSingleMeasure(
562 //            u"Meters with Measure Input",
563 //            NumberFormatter::with().unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
564 //            Locale::getEnglish(),
565 //            new Measure(5.43, new MeasureUnit(METER)),
566 //            u"5.43 meters");
567 
568 //    TODO: Implement Measure in C++
569 //    assertFormatSingleMeasure(
570 //            u"Measure format method takes precedence over fluent chain",
571 //            NumberFormatter::with().unit(METER),
572 //            Locale::getEnglish(),
573 //            new Measure(5.43, USD),
574 //            u"$5.43");
575 
576     assertFormatSingle(
577             u"Meters with Negative Sign",
578             u"measure-unit/length-meter",
579             u"unit/meter",
580             NumberFormatter::with().unit(METER),
581             Locale::getEnglish(),
582             -9876543.21,
583             u"-9,876,543.21 m");
584 
585     // The locale string "सान" appears only in brx.txt:
586     assertFormatSingle(
587             u"Interesting Data Fallback 1",
588             u"measure-unit/duration-day unit-width-full-name",
589             u"unit/day unit-width-full-name",
590             NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
591             Locale::createFromName("brx"),
592             5.43,
593             u"5.43 सान");
594 
595     // Requires following the alias from unitsNarrow to unitsShort:
596     assertFormatSingle(
597             u"Interesting Data Fallback 2",
598             u"measure-unit/duration-day unit-width-narrow",
599             u"unit/day unit-width-narrow",
600             NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
601             Locale::createFromName("brx"),
602             5.43,
603             u"5.43 d");
604 
605     // en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
606     // requiring fallback to the root.
607     assertFormatSingle(
608             u"Interesting Data Fallback 3",
609             u"measure-unit/area-square-meter unit-width-narrow",
610             u"unit/square-meter unit-width-narrow",
611             NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
612             Locale::createFromName("en-GB"),
613             5.43,
614             u"5.43m²");
615 
616     // Try accessing a narrow unit directly from root.
617     assertFormatSingle(
618             u"Interesting Data Fallback 4",
619             u"measure-unit/area-square-meter unit-width-narrow",
620             u"unit/square-meter unit-width-narrow",
621             NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
622             Locale::createFromName("root"),
623             5.43,
624             u"5.43 m²");
625 
626     // es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
627     // NOTE: This example is in the documentation.
628     assertFormatSingle(
629             u"Difference between Narrow and Short (Narrow Version)",
630             u"measure-unit/temperature-fahrenheit unit-width-narrow",
631             u"unit/fahrenheit unit-width-narrow",
632             NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_NARROW),
633             Locale("es-US"),
634             5.43,
635             u"5.43°");
636 
637     assertFormatSingle(
638             u"Difference between Narrow and Short (Short Version)",
639             u"measure-unit/temperature-fahrenheit unit-width-short",
640             u"unit/fahrenheit unit-width-short",
641             NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_SHORT),
642             Locale("es-US"),
643             5.43,
644             u"5.43 °F");
645 
646     assertFormatSingle(
647             u"MeasureUnit form without {0} in CLDR pattern",
648             u"measure-unit/temperature-kelvin unit-width-full-name",
649             u"unit/kelvin unit-width-full-name",
650             NumberFormatter::with().unit(KELVIN).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
651             Locale("es-MX"),
652             1,
653             u"kelvin");
654 
655     assertFormatSingle(
656             u"MeasureUnit form without {0} in CLDR pattern and wide base form",
657             u"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
658             u"unit/kelvin .00000000000000000000 unit-width-full-name",
659             NumberFormatter::with().precision(Precision::fixedFraction(20))
660                     .unit(KELVIN)
661                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
662             Locale("es-MX"),
663             1,
664             u"kelvin");
665 
666     assertFormatSingle(
667             u"Person unit not in short form",
668             u"measure-unit/duration-year-person unit-width-full-name",
669             u"unit/year-person unit-width-full-name",
670             NumberFormatter::with().unit(MeasureUnit::getYearPerson())
671                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
672             Locale("es-MX"),
673             5,
674             u"5 a\u00F1os");
675 }
676 
unitCompoundMeasure()677 void NumberFormatterApiTest::unitCompoundMeasure() {
678     assertFormatDescending(
679             u"Meters Per Second Short (unit that simplifies) and perUnit method",
680             u"measure-unit/length-meter per-measure-unit/duration-second",
681             u"unit/meter-per-second",
682             NumberFormatter::with().unit(METER).perUnit(SECOND),
683             Locale::getEnglish(),
684             u"87,650 m/s",
685             u"8,765 m/s",
686             u"876.5 m/s",
687             u"87.65 m/s",
688             u"8.765 m/s",
689             u"0.8765 m/s",
690             u"0.08765 m/s",
691             u"0.008765 m/s",
692             u"0 m/s");
693 
694     assertFormatDescending(
695             u"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method",
696             u"measure-unit/mass-pound per-measure-unit/area-square-mile",
697             u"unit/pound-per-square-mile",
698             NumberFormatter::with().unit(POUND).adoptPerUnit(new MeasureUnit(SQUARE_MILE)),
699             Locale::getEnglish(),
700             u"87,650 lb/mi²",
701             u"8,765 lb/mi²",
702             u"876.5 lb/mi²",
703             u"87.65 lb/mi²",
704             u"8.765 lb/mi²",
705             u"0.8765 lb/mi²",
706             u"0.08765 lb/mi²",
707             u"0.008765 lb/mi²",
708             u"0 lb/mi²");
709 
710     assertFormatDescending(
711             u"Joules Per Furlong Short (unit with no simplifications or special patterns)",
712             u"measure-unit/energy-joule per-measure-unit/length-furlong",
713             u"unit/joule-per-furlong",
714             NumberFormatter::with().unit(JOULE).perUnit(FURLONG),
715             Locale::getEnglish(),
716             u"87,650 J/fur",
717             u"8,765 J/fur",
718             u"876.5 J/fur",
719             u"87.65 J/fur",
720             u"8.765 J/fur",
721             u"0.8765 J/fur",
722             u"0.08765 J/fur",
723             u"0.008765 J/fur",
724             u"0 J/fur");
725 
726     // TODO(ICU-20941): Support constructions such as this one.
727     // assertFormatDescending(
728     //         u"Joules Per Furlong Short with unit identifier via API",
729     //         u"measure-unit/energy-joule per-measure-unit/length-furlong",
730     //         u"unit/joule-per-furlong",
731     //         NumberFormatter::with().unit(MeasureUnit::forIdentifier("joule-per-furlong", status)),
732     //         Locale::getEnglish(),
733     //         u"87,650 J/fur",
734     //         u"8,765 J/fur",
735     //         u"876.5 J/fur",
736     //         u"87.65 J/fur",
737     //         u"8.765 J/fur",
738     //         u"0.8765 J/fur",
739     //         u"0.08765 J/fur",
740     //         u"0.008765 J/fur",
741     //         u"0 J/fur");
742 }
743 
unitCurrency()744 void NumberFormatterApiTest::unitCurrency() {
745     assertFormatDescending(
746             u"Currency",
747             u"currency/GBP",
748             u"currency/GBP",
749             NumberFormatter::with().unit(GBP),
750             Locale::getEnglish(),
751             u"£87,650.00",
752             u"£8,765.00",
753             u"£876.50",
754             u"£87.65",
755             u"£8.76",
756             u"£0.88",
757             u"£0.09",
758             u"£0.01",
759             u"£0.00");
760 
761     assertFormatDescending(
762             u"Currency ISO",
763             u"currency/GBP unit-width-iso-code",
764             u"currency/GBP unit-width-iso-code",
765             NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
766             Locale::getEnglish(),
767             u"GBP 87,650.00",
768             u"GBP 8,765.00",
769             u"GBP 876.50",
770             u"GBP 87.65",
771             u"GBP 8.76",
772             u"GBP 0.88",
773             u"GBP 0.09",
774             u"GBP 0.01",
775             u"GBP 0.00");
776 
777     assertFormatDescending(
778             u"Currency Long Name",
779             u"currency/GBP unit-width-full-name",
780             u"currency/GBP unit-width-full-name",
781             NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
782             Locale::getEnglish(),
783             u"87,650.00 British pounds",
784             u"8,765.00 British pounds",
785             u"876.50 British pounds",
786             u"87.65 British pounds",
787             u"8.76 British pounds",
788             u"0.88 British pounds",
789             u"0.09 British pounds",
790             u"0.01 British pounds",
791             u"0.00 British pounds");
792 
793     assertFormatDescending(
794             u"Currency Hidden",
795             u"currency/GBP unit-width-hidden",
796             u"currency/GBP unit-width-hidden",
797             NumberFormatter::with().unit(GBP).unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
798             Locale::getEnglish(),
799             u"87,650.00",
800             u"8,765.00",
801             u"876.50",
802             u"87.65",
803             u"8.76",
804             u"0.88",
805             u"0.09",
806             u"0.01",
807             u"0.00");
808 
809 //    TODO: Implement Measure in C++
810 //    assertFormatSingleMeasure(
811 //            u"Currency with CurrencyAmount Input",
812 //            NumberFormatter::with(),
813 //            Locale::getEnglish(),
814 //            new CurrencyAmount(5.43, GBP),
815 //            u"£5.43");
816 
817 //    TODO: Enable this test when DecimalFormat wrapper is done.
818 //    assertFormatSingle(
819 //            u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat(
820 //                    PatternStringParser.parseToProperties("0 ¤¤¤"),
821 //                    DecimalFormatSymbols.getInstance(Locale::getEnglish()),
822 //                    null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds");
823 
824     assertFormatSingle(
825             u"Currency with Negative Sign",
826             u"currency/GBP",
827             u"currency/GBP",
828             NumberFormatter::with().unit(GBP),
829             Locale::getEnglish(),
830             -9876543.21,
831             u"-£9,876,543.21");
832 
833     // The full currency symbol is not shown in NARROW format.
834     // NOTE: This example is in the documentation.
835     assertFormatSingle(
836             u"Currency Difference between Narrow and Short (Narrow Version)",
837             u"currency/USD unit-width-narrow",
838             u"currency/USD unit-width-narrow",
839             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_NARROW),
840             Locale("en-CA"),
841             5.43,
842             u"$5.43");
843 
844     assertFormatSingle(
845             u"Currency Difference between Narrow and Short (Short Version)",
846             u"currency/USD unit-width-short",
847             u"currency/USD unit-width-short",
848             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
849             Locale("en-CA"),
850             5.43,
851             u"US$5.43");
852 
853     assertFormatSingle(
854             u"Currency-dependent format (Control)",
855             u"currency/USD unit-width-short",
856             u"currency/USD unit-width-short",
857             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
858             Locale("ca"),
859             444444.55,
860             u"444.444,55 USD");
861 
862     assertFormatSingle(
863             u"Currency-dependent format (Test)",
864             u"currency/ESP unit-width-short",
865             u"currency/ESP unit-width-short",
866             NumberFormatter::with().unit(ESP).unitWidth(UNUM_UNIT_WIDTH_SHORT),
867             Locale("ca"),
868             444444.55,
869             u"₧ 444.445");
870 
871     assertFormatSingle(
872             u"Currency-dependent symbols (Control)",
873             u"currency/USD unit-width-short",
874             u"currency/USD unit-width-short",
875             NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
876             Locale("pt-PT"),
877             444444.55,
878             u"444 444,55 US$");
879 
880     // NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero-
881     // width space), and they set the decimal separator to the $ symbol.
882     assertFormatSingle(
883             u"Currency-dependent symbols (Test Short)",
884             u"currency/PTE unit-width-short",
885             u"currency/PTE unit-width-short",
886             NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_SHORT),
887             Locale("pt-PT"),
888             444444.55,
889             u"444,444$55 \u200B");
890 
891     assertFormatSingle(
892             u"Currency-dependent symbols (Test Narrow)",
893             u"currency/PTE unit-width-narrow",
894             u"currency/PTE unit-width-narrow",
895             NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_NARROW),
896             Locale("pt-PT"),
897             444444.55,
898             u"444,444$55 \u200B");
899 
900     assertFormatSingle(
901             u"Currency-dependent symbols (Test ISO Code)",
902             u"currency/PTE unit-width-iso-code",
903             u"currency/PTE unit-width-iso-code",
904             NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
905             Locale("pt-PT"),
906             444444.55,
907             u"444,444$55 PTE");
908 
909     assertFormatSingle(
910             u"Plural form depending on visible digits (ICU-20499)",
911             u"currency/RON unit-width-full-name",
912             u"currency/RON unit-width-full-name",
913             NumberFormatter::with().unit(RON).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
914             Locale("ro-RO"),
915             24,
916             u"24,00 lei românești");
917 
918     assertFormatSingle(
919             u"Currency spacing in suffix (ICU-20954)",
920             u"currency/CNY",
921             u"currency/CNY",
922             NumberFormatter::with().unit(CNY),
923             Locale("lu"),
924             123.12,
925             u"123,12 CN¥");
926 }
927 
unitPercent()928 void NumberFormatterApiTest::unitPercent() {
929     assertFormatDescending(
930             u"Percent",
931             u"percent",
932             u"%",
933             NumberFormatter::with().unit(NoUnit::percent()),
934             Locale::getEnglish(),
935             u"87,650%",
936             u"8,765%",
937             u"876.5%",
938             u"87.65%",
939             u"8.765%",
940             u"0.8765%",
941             u"0.08765%",
942             u"0.008765%",
943             u"0%");
944 
945     assertFormatDescending(
946             u"Permille",
947             u"permille",
948             u"permille",
949             NumberFormatter::with().unit(NoUnit::permille()),
950             Locale::getEnglish(),
951             u"87,650‰",
952             u"8,765‰",
953             u"876.5‰",
954             u"87.65‰",
955             u"8.765‰",
956             u"0.8765‰",
957             u"0.08765‰",
958             u"0.008765‰",
959             u"0‰");
960 
961     assertFormatSingle(
962             u"NoUnit Base",
963             u"base-unit",
964             u"",
965             NumberFormatter::with().unit(NoUnit::base()),
966             Locale::getEnglish(),
967             51423,
968             u"51,423");
969 
970     assertFormatSingle(
971             u"Percent with Negative Sign",
972             u"percent",
973             u"%",
974             NumberFormatter::with().unit(NoUnit::percent()),
975             Locale::getEnglish(),
976             -98.7654321,
977             u"-98.765432%");
978 }
979 
percentParity()980 void NumberFormatterApiTest::percentParity() {
981     IcuTestErrorCode status(*this, "percentParity");
982     UnlocalizedNumberFormatter uNoUnitPercent = NumberFormatter::with().unit(NoUnit::percent());
983     UnlocalizedNumberFormatter uNoUnitPermille = NumberFormatter::with().unit(NoUnit::permille());
984     UnlocalizedNumberFormatter uMeasurePercent = NumberFormatter::with().unit(MeasureUnit::getPercent());
985     UnlocalizedNumberFormatter uMeasurePermille = NumberFormatter::with().unit(MeasureUnit::getPermille());
986 
987     int32_t localeCount;
988     auto locales = Locale::getAvailableLocales(localeCount);
989     for (int32_t i=0; i<localeCount; i++) {
990         auto& locale = locales[i];
991         UnicodeString sNoUnitPercent = uNoUnitPercent.locale(locale)
992             .formatDouble(50, status).toString(status);
993         UnicodeString sNoUnitPermille = uNoUnitPermille.locale(locale)
994             .formatDouble(50, status).toString(status);
995         UnicodeString sMeasurePercent = uMeasurePercent.locale(locale)
996             .formatDouble(50, status).toString(status);
997         UnicodeString sMeasurePermille = uMeasurePermille.locale(locale)
998             .formatDouble(50, status).toString(status);
999 
1000         assertEquals(u"Percent, locale " + UnicodeString(locale.getName()),
1001             sNoUnitPercent, sMeasurePercent);
1002         assertEquals(u"Permille, locale " + UnicodeString(locale.getName()),
1003             sNoUnitPermille, sMeasurePermille);
1004     }
1005 }
1006 
roundingFraction()1007 void NumberFormatterApiTest::roundingFraction() {
1008     assertFormatDescending(
1009             u"Integer",
1010             u"precision-integer",
1011             u".",
1012             NumberFormatter::with().precision(Precision::integer()),
1013             Locale::getEnglish(),
1014             u"87,650",
1015             u"8,765",
1016             u"876",
1017             u"88",
1018             u"9",
1019             u"1",
1020             u"0",
1021             u"0",
1022             u"0");
1023 
1024     assertFormatDescending(
1025             u"Fixed Fraction",
1026             u".000",
1027             u".000",
1028             NumberFormatter::with().precision(Precision::fixedFraction(3)),
1029             Locale::getEnglish(),
1030             u"87,650.000",
1031             u"8,765.000",
1032             u"876.500",
1033             u"87.650",
1034             u"8.765",
1035             u"0.876",
1036             u"0.088",
1037             u"0.009",
1038             u"0.000");
1039 
1040     assertFormatDescending(
1041             u"Min Fraction",
1042             u".0*",
1043             u".0+",
1044             NumberFormatter::with().precision(Precision::minFraction(1)),
1045             Locale::getEnglish(),
1046             u"87,650.0",
1047             u"8,765.0",
1048             u"876.5",
1049             u"87.65",
1050             u"8.765",
1051             u"0.8765",
1052             u"0.08765",
1053             u"0.008765",
1054             u"0.0");
1055 
1056     assertFormatDescending(
1057             u"Max Fraction",
1058             u".#",
1059             u".#",
1060             NumberFormatter::with().precision(Precision::maxFraction(1)),
1061             Locale::getEnglish(),
1062             u"87,650",
1063             u"8,765",
1064             u"876.5",
1065             u"87.6",
1066             u"8.8",
1067             u"0.9",
1068             u"0.1",
1069             u"0",
1070             u"0");
1071 
1072     assertFormatDescending(
1073             u"Min/Max Fraction",
1074             u".0##",
1075             u".0##",
1076             NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)),
1077             Locale::getEnglish(),
1078             u"87,650.0",
1079             u"8,765.0",
1080             u"876.5",
1081             u"87.65",
1082             u"8.765",
1083             u"0.876",
1084             u"0.088",
1085             u"0.009",
1086             u"0.0");
1087 }
1088 
roundingFigures()1089 void NumberFormatterApiTest::roundingFigures() {
1090     assertFormatSingle(
1091             u"Fixed Significant",
1092             u"@@@",
1093             u"@@@",
1094             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
1095             Locale::getEnglish(),
1096             -98,
1097             u"-98.0");
1098 
1099     assertFormatSingle(
1100             u"Fixed Significant Rounding",
1101             u"@@@",
1102             u"@@@",
1103             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
1104             Locale::getEnglish(),
1105             -98.7654321,
1106             u"-98.8");
1107 
1108     assertFormatSingle(
1109             u"Fixed Significant Zero",
1110             u"@@@",
1111             u"@@@",
1112             NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
1113             Locale::getEnglish(),
1114             0,
1115             u"0.00");
1116 
1117     assertFormatSingle(
1118             u"Min Significant",
1119             u"@@*",
1120             u"@@+",
1121             NumberFormatter::with().precision(Precision::minSignificantDigits(2)),
1122             Locale::getEnglish(),
1123             -9,
1124             u"-9.0");
1125 
1126     assertFormatSingle(
1127             u"Max Significant",
1128             u"@###",
1129             u"@###",
1130             NumberFormatter::with().precision(Precision::maxSignificantDigits(4)),
1131             Locale::getEnglish(),
1132             98.7654321,
1133             u"98.77");
1134 
1135     assertFormatSingle(
1136             u"Min/Max Significant",
1137             u"@@@#",
1138             u"@@@#",
1139             NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)),
1140             Locale::getEnglish(),
1141             9.99999,
1142             u"10.0");
1143 
1144     assertFormatSingle(
1145             u"Fixed Significant on zero with lots of integer width",
1146             u"@ integer-width/+000",
1147             u"@ 000",
1148             NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
1149                     .integerWidth(IntegerWidth::zeroFillTo(3)),
1150             Locale::getEnglish(),
1151             0,
1152             "000");
1153 
1154     assertFormatSingle(
1155             u"Fixed Significant on zero with zero integer width",
1156             u"@ integer-width/*",
1157             u"@ integer-width/+",
1158             NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
1159                     .integerWidth(IntegerWidth::zeroFillTo(0)),
1160             Locale::getEnglish(),
1161             0,
1162             "0");
1163 }
1164 
roundingFractionFigures()1165 void NumberFormatterApiTest::roundingFractionFigures() {
1166     assertFormatDescending(
1167             u"Basic Significant", // for comparison
1168             u"@#",
1169             u"@#",
1170             NumberFormatter::with().precision(Precision::maxSignificantDigits(2)),
1171             Locale::getEnglish(),
1172             u"88,000",
1173             u"8,800",
1174             u"880",
1175             u"88",
1176             u"8.8",
1177             u"0.88",
1178             u"0.088",
1179             u"0.0088",
1180             u"0");
1181 
1182     assertFormatDescending(
1183             u"FracSig minMaxFrac minSig",
1184             u".0#/@@@*",
1185             u".0#/@@@+",
1186             NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)),
1187             Locale::getEnglish(),
1188             u"87,650.0",
1189             u"8,765.0",
1190             u"876.5",
1191             u"87.65",
1192             u"8.76",
1193             u"0.876", // minSig beats maxFrac
1194             u"0.0876", // minSig beats maxFrac
1195             u"0.00876", // minSig beats maxFrac
1196             u"0.0");
1197 
1198     assertFormatDescending(
1199             u"FracSig minMaxFrac maxSig A",
1200             u".0##/@#",
1201             u".0##/@#",
1202             NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)),
1203             Locale::getEnglish(),
1204             u"88,000.0", // maxSig beats maxFrac
1205             u"8,800.0", // maxSig beats maxFrac
1206             u"880.0", // maxSig beats maxFrac
1207             u"88.0", // maxSig beats maxFrac
1208             u"8.8", // maxSig beats maxFrac
1209             u"0.88", // maxSig beats maxFrac
1210             u"0.088",
1211             u"0.009",
1212             u"0.0");
1213 
1214     assertFormatDescending(
1215             u"FracSig minMaxFrac maxSig B",
1216             u".00/@#",
1217             u".00/@#",
1218             NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)),
1219             Locale::getEnglish(),
1220             u"88,000.00", // maxSig beats maxFrac
1221             u"8,800.00", // maxSig beats maxFrac
1222             u"880.00", // maxSig beats maxFrac
1223             u"88.00", // maxSig beats maxFrac
1224             u"8.80", // maxSig beats maxFrac
1225             u"0.88",
1226             u"0.09",
1227             u"0.01",
1228             u"0.00");
1229 
1230     assertFormatSingle(
1231             u"FracSig with trailing zeros A",
1232             u".00/@@@*",
1233             u".00/@@@+",
1234             NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
1235             Locale::getEnglish(),
1236             0.1,
1237             u"0.10");
1238 
1239     assertFormatSingle(
1240             u"FracSig with trailing zeros B",
1241             u".00/@@@*",
1242             u".00/@@@+",
1243             NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
1244             Locale::getEnglish(),
1245             0.0999999,
1246             u"0.10");
1247 }
1248 
roundingOther()1249 void NumberFormatterApiTest::roundingOther() {
1250     assertFormatDescending(
1251             u"Rounding None",
1252             u"precision-unlimited",
1253             u".+",
1254             NumberFormatter::with().precision(Precision::unlimited()),
1255             Locale::getEnglish(),
1256             u"87,650",
1257             u"8,765",
1258             u"876.5",
1259             u"87.65",
1260             u"8.765",
1261             u"0.8765",
1262             u"0.08765",
1263             u"0.008765",
1264             u"0");
1265 
1266     assertFormatDescending(
1267             u"Increment",
1268             u"precision-increment/0.5",
1269             u"precision-increment/0.5",
1270             NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)),
1271             Locale::getEnglish(),
1272             u"87,650.0",
1273             u"8,765.0",
1274             u"876.5",
1275             u"87.5",
1276             u"9.0",
1277             u"1.0",
1278             u"0.0",
1279             u"0.0",
1280             u"0.0");
1281 
1282     assertFormatDescending(
1283             u"Increment with Min Fraction",
1284             u"precision-increment/0.50",
1285             u"precision-increment/0.50",
1286             NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)),
1287             Locale::getEnglish(),
1288             u"87,650.00",
1289             u"8,765.00",
1290             u"876.50",
1291             u"87.50",
1292             u"9.00",
1293             u"1.00",
1294             u"0.00",
1295             u"0.00",
1296             u"0.00");
1297 
1298     assertFormatDescending(
1299             u"Strange Increment",
1300             u"precision-increment/3.140",
1301             u"precision-increment/3.140",
1302             NumberFormatter::with().precision(Precision::increment(3.14).withMinFraction(3)),
1303             Locale::getEnglish(),
1304             u"87,649.960",
1305             u"8,763.740",
1306             u"876.060",
1307             u"87.920",
1308             u"9.420",
1309             u"0.000",
1310             u"0.000",
1311             u"0.000",
1312             u"0.000");
1313 
1314     assertFormatDescending(
1315             u"Increment Resolving to Power of 10",
1316             u"precision-increment/0.010",
1317             u"precision-increment/0.010",
1318             NumberFormatter::with().precision(Precision::increment(0.01).withMinFraction(3)),
1319             Locale::getEnglish(),
1320             u"87,650.000",
1321             u"8,765.000",
1322             u"876.500",
1323             u"87.650",
1324             u"8.760",
1325             u"0.880",
1326             u"0.090",
1327             u"0.010",
1328             u"0.000");
1329 
1330     assertFormatDescending(
1331             u"Currency Standard",
1332             u"currency/CZK precision-currency-standard",
1333             u"currency/CZK precision-currency-standard",
1334             NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD))
1335                     .unit(CZK),
1336             Locale::getEnglish(),
1337             u"CZK 87,650.00",
1338             u"CZK 8,765.00",
1339             u"CZK 876.50",
1340             u"CZK 87.65",
1341             u"CZK 8.76",
1342             u"CZK 0.88",
1343             u"CZK 0.09",
1344             u"CZK 0.01",
1345             u"CZK 0.00");
1346 
1347     assertFormatDescending(
1348             u"Currency Cash",
1349             u"currency/CZK precision-currency-cash",
1350             u"currency/CZK precision-currency-cash",
1351             NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
1352                     .unit(CZK),
1353             Locale::getEnglish(),
1354             u"CZK 87,650",
1355             u"CZK 8,765",
1356             u"CZK 876",
1357             u"CZK 88",
1358             u"CZK 9",
1359             u"CZK 1",
1360             u"CZK 0",
1361             u"CZK 0",
1362             u"CZK 0");
1363 
1364     assertFormatDescending(
1365             u"Currency Cash with Nickel Rounding",
1366             u"currency/CAD precision-currency-cash",
1367             u"currency/CAD precision-currency-cash",
1368             NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
1369                     .unit(CAD),
1370             Locale::getEnglish(),
1371             u"CA$87,650.00",
1372             u"CA$8,765.00",
1373             u"CA$876.50",
1374             u"CA$87.65",
1375             u"CA$8.75",
1376             u"CA$0.90",
1377             u"CA$0.10",
1378             u"CA$0.00",
1379             u"CA$0.00");
1380 
1381     assertFormatDescending(
1382             u"Currency not in top-level fluent chain",
1383             u"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
1384             u".",
1385             NumberFormatter::with().precision(
1386                     Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH).withCurrency(CZK)),
1387             Locale::getEnglish(),
1388             u"87,650",
1389             u"8,765",
1390             u"876",
1391             u"88",
1392             u"9",
1393             u"1",
1394             u"0",
1395             u"0",
1396             u"0");
1397 
1398     // NOTE: Other tests cover the behavior of the other rounding modes.
1399     assertFormatDescending(
1400             u"Rounding Mode CEILING",
1401             u"precision-integer rounding-mode-ceiling",
1402             u". rounding-mode-ceiling",
1403             NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING),
1404             Locale::getEnglish(),
1405             u"87,650",
1406             u"8,765",
1407             u"877",
1408             u"88",
1409             u"9",
1410             u"1",
1411             u"1",
1412             u"1",
1413             u"0");
1414 
1415     assertFormatSingle(
1416             u"ICU-20974 Double.MIN_NORMAL",
1417             u"scientific",
1418             u"E0",
1419             NumberFormatter::with().notation(Notation::scientific()),
1420             Locale::getEnglish(),
1421             DBL_MIN,
1422             u"2.225074E-308");
1423 
1424 #ifndef DBL_TRUE_MIN
1425 #define DBL_TRUE_MIN 4.9E-324
1426 #endif
1427 
1428     // Note: this behavior is intentionally different from Java; see
1429     // https://github.com/google/double-conversion/issues/126
1430     assertFormatSingle(
1431             u"ICU-20974 Double.MIN_VALUE",
1432             u"scientific",
1433             u"E0",
1434             NumberFormatter::with().notation(Notation::scientific()),
1435             Locale::getEnglish(),
1436             DBL_TRUE_MIN,
1437             u"5E-324");
1438 }
1439 
grouping()1440 void NumberFormatterApiTest::grouping() {
1441     assertFormatDescendingBig(
1442             u"Western Grouping",
1443             u"group-auto",
1444             u"",
1445             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
1446             Locale::getEnglish(),
1447             u"87,650,000",
1448             u"8,765,000",
1449             u"876,500",
1450             u"87,650",
1451             u"8,765",
1452             u"876.5",
1453             u"87.65",
1454             u"8.765",
1455             u"0");
1456 
1457     assertFormatDescendingBig(
1458             u"Indic Grouping",
1459             u"group-auto",
1460             u"",
1461             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
1462             Locale("en-IN"),
1463             u"8,76,50,000",
1464             u"87,65,000",
1465             u"8,76,500",
1466             u"87,650",
1467             u"8,765",
1468             u"876.5",
1469             u"87.65",
1470             u"8.765",
1471             u"0");
1472 
1473     assertFormatDescendingBig(
1474             u"Western Grouping, Min 2",
1475             u"group-min2",
1476             u",?",
1477             NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
1478             Locale::getEnglish(),
1479             u"87,650,000",
1480             u"8,765,000",
1481             u"876,500",
1482             u"87,650",
1483             u"8765",
1484             u"876.5",
1485             u"87.65",
1486             u"8.765",
1487             u"0");
1488 
1489     assertFormatDescendingBig(
1490             u"Indic Grouping, Min 2",
1491             u"group-min2",
1492             u",?",
1493             NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
1494             Locale("en-IN"),
1495             u"8,76,50,000",
1496             u"87,65,000",
1497             u"8,76,500",
1498             u"87,650",
1499             u"8765",
1500             u"876.5",
1501             u"87.65",
1502             u"8.765",
1503             u"0");
1504 
1505     assertFormatDescendingBig(
1506             u"No Grouping",
1507             u"group-off",
1508             u",_",
1509             NumberFormatter::with().grouping(UNUM_GROUPING_OFF),
1510             Locale("en-IN"),
1511             u"87650000",
1512             u"8765000",
1513             u"876500",
1514             u"87650",
1515             u"8765",
1516             u"876.5",
1517             u"87.65",
1518             u"8.765",
1519             u"0");
1520 
1521     assertFormatDescendingBig(
1522             u"Indic locale with THOUSANDS grouping",
1523             u"group-thousands",
1524             u"group-thousands",
1525             NumberFormatter::with().grouping(UNUM_GROUPING_THOUSANDS),
1526             Locale("en-IN"),
1527             u"87,650,000",
1528             u"8,765,000",
1529             u"876,500",
1530             u"87,650",
1531             u"8,765",
1532             u"876.5",
1533             u"87.65",
1534             u"8.765",
1535             u"0");
1536 
1537     // NOTE: Polish is interesting because it has minimumGroupingDigits=2 in locale data
1538     // (Most locales have either 1 or 2)
1539     // If this test breaks due to data changes, find another locale that has minimumGroupingDigits.
1540     assertFormatDescendingBig(
1541             u"Polish Grouping",
1542             u"group-auto",
1543             u"",
1544             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
1545             Locale("pl"),
1546             u"87 650 000",
1547             u"8 765 000",
1548             u"876 500",
1549             u"87 650",
1550             u"8765",
1551             u"876,5",
1552             u"87,65",
1553             u"8,765",
1554             u"0");
1555 
1556     assertFormatDescendingBig(
1557             u"Polish Grouping, Min 2",
1558             u"group-min2",
1559             u",?",
1560             NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
1561             Locale("pl"),
1562             u"87 650 000",
1563             u"8 765 000",
1564             u"876 500",
1565             u"87 650",
1566             u"8765",
1567             u"876,5",
1568             u"87,65",
1569             u"8,765",
1570             u"0");
1571 
1572     assertFormatDescendingBig(
1573             u"Polish Grouping, Always",
1574             u"group-on-aligned",
1575             u",!",
1576             NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED),
1577             Locale("pl"),
1578             u"87 650 000",
1579             u"8 765 000",
1580             u"876 500",
1581             u"87 650",
1582             u"8 765",
1583             u"876,5",
1584             u"87,65",
1585             u"8,765",
1586             u"0");
1587 
1588     // NOTE: Bulgarian is interesting because it has no grouping in the default currency format.
1589     // If this test breaks due to data changes, find another locale that has no default grouping.
1590     assertFormatDescendingBig(
1591             u"Bulgarian Currency Grouping",
1592             u"currency/USD group-auto",
1593             u"currency/USD",
1594             NumberFormatter::with().grouping(UNUM_GROUPING_AUTO).unit(USD),
1595             Locale("bg"),
1596             u"87650000,00 щ.д.",
1597             u"8765000,00 щ.д.",
1598             u"876500,00 щ.д.",
1599             u"87650,00 щ.д.",
1600             u"8765,00 щ.д.",
1601             u"876,50 щ.д.",
1602             u"87,65 щ.д.",
1603             u"8,76 щ.д.",
1604             u"0,00 щ.д.");
1605 
1606     assertFormatDescendingBig(
1607             u"Bulgarian Currency Grouping, Always",
1608             u"currency/USD group-on-aligned",
1609             u"currency/USD ,!",
1610             NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED).unit(USD),
1611             Locale("bg"),
1612             u"87 650 000,00 щ.д.",
1613             u"8 765 000,00 щ.д.",
1614             u"876 500,00 щ.д.",
1615             u"87 650,00 щ.д.",
1616             u"8 765,00 щ.д.",
1617             u"876,50 щ.д.",
1618             u"87,65 щ.д.",
1619             u"8,76 щ.д.",
1620             u"0,00 щ.д.");
1621 
1622     MacroProps macros;
1623     macros.grouper = Grouper(4, 1, 3, UNUM_GROUPING_COUNT);
1624     assertFormatDescendingBig(
1625             u"Custom Grouping via Internal API",
1626             nullptr,
1627             nullptr,
1628             NumberFormatter::with().macros(macros),
1629             Locale::getEnglish(),
1630             u"8,7,6,5,0000",
1631             u"8,7,6,5000",
1632             u"876500",
1633             u"87650",
1634             u"8765",
1635             u"876.5",
1636             u"87.65",
1637             u"8.765",
1638             u"0");
1639 }
1640 
padding()1641 void NumberFormatterApiTest::padding() {
1642     assertFormatDescending(
1643             u"Padding",
1644             nullptr,
1645             nullptr,
1646             NumberFormatter::with().padding(Padder::none()),
1647             Locale::getEnglish(),
1648             u"87,650",
1649             u"8,765",
1650             u"876.5",
1651             u"87.65",
1652             u"8.765",
1653             u"0.8765",
1654             u"0.08765",
1655             u"0.008765",
1656             u"0");
1657 
1658     assertFormatDescending(
1659             u"Padding",
1660             nullptr,
1661             nullptr,
1662             NumberFormatter::with().padding(
1663                     Padder::codePoints(
1664                             '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
1665             Locale::getEnglish(),
1666             u"**87,650",
1667             u"***8,765",
1668             u"***876.5",
1669             u"***87.65",
1670             u"***8.765",
1671             u"**0.8765",
1672             u"*0.08765",
1673             u"0.008765",
1674             u"*******0");
1675 
1676     assertFormatDescending(
1677             u"Padding with code points",
1678             nullptr,
1679             nullptr,
1680             NumberFormatter::with().padding(
1681                     Padder::codePoints(
1682                             0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
1683             Locale::getEnglish(),
1684             u"����87,650",
1685             u"������8,765",
1686             u"������876.5",
1687             u"������87.65",
1688             u"������8.765",
1689             u"����0.8765",
1690             u"��0.08765",
1691             u"0.008765",
1692             u"��������������0");
1693 
1694     assertFormatDescending(
1695             u"Padding with wide digits",
1696             nullptr,
1697             nullptr,
1698             NumberFormatter::with().padding(
1699                             Padder::codePoints(
1700                                     '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX))
1701                     .adoptSymbols(new NumberingSystem(MATHSANB)),
1702             Locale::getEnglish(),
1703             u"**����,������",
1704             u"***��,������",
1705             u"***������.��",
1706             u"***����.����",
1707             u"***��.������",
1708             u"**��.��������",
1709             u"*��.����������",
1710             u"��.������������",
1711             u"*******��");
1712 
1713     assertFormatDescending(
1714             u"Padding with currency spacing",
1715             nullptr,
1716             nullptr,
1717             NumberFormatter::with().padding(
1718                             Padder::codePoints(
1719                                     '*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX))
1720                     .unit(GBP)
1721                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
1722             Locale::getEnglish(),
1723             u"GBP 87,650.00",
1724             u"GBP 8,765.00",
1725             u"GBP*876.50",
1726             u"GBP**87.65",
1727             u"GBP***8.76",
1728             u"GBP***0.88",
1729             u"GBP***0.09",
1730             u"GBP***0.01",
1731             u"GBP***0.00");
1732 
1733     assertFormatSingle(
1734             u"Pad Before Prefix",
1735             nullptr,
1736             nullptr,
1737             NumberFormatter::with().padding(
1738                     Padder::codePoints(
1739                             '*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX)),
1740             Locale::getEnglish(),
1741             -88.88,
1742             u"**-88.88");
1743 
1744     assertFormatSingle(
1745             u"Pad After Prefix",
1746             nullptr,
1747             nullptr,
1748             NumberFormatter::with().padding(
1749                     Padder::codePoints(
1750                             '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
1751             Locale::getEnglish(),
1752             -88.88,
1753             u"-**88.88");
1754 
1755     assertFormatSingle(
1756             u"Pad Before Suffix",
1757             nullptr,
1758             nullptr,
1759             NumberFormatter::with().padding(
1760                     Padder::codePoints(
1761                             '*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX)).unit(NoUnit::percent()),
1762             Locale::getEnglish(),
1763             88.88,
1764             u"88.88**%");
1765 
1766     assertFormatSingle(
1767             u"Pad After Suffix",
1768             nullptr,
1769             nullptr,
1770             NumberFormatter::with().padding(
1771                     Padder::codePoints(
1772                             '*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX)).unit(NoUnit::percent()),
1773             Locale::getEnglish(),
1774             88.88,
1775             u"88.88%**");
1776 
1777     assertFormatSingle(
1778             u"Currency Spacing with Zero Digit Padding Broken",
1779             nullptr,
1780             nullptr,
1781             NumberFormatter::with().padding(
1782                             Padder::codePoints(
1783                                     '0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX))
1784                     .unit(GBP)
1785                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
1786             Locale::getEnglish(),
1787             514.23,
1788             u"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
1789 }
1790 
integerWidth()1791 void NumberFormatterApiTest::integerWidth() {
1792     assertFormatDescending(
1793             u"Integer Width Default",
1794             u"integer-width/+0",
1795             u"0",
1796             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
1797             Locale::getEnglish(),
1798             u"87,650",
1799             u"8,765",
1800             u"876.5",
1801             u"87.65",
1802             u"8.765",
1803             u"0.8765",
1804             u"0.08765",
1805             u"0.008765",
1806             u"0");
1807 
1808     assertFormatDescending(
1809             u"Integer Width Zero Fill 0",
1810             u"integer-width/*",
1811             u"integer-width/+",
1812             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
1813             Locale::getEnglish(),
1814             u"87,650",
1815             u"8,765",
1816             u"876.5",
1817             u"87.65",
1818             u"8.765",
1819             u".8765",
1820             u".08765",
1821             u".008765",
1822             u"0");  // see ICU-20844
1823 
1824     assertFormatDescending(
1825             u"Integer Width Zero Fill 3",
1826             u"integer-width/+000",
1827             u"000",
1828             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
1829             Locale::getEnglish(),
1830             u"87,650",
1831             u"8,765",
1832             u"876.5",
1833             u"087.65",
1834             u"008.765",
1835             u"000.8765",
1836             u"000.08765",
1837             u"000.008765",
1838             u"000");
1839 
1840     assertFormatDescending(
1841             u"Integer Width Max 3",
1842             u"integer-width/##0",
1843             u"integer-width/##0",
1844             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
1845             Locale::getEnglish(),
1846             u"650",
1847             u"765",
1848             u"876.5",
1849             u"87.65",
1850             u"8.765",
1851             u"0.8765",
1852             u"0.08765",
1853             u"0.008765",
1854             u"0");
1855 
1856     assertFormatDescending(
1857             u"Integer Width Fixed 2",
1858             u"integer-width/00",
1859             u"integer-width/00",
1860             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1861             Locale::getEnglish(),
1862             u"50",
1863             u"65",
1864             u"76.5",
1865             u"87.65",
1866             u"08.765",
1867             u"00.8765",
1868             u"00.08765",
1869             u"00.008765",
1870             u"00");
1871 
1872     assertFormatDescending(
1873             u"Integer Width Compact",
1874             u"compact-short integer-width/000",
1875             u"compact-short integer-width/000",
1876             NumberFormatter::with()
1877                 .notation(Notation::compactShort())
1878                 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
1879             Locale::getEnglish(),
1880             u"088K",
1881             u"008.8K",
1882             u"876",
1883             u"088",
1884             u"008.8",
1885             u"000.88",
1886             u"000.088",
1887             u"000.0088",
1888             u"000");
1889 
1890     assertFormatDescending(
1891             u"Integer Width Scientific",
1892             u"scientific integer-width/000",
1893             u"scientific integer-width/000",
1894             NumberFormatter::with()
1895                 .notation(Notation::scientific())
1896                 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
1897             Locale::getEnglish(),
1898             u"008.765E4",
1899             u"008.765E3",
1900             u"008.765E2",
1901             u"008.765E1",
1902             u"008.765E0",
1903             u"008.765E-1",
1904             u"008.765E-2",
1905             u"008.765E-3",
1906             u"000E0");
1907 
1908     assertFormatDescending(
1909             u"Integer Width Engineering",
1910             u"engineering integer-width/000",
1911             u"engineering integer-width/000",
1912             NumberFormatter::with()
1913                 .notation(Notation::engineering())
1914                 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
1915             Locale::getEnglish(),
1916             u"087.65E3",
1917             u"008.765E3",
1918             u"876.5E0",
1919             u"087.65E0",
1920             u"008.765E0",
1921             u"876.5E-3",
1922             u"087.65E-3",
1923             u"008.765E-3",
1924             u"000E0");
1925 
1926     assertFormatSingle(
1927             u"Integer Width Remove All A",
1928             u"integer-width/00",
1929             u"integer-width/00",
1930             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1931             "en",
1932             2500,
1933             u"00");
1934 
1935     assertFormatSingle(
1936             u"Integer Width Remove All B",
1937             u"integer-width/00",
1938             u"integer-width/00",
1939             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1940             "en",
1941             25000,
1942             u"00");
1943 
1944     assertFormatSingle(
1945             u"Integer Width Remove All B, Bytes Mode",
1946             u"integer-width/00",
1947             u"integer-width/00",
1948             NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
1949             "en",
1950             // Note: this double produces all 17 significant digits
1951             10000000000000002000.0,
1952             u"00");
1953 }
1954 
symbols()1955 void NumberFormatterApiTest::symbols() {
1956     assertFormatDescending(
1957             u"French Symbols with Japanese Data 1",
1958             nullptr,
1959             nullptr,
1960             NumberFormatter::with().symbols(FRENCH_SYMBOLS),
1961             Locale::getJapan(),
1962             u"87\u202F650",
1963             u"8\u202F765",
1964             u"876,5",
1965             u"87,65",
1966             u"8,765",
1967             u"0,8765",
1968             u"0,08765",
1969             u"0,008765",
1970             u"0");
1971 
1972     assertFormatSingle(
1973             u"French Symbols with Japanese Data 2",
1974             nullptr,
1975             nullptr,
1976             NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS),
1977             Locale::getJapan(),
1978             12345,
1979             u"1,2\u4E07");
1980 
1981     assertFormatDescending(
1982             u"Latin Numbering System with Arabic Data",
1983             u"currency/USD latin",
1984             u"currency/USD latin",
1985             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
1986             Locale("ar"),
1987             u"US$ 87,650.00",
1988             u"US$ 8,765.00",
1989             u"US$ 876.50",
1990             u"US$ 87.65",
1991             u"US$ 8.76",
1992             u"US$ 0.88",
1993             u"US$ 0.09",
1994             u"US$ 0.01",
1995             u"US$ 0.00");
1996 
1997     assertFormatDescending(
1998             u"Math Numbering System with French Data",
1999             u"numbering-system/mathsanb",
2000             u"numbering-system/mathsanb",
2001             NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB)),
2002             Locale::getFrench(),
2003             u"����\u202F������",
2004             u"��\u202F������",
2005             u"������,��",
2006             u"����,����",
2007             u"��,������",
2008             u"��,��������",
2009             u"��,����������",
2010             u"��,������������",
2011             u"��");
2012 
2013     assertFormatSingle(
2014             u"Swiss Symbols (used in documentation)",
2015             nullptr,
2016             nullptr,
2017             NumberFormatter::with().symbols(SWISS_SYMBOLS),
2018             Locale::getEnglish(),
2019             12345.67,
2020             u"12’345.67");
2021 
2022     assertFormatSingle(
2023             u"Myanmar Symbols (used in documentation)",
2024             nullptr,
2025             nullptr,
2026             NumberFormatter::with().symbols(MYANMAR_SYMBOLS),
2027             Locale::getEnglish(),
2028             12345.67,
2029             u"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
2030 
2031     // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
2032 
2033     assertFormatSingle(
2034             u"Currency symbol should precede number in ar with NS latn",
2035             u"currency/USD latin",
2036             u"currency/USD latin",
2037             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
2038             Locale("ar"),
2039             12345.67,
2040             u"US$ 12,345.67");
2041 
2042     assertFormatSingle(
2043             u"Currency symbol should precede number in ar@numbers=latn",
2044             u"currency/USD",
2045             u"currency/USD",
2046             NumberFormatter::with().unit(USD),
2047             Locale("ar@numbers=latn"),
2048             12345.67,
2049             u"US$ 12,345.67");
2050 
2051     assertFormatSingle(
2052             u"Currency symbol should follow number in ar-EG with NS arab",
2053             u"currency/USD",
2054             u"currency/USD",
2055             NumberFormatter::with().unit(USD),
2056             Locale("ar-EG"),
2057             12345.67,
2058             u"١٢٬٣٤٥٫٦٧ US$");
2059 
2060     assertFormatSingle(
2061             u"Currency symbol should follow number in ar@numbers=arab",
2062             u"currency/USD",
2063             u"currency/USD",
2064             NumberFormatter::with().unit(USD),
2065             Locale("ar@numbers=arab"),
2066             12345.67,
2067             u"١٢٬٣٤٥٫٦٧ US$");
2068 
2069     assertFormatSingle(
2070             u"NumberingSystem in API should win over @numbers keyword",
2071             u"currency/USD latin",
2072             u"currency/USD latin",
2073             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
2074             Locale("ar@numbers=arab"),
2075             12345.67,
2076             u"US$ 12,345.67");
2077 
2078     UErrorCode status = U_ZERO_ERROR;
2079     assertEquals(
2080             "NumberingSystem in API should win over @numbers keyword in reverse order",
2081             u"US$ 12,345.67",
2082             NumberFormatter::withLocale(Locale("ar@numbers=arab")).adoptSymbols(new NumberingSystem(LATN))
2083                     .unit(USD)
2084                     .formatDouble(12345.67, status)
2085                     .toString(status));
2086 
2087     DecimalFormatSymbols symbols = SWISS_SYMBOLS;
2088     UnlocalizedNumberFormatter f = NumberFormatter::with().symbols(symbols);
2089     symbols.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol, u"!", status);
2090     assertFormatSingle(
2091             u"Symbols object should be copied",
2092             nullptr,
2093             nullptr,
2094             f,
2095             Locale::getEnglish(),
2096             12345.67,
2097             u"12’345.67");
2098 
2099     assertFormatSingle(
2100             u"The last symbols setter wins",
2101             u"latin",
2102             u"latin",
2103             NumberFormatter::with().symbols(symbols).adoptSymbols(new NumberingSystem(LATN)),
2104             Locale::getEnglish(),
2105             12345.67,
2106             u"12,345.67");
2107 
2108     assertFormatSingle(
2109             u"The last symbols setter wins",
2110             nullptr,
2111             nullptr,
2112             NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).symbols(symbols),
2113             Locale::getEnglish(),
2114             12345.67,
2115             u"12!345.67");
2116 }
2117 
2118 // TODO: Enable if/when currency symbol override is added.
2119 //void NumberFormatterTest::symbolsOverride() {
2120 //    DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
2121 //    dfs.setCurrencySymbol("@");
2122 //    dfs.setInternationalCurrencySymbol("foo");
2123 //    assertFormatSingle(
2124 //            u"Custom Short Currency Symbol",
2125 //            NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
2126 //            Locale::getEnglish(),
2127 //            12.3,
2128 //            u"@ 12.30");
2129 //}
2130 
sign()2131 void NumberFormatterApiTest::sign() {
2132     assertFormatSingle(
2133             u"Sign Auto Positive",
2134             u"sign-auto",
2135             u"",
2136             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
2137             Locale::getEnglish(),
2138             444444,
2139             u"444,444");
2140 
2141     assertFormatSingle(
2142             u"Sign Auto Negative",
2143             u"sign-auto",
2144             u"",
2145             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
2146             Locale::getEnglish(),
2147             -444444,
2148             u"-444,444");
2149 
2150     assertFormatSingle(
2151             u"Sign Auto Zero",
2152             u"sign-auto",
2153             u"",
2154             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
2155             Locale::getEnglish(),
2156             0,
2157             u"0");
2158 
2159     assertFormatSingle(
2160             u"Sign Always Positive",
2161             u"sign-always",
2162             u"+!",
2163             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
2164             Locale::getEnglish(),
2165             444444,
2166             u"+444,444");
2167 
2168     assertFormatSingle(
2169             u"Sign Always Negative",
2170             u"sign-always",
2171             u"+!",
2172             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
2173             Locale::getEnglish(),
2174             -444444,
2175             u"-444,444");
2176 
2177     assertFormatSingle(
2178             u"Sign Always Zero",
2179             u"sign-always",
2180             u"+!",
2181             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
2182             Locale::getEnglish(),
2183             0,
2184             u"+0");
2185 
2186     assertFormatSingle(
2187             u"Sign Never Positive",
2188             u"sign-never",
2189             u"+_",
2190             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
2191             Locale::getEnglish(),
2192             444444,
2193             u"444,444");
2194 
2195     assertFormatSingle(
2196             u"Sign Never Negative",
2197             u"sign-never",
2198             u"+_",
2199             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
2200             Locale::getEnglish(),
2201             -444444,
2202             u"444,444");
2203 
2204     assertFormatSingle(
2205             u"Sign Never Zero",
2206             u"sign-never",
2207             u"+_",
2208             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
2209             Locale::getEnglish(),
2210             0,
2211             u"0");
2212 
2213     assertFormatSingle(
2214             u"Sign Accounting Positive",
2215             u"currency/USD sign-accounting",
2216             u"currency/USD ()",
2217             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
2218             Locale::getEnglish(),
2219             444444,
2220             u"$444,444.00");
2221 
2222     assertFormatSingle(
2223             u"Sign Accounting Negative",
2224             u"currency/USD sign-accounting",
2225             u"currency/USD ()",
2226             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
2227             Locale::getEnglish(),
2228             -444444,
2229             u"($444,444.00)");
2230 
2231     assertFormatSingle(
2232             u"Sign Accounting Zero",
2233             u"currency/USD sign-accounting",
2234             u"currency/USD ()",
2235             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
2236             Locale::getEnglish(),
2237             0,
2238             u"$0.00");
2239 
2240     assertFormatSingle(
2241             u"Sign Accounting-Always Positive",
2242             u"currency/USD sign-accounting-always",
2243             u"currency/USD ()!",
2244             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
2245             Locale::getEnglish(),
2246             444444,
2247             u"+$444,444.00");
2248 
2249     assertFormatSingle(
2250             u"Sign Accounting-Always Negative",
2251             u"currency/USD sign-accounting-always",
2252             u"currency/USD ()!",
2253             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
2254             Locale::getEnglish(),
2255             -444444,
2256             u"($444,444.00)");
2257 
2258     assertFormatSingle(
2259             u"Sign Accounting-Always Zero",
2260             u"currency/USD sign-accounting-always",
2261             u"currency/USD ()!",
2262             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
2263             Locale::getEnglish(),
2264             0,
2265             u"+$0.00");
2266 
2267     assertFormatSingle(
2268             u"Sign Except-Zero Positive",
2269             u"sign-except-zero",
2270             u"+?",
2271             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
2272             Locale::getEnglish(),
2273             444444,
2274             u"+444,444");
2275 
2276     assertFormatSingle(
2277             u"Sign Except-Zero Negative",
2278             u"sign-except-zero",
2279             u"+?",
2280             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
2281             Locale::getEnglish(),
2282             -444444,
2283             u"-444,444");
2284 
2285     assertFormatSingle(
2286             u"Sign Except-Zero Zero",
2287             u"sign-except-zero",
2288             u"+?",
2289             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
2290             Locale::getEnglish(),
2291             0,
2292             u"0");
2293 
2294     assertFormatSingle(
2295             u"Sign Accounting-Except-Zero Positive",
2296             u"currency/USD sign-accounting-except-zero",
2297             u"currency/USD ()?",
2298             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
2299             Locale::getEnglish(),
2300             444444,
2301             u"+$444,444.00");
2302 
2303     assertFormatSingle(
2304             u"Sign Accounting-Except-Zero Negative",
2305             u"currency/USD sign-accounting-except-zero",
2306             u"currency/USD ()?",
2307             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
2308             Locale::getEnglish(),
2309             -444444,
2310             u"($444,444.00)");
2311 
2312     assertFormatSingle(
2313             u"Sign Accounting-Except-Zero Zero",
2314             u"currency/USD sign-accounting-except-zero",
2315             u"currency/USD ()?",
2316             NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
2317             Locale::getEnglish(),
2318             0,
2319             u"$0.00");
2320 
2321     assertFormatSingle(
2322             u"Sign Accounting Negative Hidden",
2323             u"currency/USD unit-width-hidden sign-accounting",
2324             u"currency/USD unit-width-hidden ()",
2325             NumberFormatter::with()
2326                     .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
2327                     .unit(USD)
2328                     .unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
2329             Locale::getEnglish(),
2330             -444444,
2331             u"(444,444.00)");
2332 
2333     assertFormatSingle(
2334             u"Sign Accounting Negative Narrow",
2335             u"currency/USD unit-width-narrow sign-accounting",
2336             u"currency/USD unit-width-narrow ()",
2337             NumberFormatter::with()
2338                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
2339                 .unit(USD)
2340                 .unitWidth(UNUM_UNIT_WIDTH_NARROW),
2341             Locale::getCanada(),
2342             -444444,
2343             u"($444,444.00)");
2344 
2345     assertFormatSingle(
2346             u"Sign Accounting Negative Short",
2347             u"currency/USD sign-accounting",
2348             u"currency/USD ()",
2349             NumberFormatter::with()
2350                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
2351                 .unit(USD)
2352                 .unitWidth(UNUM_UNIT_WIDTH_SHORT),
2353             Locale::getCanada(),
2354             -444444,
2355             u"(US$444,444.00)");
2356 
2357     assertFormatSingle(
2358             u"Sign Accounting Negative Iso Code",
2359             u"currency/USD unit-width-iso-code sign-accounting",
2360             u"currency/USD unit-width-iso-code ()",
2361             NumberFormatter::with()
2362                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
2363                 .unit(USD)
2364                 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
2365             Locale::getCanada(),
2366             -444444,
2367             u"(USD 444,444.00)");
2368 
2369     // Note: CLDR does not provide an accounting pattern for long name currency.
2370     // We fall back to normal currency format. This may change in the future.
2371     assertFormatSingle(
2372             u"Sign Accounting Negative Full Name",
2373             u"currency/USD unit-width-full-name sign-accounting",
2374             u"currency/USD unit-width-full-name ()",
2375             NumberFormatter::with()
2376                 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
2377                 .unit(USD)
2378                 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2379             Locale::getCanada(),
2380             -444444,
2381             u"-444,444.00 US dollars");
2382 }
2383 
signNearZero()2384 void NumberFormatterApiTest::signNearZero() {
2385     // https://unicode-org.atlassian.net/browse/ICU-20709
2386     IcuTestErrorCode status(*this, "signNearZero");
2387     const struct TestCase {
2388         UNumberSignDisplay sign;
2389         double input;
2390         const char16_t* expected;
2391     } cases[] = {
2392         { UNUM_SIGN_AUTO,  1.1, u"1" },
2393         { UNUM_SIGN_AUTO,  0.9, u"1" },
2394         { UNUM_SIGN_AUTO,  0.1, u"0" },
2395         { UNUM_SIGN_AUTO, -0.1, u"-0" }, // interesting case
2396         { UNUM_SIGN_AUTO, -0.9, u"-1" },
2397         { UNUM_SIGN_AUTO, -1.1, u"-1" },
2398         { UNUM_SIGN_ALWAYS,  1.1, u"+1" },
2399         { UNUM_SIGN_ALWAYS,  0.9, u"+1" },
2400         { UNUM_SIGN_ALWAYS,  0.1, u"+0" },
2401         { UNUM_SIGN_ALWAYS, -0.1, u"-0" },
2402         { UNUM_SIGN_ALWAYS, -0.9, u"-1" },
2403         { UNUM_SIGN_ALWAYS, -1.1, u"-1" },
2404         { UNUM_SIGN_EXCEPT_ZERO,  1.1, u"+1" },
2405         { UNUM_SIGN_EXCEPT_ZERO,  0.9, u"+1" },
2406         { UNUM_SIGN_EXCEPT_ZERO,  0.1, u"0" }, // interesting case
2407         { UNUM_SIGN_EXCEPT_ZERO, -0.1, u"0" }, // interesting case
2408         { UNUM_SIGN_EXCEPT_ZERO, -0.9, u"-1" },
2409         { UNUM_SIGN_EXCEPT_ZERO, -1.1, u"-1" },
2410     };
2411     for (auto& cas : cases) {
2412         auto sign = cas.sign;
2413         auto input = cas.input;
2414         auto expected = cas.expected;
2415         auto actual = NumberFormatter::with()
2416             .sign(sign)
2417             .precision(Precision::integer())
2418             .locale(Locale::getUS())
2419             .formatDouble(input, status)
2420             .toString(status);
2421         assertEquals(
2422             DoubleToUnicodeString(input) + " @ SignDisplay " + Int64ToUnicodeString(sign),
2423             expected, actual);
2424     }
2425 }
2426 
signCoverage()2427 void NumberFormatterApiTest::signCoverage() {
2428     // https://unicode-org.atlassian.net/browse/ICU-20708
2429     IcuTestErrorCode status(*this, "signCoverage");
2430     const struct TestCase {
2431         UNumberSignDisplay sign;
2432         const char16_t* expectedStrings[8];
2433     } cases[] = {
2434         { UNUM_SIGN_AUTO, {        u"-∞", u"-1", u"-0",  u"0",  u"1",  u"∞",  u"NaN", u"-NaN" } },
2435         { UNUM_SIGN_ALWAYS, {      u"-∞", u"-1", u"-0", u"+0", u"+1", u"+∞", u"+NaN", u"-NaN" } },
2436         { UNUM_SIGN_NEVER, {        u"∞",  u"1",  u"0",  u"0",  u"1",  u"∞",  u"NaN",  u"NaN" } },
2437         { UNUM_SIGN_EXCEPT_ZERO, { u"-∞", u"-1",  u"0",  u"0", u"+1", u"+∞",  u"NaN",  u"NaN" } },
2438     };
2439     double negNaN = std::copysign(uprv_getNaN(), -0.0);
2440     const double inputs[] = {
2441         -uprv_getInfinity(), -1, -0.0, 0, 1, uprv_getInfinity(), uprv_getNaN(), negNaN
2442     };
2443     for (auto& cas : cases) {
2444         auto sign = cas.sign;
2445         for (int32_t i = 0; i < UPRV_LENGTHOF(inputs); i++) {
2446             auto input = inputs[i];
2447             auto expected = cas.expectedStrings[i];
2448             auto actual = NumberFormatter::with()
2449                 .sign(sign)
2450                 .locale(Locale::getUS())
2451                 .formatDouble(input, status)
2452                 .toString(status);
2453             assertEquals(
2454                 DoubleToUnicodeString(input) + " " + Int64ToUnicodeString(sign),
2455                 expected, actual);
2456         }
2457     }
2458 }
2459 
decimal()2460 void NumberFormatterApiTest::decimal() {
2461     assertFormatDescending(
2462             u"Decimal Default",
2463             u"decimal-auto",
2464             u"",
2465             NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO),
2466             Locale::getEnglish(),
2467             u"87,650",
2468             u"8,765",
2469             u"876.5",
2470             u"87.65",
2471             u"8.765",
2472             u"0.8765",
2473             u"0.08765",
2474             u"0.008765",
2475             u"0");
2476 
2477     assertFormatDescending(
2478             u"Decimal Always Shown",
2479             u"decimal-always",
2480             u"decimal-always",
2481             NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS),
2482             Locale::getEnglish(),
2483             u"87,650.",
2484             u"8,765.",
2485             u"876.5",
2486             u"87.65",
2487             u"8.765",
2488             u"0.8765",
2489             u"0.08765",
2490             u"0.008765",
2491             u"0.");
2492 }
2493 
scale()2494 void NumberFormatterApiTest::scale() {
2495     assertFormatDescending(
2496             u"Multiplier None",
2497             u"scale/1",
2498             u"",
2499             NumberFormatter::with().scale(Scale::none()),
2500             Locale::getEnglish(),
2501             u"87,650",
2502             u"8,765",
2503             u"876.5",
2504             u"87.65",
2505             u"8.765",
2506             u"0.8765",
2507             u"0.08765",
2508             u"0.008765",
2509             u"0");
2510 
2511     assertFormatDescending(
2512             u"Multiplier Power of Ten",
2513             u"scale/1000000",
2514             u"scale/1E6",
2515             NumberFormatter::with().scale(Scale::powerOfTen(6)),
2516             Locale::getEnglish(),
2517             u"87,650,000,000",
2518             u"8,765,000,000",
2519             u"876,500,000",
2520             u"87,650,000",
2521             u"8,765,000",
2522             u"876,500",
2523             u"87,650",
2524             u"8,765",
2525             u"0");
2526 
2527     assertFormatDescending(
2528             u"Multiplier Arbitrary Double",
2529             u"scale/5.2",
2530             u"scale/5.2",
2531             NumberFormatter::with().scale(Scale::byDouble(5.2)),
2532             Locale::getEnglish(),
2533             u"455,780",
2534             u"45,578",
2535             u"4,557.8",
2536             u"455.78",
2537             u"45.578",
2538             u"4.5578",
2539             u"0.45578",
2540             u"0.045578",
2541             u"0");
2542 
2543     assertFormatDescending(
2544             u"Multiplier Arbitrary BigDecimal",
2545             u"scale/5.2",
2546             u"scale/5.2",
2547             NumberFormatter::with().scale(Scale::byDecimal({"5.2", -1})),
2548             Locale::getEnglish(),
2549             u"455,780",
2550             u"45,578",
2551             u"4,557.8",
2552             u"455.78",
2553             u"45.578",
2554             u"4.5578",
2555             u"0.45578",
2556             u"0.045578",
2557             u"0");
2558 
2559     assertFormatDescending(
2560             u"Multiplier Arbitrary Double And Power Of Ten",
2561             u"scale/5200",
2562             u"scale/5200",
2563             NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(5.2, 3)),
2564             Locale::getEnglish(),
2565             u"455,780,000",
2566             u"45,578,000",
2567             u"4,557,800",
2568             u"455,780",
2569             u"45,578",
2570             u"4,557.8",
2571             u"455.78",
2572             u"45.578",
2573             u"0");
2574 
2575     assertFormatDescending(
2576             u"Multiplier Zero",
2577             u"scale/0",
2578             u"scale/0",
2579             NumberFormatter::with().scale(Scale::byDouble(0)),
2580             Locale::getEnglish(),
2581             u"0",
2582             u"0",
2583             u"0",
2584             u"0",
2585             u"0",
2586             u"0",
2587             u"0",
2588             u"0",
2589             u"0");
2590 
2591     assertFormatSingle(
2592             u"Multiplier Skeleton Scientific Notation and Percent",
2593             u"percent scale/1E2",
2594             u"%x100",
2595             NumberFormatter::with().unit(NoUnit::percent()).scale(Scale::powerOfTen(2)),
2596             Locale::getEnglish(),
2597             0.5,
2598             u"50%");
2599 
2600     assertFormatSingle(
2601             u"Negative Multiplier",
2602             u"scale/-5.2",
2603             u"scale/-5.2",
2604             NumberFormatter::with().scale(Scale::byDouble(-5.2)),
2605             Locale::getEnglish(),
2606             2,
2607             u"-10.4");
2608 
2609     assertFormatSingle(
2610             u"Negative One Multiplier",
2611             u"scale/-1",
2612             u"scale/-1",
2613             NumberFormatter::with().scale(Scale::byDouble(-1)),
2614             Locale::getEnglish(),
2615             444444,
2616             u"-444,444");
2617 
2618     assertFormatSingle(
2619             u"Two-Type Multiplier with Overlap",
2620             u"scale/10000",
2621             u"scale/1E4",
2622             NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(100, 2)),
2623             Locale::getEnglish(),
2624             2,
2625             u"20,000");
2626 }
2627 
locale()2628 void NumberFormatterApiTest::locale() {
2629     // Coverage for the locale setters.
2630     UErrorCode status = U_ZERO_ERROR;
2631     UnicodeString actual = NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status)
2632             .toString(status);
2633     assertEquals("Locale withLocale()", u"1\u202f234", actual);
2634 }
2635 
skeletonUserGuideExamples()2636 void NumberFormatterApiTest::skeletonUserGuideExamples() {
2637     IcuTestErrorCode status(*this, "skeletonUserGuideExamples");
2638 
2639     // Test the skeleton examples in userguide/format_parse/numbers/skeletons.md
2640     struct TestCase {
2641         const char16_t* skeleton;
2642         const char16_t* conciseSkeleton;
2643         double input;
2644         const char16_t* expected;
2645     } cases[] = {
2646         {u"percent", u"%", 25, u"25%"},
2647         {u".00", u".00", 25, u"25.00"},
2648         {u"percent .00", u"% .00", 25, u"25.00%"},
2649         {u"scale/100", u"scale/100", 0.3, u"30"},
2650         {u"percent scale/100", u"%x100", 0.3, u"30%"},
2651         {u"measure-unit/length-meter", u"unit/meter", 5, u"5 m"},
2652         {u"measure-unit/length-meter unit-width-full-name", u"unit/meter unit-width-full-name", 5, u"5 meters"},
2653         {u"currency/CAD", u"currency/CAD", 10, u"CA$10.00"},
2654         {u"currency/CAD unit-width-narrow", u"currency/CAD unit-width-narrow", 10, u"$10.00"},
2655         {u"compact-short", u"K", 5000, u"5K"},
2656         {u"compact-long", u"KK", 5000, u"5 thousand"},
2657         {u"compact-short currency/CAD", u"K currency/CAD", 5000, u"CA$5K"},
2658         {u"", u"", 5000, u"5,000"},
2659         {u"group-min2", u",?", 5000, u"5000"},
2660         {u"group-min2", u",?", 15000, u"15,000"},
2661         {u"sign-always", u"+!", 60, u"+60"},
2662         {u"sign-always", u"+!", 0, u"+0"},
2663         {u"sign-except-zero", u"+?", 60, u"+60"},
2664         {u"sign-except-zero", u"+?", 0, u"0"},
2665         {u"sign-accounting currency/CAD", u"() currency/CAD", -40, u"(CA$40.00)"}
2666     };
2667 
2668     for (const auto& cas : cases) {
2669         status.setScope(cas.skeleton);
2670         FormattedNumber actual = NumberFormatter::forSkeleton(cas.skeleton, status)
2671             .locale("en-US")
2672             .formatDouble(cas.input, status);
2673         assertEquals(cas.skeleton, cas.expected, actual.toTempString(status));
2674         status.errIfFailureAndReset();
2675         FormattedNumber actualConcise = NumberFormatter::forSkeleton(cas.conciseSkeleton, status)
2676             .locale("en-US")
2677             .formatDouble(cas.input, status);
2678         assertEquals(cas.conciseSkeleton, cas.expected, actualConcise.toTempString(status));
2679         status.errIfFailureAndReset();
2680     }
2681 }
2682 
formatTypes()2683 void NumberFormatterApiTest::formatTypes() {
2684     UErrorCode status = U_ZERO_ERROR;
2685     LocalizedNumberFormatter formatter = NumberFormatter::withLocale(Locale::getEnglish());
2686 
2687     // Double
2688     assertEquals("Format double", "514.23", formatter.formatDouble(514.23, status).toString(status));
2689 
2690     // Int64
2691     assertEquals("Format int64", "51,423", formatter.formatDouble(51423L, status).toString(status));
2692 
2693     // decNumber
2694     UnicodeString actual = formatter.formatDecimal("98765432123456789E1", status).toString(status);
2695     assertEquals("Format decNumber", u"987,654,321,234,567,890", actual);
2696 
2697     // Also test proper DecimalQuantity bytes storage when all digits are in the fraction.
2698     // The number needs to have exactly 40 digits, which is the size of the default buffer.
2699     // (issue discovered by the address sanitizer in C++)
2700     static const char* str = "0.009876543210987654321098765432109876543211";
2701     actual = formatter.precision(Precision::unlimited()).formatDecimal(str, status).toString(status);
2702     assertEquals("Format decNumber to 40 digits", str, actual);
2703 }
2704 
fieldPositionLogic()2705 void NumberFormatterApiTest::fieldPositionLogic() {
2706     IcuTestErrorCode status(*this, "fieldPositionLogic");
2707 
2708     const char16_t* message = u"Field position logic test";
2709 
2710     FormattedNumber fmtd = assertFormatSingle(
2711             message,
2712             u"",
2713             u"",
2714             NumberFormatter::with(),
2715             Locale::getEnglish(),
2716             -9876543210.12,
2717             u"-9,876,543,210.12");
2718 
2719     static const UFieldPosition expectedFieldPositions[] = {
2720             // field, begin index, end index
2721             {UNUM_SIGN_FIELD, 0, 1},
2722             {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
2723             {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
2724             {UNUM_GROUPING_SEPARATOR_FIELD, 10, 11},
2725             {UNUM_INTEGER_FIELD, 1, 14},
2726             {UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
2727             {UNUM_FRACTION_FIELD, 15, 17}};
2728 
2729     assertNumberFieldPositions(
2730             message,
2731             fmtd,
2732             expectedFieldPositions,
2733             UPRV_LENGTHOF(expectedFieldPositions));
2734 
2735     // Test the iteration functionality of nextFieldPosition
2736     ConstrainedFieldPosition actual;
2737     actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD);
2738     int32_t i = 1;
2739     while (fmtd.nextPosition(actual, status)) {
2740         UFieldPosition expected = expectedFieldPositions[i++];
2741         assertEquals(
2742                 UnicodeString(u"Next for grouping, field, case #") + Int64ToUnicodeString(i),
2743                 expected.field,
2744                 actual.getField());
2745         assertEquals(
2746                 UnicodeString(u"Next for grouping, begin index, case #") + Int64ToUnicodeString(i),
2747                 expected.beginIndex,
2748                 actual.getStart());
2749         assertEquals(
2750                 UnicodeString(u"Next for grouping, end index, case #") + Int64ToUnicodeString(i),
2751                 expected.endIndex,
2752                 actual.getLimit());
2753     }
2754     assertEquals(u"Should have seen all grouping separators", 4, i);
2755 
2756     // Make sure strings without fraction do not contain fraction field
2757     actual.reset();
2758     actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD);
2759     fmtd = NumberFormatter::withLocale("en").formatInt(5, status);
2760     assertFalse(u"No fraction part in an integer", fmtd.nextPosition(actual, status));
2761 }
2762 
fieldPositionCoverage()2763 void NumberFormatterApiTest::fieldPositionCoverage() {
2764     IcuTestErrorCode status(*this, "fieldPositionCoverage");
2765 
2766     {
2767         const char16_t* message = u"Measure unit field position basic";
2768         FormattedNumber result = assertFormatSingle(
2769                 message,
2770                 u"measure-unit/temperature-fahrenheit",
2771                 u"unit/fahrenheit",
2772                 NumberFormatter::with().unit(FAHRENHEIT),
2773                 Locale::getEnglish(),
2774                 68,
2775                 u"68°F");
2776         static const UFieldPosition expectedFieldPositions[] = {
2777                 // field, begin index, end index
2778                 {UNUM_INTEGER_FIELD, 0, 2},
2779                 {UNUM_MEASURE_UNIT_FIELD, 2, 4}};
2780         assertNumberFieldPositions(
2781                 message,
2782                 result,
2783                 expectedFieldPositions,
2784                 UPRV_LENGTHOF(expectedFieldPositions));
2785     }
2786 
2787     {
2788         const char16_t* message = u"Measure unit field position with compound unit";
2789         FormattedNumber result = assertFormatSingle(
2790                 message,
2791                 u"measure-unit/temperature-fahrenheit per-measure-unit/duration-day",
2792                 u"unit/fahrenheit-per-day",
2793                 NumberFormatter::with().unit(FAHRENHEIT).perUnit(DAY),
2794                 Locale::getEnglish(),
2795                 68,
2796                 u"68°F/d");
2797         static const UFieldPosition expectedFieldPositions[] = {
2798                 // field, begin index, end index
2799                 {UNUM_INTEGER_FIELD, 0, 2},
2800                 // coverage for old enum:
2801                 {DecimalFormat::kMeasureUnitField, 2, 6}};
2802         assertNumberFieldPositions(
2803                 message,
2804                 result,
2805                 expectedFieldPositions,
2806                 UPRV_LENGTHOF(expectedFieldPositions));
2807     }
2808 
2809     {
2810         const char16_t* message = u"Measure unit field position with spaces";
2811         FormattedNumber result = assertFormatSingle(
2812                 message,
2813                 u"measure-unit/length-meter unit-width-full-name",
2814                 u"unit/meter unit-width-full-name",
2815                 NumberFormatter::with().unit(METER).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2816                 Locale::getEnglish(),
2817                 68,
2818                 u"68 meters");
2819         static const UFieldPosition expectedFieldPositions[] = {
2820                 // field, begin index, end index
2821                 {UNUM_INTEGER_FIELD, 0, 2},
2822                 // note: field starts after the space
2823                 {UNUM_MEASURE_UNIT_FIELD, 3, 9}};
2824         assertNumberFieldPositions(
2825                 message,
2826                 result,
2827                 expectedFieldPositions,
2828                 UPRV_LENGTHOF(expectedFieldPositions));
2829     }
2830 
2831     {
2832         const char16_t* message = u"Measure unit field position with prefix and suffix";
2833         FormattedNumber result = assertFormatSingle(
2834                 message,
2835                 u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
2836                 u"unit/meter-per-second unit-width-full-name",
2837                 NumberFormatter::with().unit(METER).perUnit(SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2838                 "ky", // locale with the interesting data
2839                 68,
2840                 u"секундасына 68 метр");
2841         static const UFieldPosition expectedFieldPositions[] = {
2842                 // field, begin index, end index
2843                 {UNUM_MEASURE_UNIT_FIELD, 0, 11},
2844                 {UNUM_INTEGER_FIELD, 12, 14},
2845                 {UNUM_MEASURE_UNIT_FIELD, 15, 19}};
2846         assertNumberFieldPositions(
2847                 message,
2848                 result,
2849                 expectedFieldPositions,
2850                 UPRV_LENGTHOF(expectedFieldPositions));
2851     }
2852 
2853     {
2854         const char16_t* message = u"Measure unit field position with inner spaces";
2855         FormattedNumber result = assertFormatSingle(
2856                 message,
2857                 u"measure-unit/temperature-fahrenheit unit-width-full-name",
2858                 u"unit/fahrenheit unit-width-full-name",
2859                 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
2860                 "vi", // locale with the interesting data
2861                 68,
2862                 u"68 độ F");
2863         static const UFieldPosition expectedFieldPositions[] = {
2864                 // field, begin index, end index
2865                 {UNUM_INTEGER_FIELD, 0, 2},
2866                 // Should trim leading/trailing spaces, but not inner spaces:
2867                 {UNUM_MEASURE_UNIT_FIELD, 3, 7}};
2868         assertNumberFieldPositions(
2869                 message,
2870                 result,
2871                 expectedFieldPositions,
2872                 UPRV_LENGTHOF(expectedFieldPositions));
2873     }
2874 
2875     {
2876         // Data: other{"‎{0} K"} == "\u200E{0} K"
2877         // If that data changes, try to find another example of a non-empty unit prefix/suffix
2878         // that is also all ignorables (whitespace and bidi control marks).
2879         const char16_t* message = u"Measure unit field position with fully ignorable prefix";
2880         FormattedNumber result = assertFormatSingle(
2881                 message,
2882                 u"measure-unit/temperature-kelvin",
2883                 u"unit/kelvin",
2884                 NumberFormatter::with().unit(KELVIN),
2885                 "fa", // locale with the interesting data
2886                 68,
2887                 u"‎۶۸ K");
2888         static const UFieldPosition expectedFieldPositions[] = {
2889                 // field, begin index, end index
2890                 {UNUM_INTEGER_FIELD, 1, 3},
2891                 {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
2892         assertNumberFieldPositions(
2893                 message,
2894                 result,
2895                 expectedFieldPositions,
2896                 UPRV_LENGTHOF(expectedFieldPositions));
2897     }
2898 
2899     {
2900         const char16_t* message = u"Compact field basic";
2901         FormattedNumber result = assertFormatSingle(
2902                 message,
2903                 u"compact-short",
2904                 u"K",
2905                 NumberFormatter::with().notation(Notation::compactShort()),
2906                 Locale::getUS(),
2907                 65000,
2908                 u"65K");
2909         static const UFieldPosition expectedFieldPositions[] = {
2910                 // field, begin index, end index
2911                 {UNUM_INTEGER_FIELD, 0, 2},
2912                 {UNUM_COMPACT_FIELD, 2, 3}};
2913         assertNumberFieldPositions(
2914                 message,
2915                 result,
2916                 expectedFieldPositions,
2917                 UPRV_LENGTHOF(expectedFieldPositions));
2918     }
2919 
2920     {
2921         const char16_t* message = u"Compact field with spaces";
2922         FormattedNumber result = assertFormatSingle(
2923                 message,
2924                 u"compact-long",
2925                 u"KK",
2926                 NumberFormatter::with().notation(Notation::compactLong()),
2927                 Locale::getUS(),
2928                 65000,
2929                 u"65 thousand");
2930         static const UFieldPosition expectedFieldPositions[] = {
2931                 // field, begin index, end index
2932                 {UNUM_INTEGER_FIELD, 0, 2},
2933                 {UNUM_COMPACT_FIELD, 3, 11}};
2934         assertNumberFieldPositions(
2935                 message,
2936                 result,
2937                 expectedFieldPositions,
2938                 UPRV_LENGTHOF(expectedFieldPositions));
2939     }
2940 
2941     {
2942         const char16_t* message = u"Compact field with inner space";
2943         FormattedNumber result = assertFormatSingle(
2944                 message,
2945                 u"compact-long",
2946                 u"KK",
2947                 NumberFormatter::with().notation(Notation::compactLong()),
2948                 "fil",  // locale with interesting data
2949                 6000,
2950                 u"6 na libo");
2951         static const UFieldPosition expectedFieldPositions[] = {
2952                 // field, begin index, end index
2953                 {UNUM_INTEGER_FIELD, 0, 1},
2954                 {UNUM_COMPACT_FIELD, 2, 9}};
2955         assertNumberFieldPositions(
2956                 message,
2957                 result,
2958                 expectedFieldPositions,
2959                 UPRV_LENGTHOF(expectedFieldPositions));
2960     }
2961 
2962     {
2963         const char16_t* message = u"Compact field with bidi mark";
2964         FormattedNumber result = assertFormatSingle(
2965                 message,
2966                 u"compact-long",
2967                 u"KK",
2968                 NumberFormatter::with().notation(Notation::compactLong()),
2969                 "he",  // locale with interesting data
2970                 6000,
2971                 u"\u200F6 אלף");
2972         static const UFieldPosition expectedFieldPositions[] = {
2973                 // field, begin index, end index
2974                 {UNUM_INTEGER_FIELD, 1, 2},
2975                 {UNUM_COMPACT_FIELD, 3, 6}};
2976         assertNumberFieldPositions(
2977                 message,
2978                 result,
2979                 expectedFieldPositions,
2980                 UPRV_LENGTHOF(expectedFieldPositions));
2981     }
2982 
2983     {
2984         const char16_t* message = u"Compact with currency fields";
2985         FormattedNumber result = assertFormatSingle(
2986                 message,
2987                 u"compact-short currency/USD",
2988                 u"K currency/USD",
2989                 NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
2990                 "sr_Latn",  // locale with interesting data
2991                 65000,
2992                 u"65 hilj. US$");
2993         static const UFieldPosition expectedFieldPositions[] = {
2994                 // field, begin index, end index
2995                 {UNUM_INTEGER_FIELD, 0, 2},
2996                 {UNUM_COMPACT_FIELD, 3, 8},
2997                 {UNUM_CURRENCY_FIELD, 9, 12}};
2998         assertNumberFieldPositions(
2999                 message,
3000                 result,
3001                 expectedFieldPositions,
3002                 UPRV_LENGTHOF(expectedFieldPositions));
3003     }
3004 
3005     {
3006         const char16_t* message = u"Currency long name fields";
3007         FormattedNumber result = assertFormatSingle(
3008                 message,
3009                 u"currency/USD unit-width-full-name",
3010                 u"currency/USD unit-width-full-name",
3011                 NumberFormatter::with().unit(USD)
3012                     .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
3013                 "en",
3014                 12345,
3015                 u"12,345.00 US dollars");
3016         static const UFieldPosition expectedFieldPositions[] = {
3017                 // field, begin index, end index
3018                 {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
3019                 {UNUM_INTEGER_FIELD, 0, 6},
3020                 {UNUM_DECIMAL_SEPARATOR_FIELD, 6, 7},
3021                 {UNUM_FRACTION_FIELD, 7, 9},
3022                 {UNUM_CURRENCY_FIELD, 10, 20}};
3023         assertNumberFieldPositions(
3024                 message,
3025                 result,
3026                 expectedFieldPositions,
3027                 UPRV_LENGTHOF(expectedFieldPositions));
3028     }
3029 
3030     {
3031         const char16_t* message = u"Compact with measure unit fields";
3032         FormattedNumber result = assertFormatSingle(
3033                 message,
3034                 u"compact-long measure-unit/length-meter unit-width-full-name",
3035                 u"KK unit/meter unit-width-full-name",
3036                 NumberFormatter::with().notation(Notation::compactLong())
3037                     .unit(METER)
3038                     .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3039                 Locale::getUS(),
3040                 65000,
3041                 u"65 thousand meters");
3042         static const UFieldPosition expectedFieldPositions[] = {
3043                 // field, begin index, end index
3044                 {UNUM_INTEGER_FIELD, 0, 2},
3045                 {UNUM_COMPACT_FIELD, 3, 11},
3046                 {UNUM_MEASURE_UNIT_FIELD, 12, 18}};
3047         assertNumberFieldPositions(
3048                 message,
3049                 result,
3050                 expectedFieldPositions,
3051                 UPRV_LENGTHOF(expectedFieldPositions));
3052     }
3053 }
3054 
toFormat()3055 void NumberFormatterApiTest::toFormat() {
3056     IcuTestErrorCode status(*this, "icuFormat");
3057     LocalizedNumberFormatter lnf = NumberFormatter::withLocale("fr")
3058             .precision(Precision::fixedFraction(3));
3059     LocalPointer<Format> format(lnf.toFormat(status), status);
3060     FieldPosition fpos(UNUM_DECIMAL_SEPARATOR_FIELD);
3061     UnicodeString sb;
3062     format->format(514.23, sb, fpos, status);
3063     assertEquals("Should correctly format number", u"514,230", sb);
3064     assertEquals("Should find decimal separator", 3, fpos.getBeginIndex());
3065     assertEquals("Should find end of decimal separator", 4, fpos.getEndIndex());
3066     assertEquals(
3067             "ICU Format should round-trip",
3068             lnf.toSkeleton(status),
3069             dynamic_cast<LocalizedNumberFormatterAsFormat*>(format.getAlias())->getNumberFormatter()
3070                     .toSkeleton(status));
3071 
3072     UFormattedNumberData result;
3073     result.quantity.setToDouble(514.23);
3074     lnf.formatImpl(&result, status);
3075     FieldPositionIterator fpi1;
3076     {
3077         FieldPositionIteratorHandler fpih(&fpi1, status);
3078         result.getAllFieldPositions(fpih, status);
3079     }
3080 
3081     FieldPositionIterator fpi2;
3082     format->format(514.23, sb.remove(), &fpi2, status);
3083 
3084     assertTrue("Should produce same field position iterator", fpi1 == fpi2);
3085 }
3086 
errors()3087 void NumberFormatterApiTest::errors() {
3088     LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).precision(
3089             Precision::fixedFraction(
3090                     -1));
3091 
3092     // formatInt
3093     UErrorCode status = U_ZERO_ERROR;
3094     FormattedNumber fn = lnf.formatInt(1, status);
3095     assertEquals(
3096             "Should fail in formatInt method with error code for rounding",
3097             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
3098             status);
3099 
3100     // formatDouble
3101     status = U_ZERO_ERROR;
3102     fn = lnf.formatDouble(1.0, status);
3103     assertEquals(
3104             "Should fail in formatDouble method with error code for rounding",
3105             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
3106             status);
3107 
3108     // formatDecimal (decimal error)
3109     status = U_ZERO_ERROR;
3110     fn = NumberFormatter::withLocale("en").formatDecimal("1x2", status);
3111     assertEquals(
3112             "Should fail in formatDecimal method with error code for decimal number syntax",
3113             U_DECIMAL_NUMBER_SYNTAX_ERROR,
3114             status);
3115 
3116     // formatDecimal (setting error)
3117     status = U_ZERO_ERROR;
3118     fn = lnf.formatDecimal("1.0", status);
3119     assertEquals(
3120             "Should fail in formatDecimal method with error code for rounding",
3121             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
3122             status);
3123 
3124     // Skeleton string
3125     status = U_ZERO_ERROR;
3126     UnicodeString output = lnf.toSkeleton(status);
3127     assertEquals(
3128             "Should fail on toSkeleton terminal method with correct error code",
3129             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
3130             status);
3131     assertTrue(
3132             "Terminal toSkeleton on error object should be bogus",
3133             output.isBogus());
3134 
3135     // FieldPosition (constrained category)
3136     status = U_ZERO_ERROR;
3137     ConstrainedFieldPosition fp;
3138     fp.constrainCategory(UFIELD_CATEGORY_NUMBER);
3139     fn.nextPosition(fp, status);
3140     assertEquals(
3141             "Should fail on FieldPosition terminal method with correct error code",
3142             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
3143             status);
3144 
3145     // FieldPositionIterator (no constraints)
3146     status = U_ZERO_ERROR;
3147     fp.reset();
3148     fn.nextPosition(fp, status);
3149     assertEquals(
3150             "Should fail on FieldPositoinIterator terminal method with correct error code",
3151             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
3152             status);
3153 
3154     // Appendable
3155     status = U_ZERO_ERROR;
3156     UnicodeStringAppendable appendable(output.remove());
3157     fn.appendTo(appendable, status);
3158     assertEquals(
3159             "Should fail on Appendable terminal method with correct error code",
3160             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
3161             status);
3162 
3163     // UnicodeString
3164     status = U_ZERO_ERROR;
3165     output = fn.toString(status);
3166     assertEquals(
3167             "Should fail on UnicodeString terminal method with correct error code",
3168             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
3169             status);
3170     assertTrue(
3171             "Terminal UnicodeString on error object should be bogus",
3172             output.isBogus());
3173 
3174     // CopyErrorTo
3175     status = U_ZERO_ERROR;
3176     lnf.copyErrorTo(status);
3177     assertEquals(
3178             "Should fail since rounder is not legal with correct error code",
3179             U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
3180             status);
3181 }
3182 
validRanges()3183 void NumberFormatterApiTest::validRanges() {
3184 
3185 #define EXPECTED_MAX_INT_FRAC_SIG 999
3186 
3187 #define VALID_RANGE_ASSERT(status, method, lowerBound, argument) UPRV_BLOCK_MACRO_BEGIN { \
3188     UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \
3189         ? U_ZERO_ERROR \
3190         : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \
3191     assertEquals( \
3192         UnicodeString(u"Incorrect status for " #method " on input ") \
3193             + Int64ToUnicodeString(argument), \
3194         expectedStatus, \
3195         status); \
3196 } UPRV_BLOCK_MACRO_END
3197 
3198 #define VALID_RANGE_ONEARG(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
3199     for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
3200         UErrorCode status = U_ZERO_ERROR; \
3201         NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \
3202         VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
3203     } \
3204 } UPRV_BLOCK_MACRO_END
3205 
3206 #define VALID_RANGE_TWOARGS(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
3207     for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
3208         UErrorCode status = U_ZERO_ERROR; \
3209         /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \
3210         NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \
3211         VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
3212         status = U_ZERO_ERROR; \
3213         /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \
3214         NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \
3215         VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
3216         /* Check that first argument must be less than or equal to second argument */ \
3217         NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \
3218         assertEquals("Incorrect status for " #method " on max < min input", \
3219             U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \
3220             status); \
3221     } \
3222 } UPRV_BLOCK_MACRO_END
3223 
3224     VALID_RANGE_ONEARG(precision, Precision::fixedFraction, 0);
3225     VALID_RANGE_ONEARG(precision, Precision::minFraction, 0);
3226     VALID_RANGE_ONEARG(precision, Precision::maxFraction, 0);
3227     VALID_RANGE_TWOARGS(precision, Precision::minMaxFraction, 0);
3228     VALID_RANGE_ONEARG(precision, Precision::fixedSignificantDigits, 1);
3229     VALID_RANGE_ONEARG(precision, Precision::minSignificantDigits, 1);
3230     VALID_RANGE_ONEARG(precision, Precision::maxSignificantDigits, 1);
3231     VALID_RANGE_TWOARGS(precision, Precision::minMaxSignificantDigits, 1);
3232     VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMinDigits, 1);
3233     VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMaxDigits, 1);
3234     VALID_RANGE_ONEARG(notation, Notation::scientific().withMinExponentDigits, 1);
3235     VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo, 0);
3236     VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo(0).truncateAt, -1);
3237 }
3238 
copyMove()3239 void NumberFormatterApiTest::copyMove() {
3240     IcuTestErrorCode status(*this, "copyMove");
3241 
3242     // Default constructors
3243     LocalizedNumberFormatter l1;
3244     assertEquals("Initial behavior", u"10", l1.formatInt(10, status).toString(status), true);
3245     if (status.errDataIfFailureAndReset()) { return; }
3246     assertEquals("Initial call count", 1, l1.getCallCount());
3247     assertTrue("Initial compiled", l1.getCompiled() == nullptr);
3248 
3249     // Setup
3250     l1 = NumberFormatter::withLocale("en").unit(NoUnit::percent()).threshold(3);
3251     assertEquals("Initial behavior", u"10%", l1.formatInt(10, status).toString(status));
3252     assertEquals("Initial call count", 1, l1.getCallCount());
3253     assertTrue("Initial compiled", l1.getCompiled() == nullptr);
3254     l1.formatInt(123, status);
3255     assertEquals("Still not compiled", 2, l1.getCallCount());
3256     assertTrue("Still not compiled", l1.getCompiled() == nullptr);
3257     l1.formatInt(123, status);
3258     assertEquals("Compiled", u"10%", l1.formatInt(10, status).toString(status));
3259     assertEquals("Compiled", INT32_MIN, l1.getCallCount());
3260     assertTrue("Compiled", l1.getCompiled() != nullptr);
3261 
3262     // Copy constructor
3263     LocalizedNumberFormatter l2 = l1;
3264     assertEquals("[constructor] Copy behavior", u"10%", l2.formatInt(10, status).toString(status));
3265     assertEquals("[constructor] Copy should not have compiled state", 1, l2.getCallCount());
3266     assertTrue("[constructor] Copy should not have compiled state", l2.getCompiled() == nullptr);
3267 
3268     // Move constructor
3269     LocalizedNumberFormatter l3 = std::move(l1);
3270     assertEquals("[constructor] Move behavior", u"10%", l3.formatInt(10, status).toString(status));
3271     assertEquals("[constructor] Move *should* have compiled state", INT32_MIN, l3.getCallCount());
3272     assertTrue("[constructor] Move *should* have compiled state", l3.getCompiled() != nullptr);
3273     assertEquals("[constructor] Source should be reset after move", 0, l1.getCallCount());
3274     assertTrue("[constructor] Source should be reset after move", l1.getCompiled() == nullptr);
3275 
3276     // Reset l1 and l2 to check for macro-props copying for behavior testing
3277     // Make the test more interesting: also warm them up with a compiled formatter.
3278     l1 = NumberFormatter::withLocale("en");
3279     l1.formatInt(1, status);
3280     l1.formatInt(1, status);
3281     l1.formatInt(1, status);
3282     l2 = NumberFormatter::withLocale("en");
3283     l2.formatInt(1, status);
3284     l2.formatInt(1, status);
3285     l2.formatInt(1, status);
3286 
3287     // Copy assignment
3288     l1 = l3;
3289     assertEquals("[assignment] Copy behavior", u"10%", l1.formatInt(10, status).toString(status));
3290     assertEquals("[assignment] Copy should not have compiled state", 1, l1.getCallCount());
3291     assertTrue("[assignment] Copy should not have compiled state", l1.getCompiled() == nullptr);
3292 
3293     // Move assignment
3294     l2 = std::move(l3);
3295     assertEquals("[assignment] Move behavior", u"10%", l2.formatInt(10, status).toString(status));
3296     assertEquals("[assignment] Move *should* have compiled state", INT32_MIN, l2.getCallCount());
3297     assertTrue("[assignment] Move *should* have compiled state", l2.getCompiled() != nullptr);
3298     assertEquals("[assignment] Source should be reset after move", 0, l3.getCallCount());
3299     assertTrue("[assignment] Source should be reset after move", l3.getCompiled() == nullptr);
3300 
3301     // Coverage tests for UnlocalizedNumberFormatter
3302     UnlocalizedNumberFormatter u1;
3303     assertEquals("Default behavior", u"10", u1.locale("en").formatInt(10, status).toString(status));
3304     u1 = u1.unit(NoUnit::percent());
3305     assertEquals("Copy assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
3306     UnlocalizedNumberFormatter u2 = u1;
3307     assertEquals("Copy constructor", u"10%", u2.locale("en").formatInt(10, status).toString(status));
3308     UnlocalizedNumberFormatter u3 = std::move(u1);
3309     assertEquals("Move constructor", u"10%", u3.locale("en").formatInt(10, status).toString(status));
3310     u1 = NumberFormatter::with();
3311     u1 = std::move(u2);
3312     assertEquals("Move assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
3313 
3314     // FormattedNumber move operators
3315     FormattedNumber result = l1.formatInt(10, status);
3316     assertEquals("FormattedNumber move constructor", u"10%", result.toString(status));
3317     result = l1.formatInt(20, status);
3318     assertEquals("FormattedNumber move assignment", u"20%", result.toString(status));
3319 }
3320 
localPointerCAPI()3321 void NumberFormatterApiTest::localPointerCAPI() {
3322     // NOTE: This is also the sample code in unumberformatter.h
3323     UErrorCode ec = U_ZERO_ERROR;
3324 
3325     // Setup:
3326     LocalUNumberFormatterPointer uformatter(unumf_openForSkeletonAndLocale(u"percent", -1, "en", &ec));
3327     LocalUFormattedNumberPointer uresult(unumf_openResult(&ec));
3328     if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
3329 
3330     // Format a decimal number:
3331     unumf_formatDecimal(uformatter.getAlias(), "9.87E-3", -1, uresult.getAlias(), &ec);
3332     if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
3333 
3334     // Get the location of the percent sign:
3335     UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
3336     unumf_resultNextFieldPosition(uresult.getAlias(), &ufpos, &ec);
3337     assertEquals("Percent sign location within '0.00987%'", 7, ufpos.beginIndex);
3338     assertEquals("Percent sign location within '0.00987%'", 8, ufpos.endIndex);
3339 
3340     // No need to do any cleanup since we are using LocalPointer.
3341 }
3342 
toObject()3343 void NumberFormatterApiTest::toObject() {
3344     IcuTestErrorCode status(*this, "toObject");
3345 
3346     // const lvalue version
3347     {
3348         LocalizedNumberFormatter lnf = NumberFormatter::withLocale("en")
3349                 .precision(Precision::fixedFraction(2));
3350         LocalPointer<LocalizedNumberFormatter> lnf2(lnf.clone());
3351         assertFalse("should create successfully, const lvalue", lnf2.isNull());
3352         assertEquals("object API test, const lvalue", u"1,000.00",
3353             lnf2->formatDouble(1000, status).toString(status));
3354     }
3355 
3356     // rvalue reference version
3357     {
3358         LocalPointer<LocalizedNumberFormatter> lnf(
3359             NumberFormatter::withLocale("en")
3360                 .precision(Precision::fixedFraction(2))
3361                 .clone());
3362         assertFalse("should create successfully, rvalue reference", lnf.isNull());
3363         assertEquals("object API test, rvalue reference", u"1,000.00",
3364             lnf->formatDouble(1000, status).toString(status));
3365     }
3366 
3367     // to std::unique_ptr via constructor
3368     {
3369         std::unique_ptr<LocalizedNumberFormatter> lnf(
3370             NumberFormatter::withLocale("en")
3371                 .precision(Precision::fixedFraction(2))
3372                 .clone());
3373         assertTrue("should create successfully, unique_ptr", static_cast<bool>(lnf));
3374         assertEquals("object API test, unique_ptr", u"1,000.00",
3375             lnf->formatDouble(1000, status).toString(status));
3376     }
3377 
3378     // to std::unique_ptr via assignment
3379     {
3380         std::unique_ptr<LocalizedNumberFormatter> lnf =
3381             NumberFormatter::withLocale("en")
3382                 .precision(Precision::fixedFraction(2))
3383                 .clone();
3384         assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf));
3385         assertEquals("object API test, unique_ptr B", u"1,000.00",
3386             lnf->formatDouble(1000, status).toString(status));
3387     }
3388 
3389     // to LocalPointer via assignment
3390     {
3391         LocalPointer<UnlocalizedNumberFormatter> f =
3392             NumberFormatter::with().clone();
3393     }
3394 
3395     // make sure no memory leaks
3396     {
3397         NumberFormatter::with().clone();
3398     }
3399 }
3400 
toDecimalNumber()3401 void NumberFormatterApiTest::toDecimalNumber() {
3402     IcuTestErrorCode status(*this, "toDecimalNumber");
3403     FormattedNumber fn = NumberFormatter::withLocale("bn-BD")
3404         .scale(Scale::powerOfTen(2))
3405         .precision(Precision::maxSignificantDigits(5))
3406         .formatDouble(9.87654321e12, status);
3407     assertEquals("Should have expected localized string result",
3408         u"৯৮,৭৬,৫০,০০,০০,০০,০০০", fn.toString(status));
3409     assertEquals(u"Should have expected toDecimalNumber string result",
3410         "9.8765E+14", fn.toDecimalNumber<std::string>(status).c_str());
3411 }
3412 
3413 
assertFormatDescending(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,...)3414 void NumberFormatterApiTest::assertFormatDescending(
3415         const char16_t* umessage,
3416         const char16_t* uskeleton,
3417         const char16_t* conciseSkeleton,
3418         const UnlocalizedNumberFormatter& f,
3419         Locale locale,
3420         ...) {
3421     va_list args;
3422     va_start(args, locale);
3423     UnicodeString message(TRUE, umessage, -1);
3424     static double inputs[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
3425     const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
3426     const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
3427     IcuTestErrorCode status(*this, "assertFormatDescending");
3428     status.setScope(message);
3429     UnicodeString expecteds[10];
3430     for (int16_t i = 0; i < 9; i++) {
3431         char16_t caseNumber = u'0' + i;
3432         double d = inputs[i];
3433         UnicodeString expected = va_arg(args, const char16_t*);
3434         expecteds[i] = expected;
3435         UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
3436         assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
3437         assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
3438         UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
3439         assertSuccess(message + u": Safe Path: " + caseNumber, status);
3440         assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
3441     }
3442     if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
3443         UnicodeString skeleton(TRUE, uskeleton, -1);
3444         // Only compare normalized skeletons: the tests need not provide the normalized forms.
3445         // Use the normalized form to construct the testing formatter to guarantee no loss of info.
3446         UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
3447         assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
3448         LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
3449         for (int32_t i = 0; i < 9; i++) {
3450             double d = inputs[i];
3451             UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
3452             assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
3453         }
3454         // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
3455         // If the concise skeleton starts with '~', disable the round-trip check.
3456         bool shouldRoundTrip = true;
3457         if (conciseSkeleton[0] == u'~') {
3458             conciseSkeleton++;
3459             shouldRoundTrip = false;
3460         }
3461         LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
3462         if (shouldRoundTrip) {
3463             assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
3464         }
3465         for (int32_t i = 0; i < 9; i++) {
3466             double d = inputs[i];
3467             UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
3468             assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
3469         }
3470     } else {
3471         assertUndefinedSkeleton(f);
3472     }
3473 }
3474 
assertFormatDescendingBig(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,...)3475 void NumberFormatterApiTest::assertFormatDescendingBig(
3476         const char16_t* umessage,
3477         const char16_t* uskeleton,
3478         const char16_t* conciseSkeleton,
3479         const UnlocalizedNumberFormatter& f,
3480         Locale locale,
3481         ...) {
3482     va_list args;
3483     va_start(args, locale);
3484     UnicodeString message(TRUE, umessage, -1);
3485     static double inputs[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
3486     const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
3487     const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
3488     IcuTestErrorCode status(*this, "assertFormatDescendingBig");
3489     status.setScope(message);
3490     UnicodeString expecteds[10];
3491     for (int16_t i = 0; i < 9; i++) {
3492         char16_t caseNumber = u'0' + i;
3493         double d = inputs[i];
3494         UnicodeString expected = va_arg(args, const char16_t*);
3495         expecteds[i] = expected;
3496         UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
3497         assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
3498         assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
3499         UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
3500         assertSuccess(message + u": Safe Path: " + caseNumber, status);
3501         assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
3502     }
3503     if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
3504         UnicodeString skeleton(TRUE, uskeleton, -1);
3505         // Only compare normalized skeletons: the tests need not provide the normalized forms.
3506         // Use the normalized form to construct the testing formatter to guarantee no loss of info.
3507         UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
3508         assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
3509         LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
3510         for (int32_t i = 0; i < 9; i++) {
3511             double d = inputs[i];
3512             UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
3513             assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
3514         }
3515         // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
3516         // If the concise skeleton starts with '~', disable the round-trip check.
3517         bool shouldRoundTrip = true;
3518         if (conciseSkeleton[0] == u'~') {
3519             conciseSkeleton++;
3520             shouldRoundTrip = false;
3521         }
3522         LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
3523         if (shouldRoundTrip) {
3524             assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
3525         }
3526         for (int32_t i = 0; i < 9; i++) {
3527             double d = inputs[i];
3528             UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
3529             assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
3530         }
3531     } else {
3532         assertUndefinedSkeleton(f);
3533     }
3534 }
3535 
3536 FormattedNumber
assertFormatSingle(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,double input,const UnicodeString & expected)3537 NumberFormatterApiTest::assertFormatSingle(
3538         const char16_t* umessage,
3539         const char16_t* uskeleton,
3540         const char16_t* conciseSkeleton,
3541         const UnlocalizedNumberFormatter& f,
3542         Locale locale,
3543         double input,
3544         const UnicodeString& expected) {
3545     UnicodeString message(TRUE, umessage, -1);
3546     const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
3547     const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
3548     IcuTestErrorCode status(*this, "assertFormatSingle");
3549     status.setScope(message);
3550     FormattedNumber result1 = l1.formatDouble(input, status);
3551     UnicodeString actual1 = result1.toString(status);
3552     assertSuccess(message + u": Unsafe Path", status);
3553     assertEquals(message + u": Unsafe Path", expected, actual1);
3554     UnicodeString actual2 = l2.formatDouble(input, status).toString(status);
3555     assertSuccess(message + u": Safe Path", status);
3556     assertEquals(message + u": Safe Path", expected, actual2);
3557     if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
3558         UnicodeString skeleton(TRUE, uskeleton, -1);
3559         // Only compare normalized skeletons: the tests need not provide the normalized forms.
3560         // Use the normalized form to construct the testing formatter to ensure no loss of info.
3561         UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
3562         assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
3563         LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
3564         UnicodeString actual3 = l3.formatDouble(input, status).toString(status);
3565         assertEquals(message + ": Skeleton Path: '" + normalized + "': " + input, expected, actual3);
3566         // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
3567         // If the concise skeleton starts with '~', disable the round-trip check.
3568         bool shouldRoundTrip = true;
3569         if (conciseSkeleton[0] == u'~') {
3570             conciseSkeleton++;
3571             shouldRoundTrip = false;
3572         }
3573         LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
3574         if (shouldRoundTrip) {
3575             assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
3576         }
3577         UnicodeString actual4 = l4.formatDouble(input, status).toString(status);
3578         assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + input, expected, actual4);
3579     } else {
3580         assertUndefinedSkeleton(f);
3581     }
3582     return result1;
3583 }
3584 
assertUndefinedSkeleton(const UnlocalizedNumberFormatter & f)3585 void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter& f) {
3586     UErrorCode status = U_ZERO_ERROR;
3587     UnicodeString skeleton = f.toSkeleton(status);
3588     assertEquals(
3589             u"Expect toSkeleton to fail, but passed, producing: " + skeleton,
3590             U_UNSUPPORTED_ERROR,
3591             status);
3592 }
3593 
assertNumberFieldPositions(const char16_t * message,const FormattedNumber & formattedNumber,const UFieldPosition * expectedFieldPositions,int32_t length)3594 void NumberFormatterApiTest::assertNumberFieldPositions(
3595         const char16_t* message,
3596         const FormattedNumber& formattedNumber,
3597         const UFieldPosition* expectedFieldPositions,
3598         int32_t length) {
3599     IcuTestErrorCode status(*this, "assertNumberFieldPositions");
3600 
3601     // Check FormattedValue functions
3602     checkFormattedValue(
3603         message,
3604         static_cast<const FormattedValue&>(formattedNumber),
3605         formattedNumber.toString(status),
3606         UFIELD_CATEGORY_NUMBER,
3607         expectedFieldPositions,
3608         length);
3609 }
3610 
3611 
3612 #endif /* #if !UCONFIG_NO_FORMATTING */
3613