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