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 "unicode/testlog.h"
15 #include "unicode/utypes.h"
16 #include "number_asformat.h"
17 #include "number_types.h"
18 #include "number_utils.h"
19 #include "number_utypes.h"
20 #include "number_microprops.h"
21 #include "numbertest.h"
22
23 using number::impl::UFormattedNumberData;
24
25 // Horrible workaround for the lack of a status code in the constructor...
26 // (Also affects numbertest_range.cpp)
27 UErrorCode globalNumberFormatterApiTestStatus = U_ZERO_ERROR;
28
NumberFormatterApiTest()29 NumberFormatterApiTest::NumberFormatterApiTest()
30 : NumberFormatterApiTest(globalNumberFormatterApiTestStatus) {
31 }
32
NumberFormatterApiTest(UErrorCode & status)33 NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode& status)
34 : USD(u"USD", status),
35 GBP(u"GBP", status),
36 CZK(u"CZK", status),
37 CAD(u"CAD", status),
38 ESP(u"ESP", status),
39 PTE(u"PTE", status),
40 RON(u"RON", status),
41 TWD(u"TWD", status),
42 TRY(u"TRY", status),
43 CNY(u"CNY", status),
44 FRENCH_SYMBOLS(Locale::getFrench(), status),
45 SWISS_SYMBOLS(Locale("de-CH"), status),
46 MYANMAR_SYMBOLS(Locale("my"), status) {
47
48 // Check for error on the first MeasureUnit in case there is no data
49 LocalPointer<MeasureUnit> unit(MeasureUnit::createMeter(status));
50 if (U_FAILURE(status)) {
51 dataerrln("%s %d status = %s", __FILE__, __LINE__, u_errorName(status));
52 return;
53 }
54 METER = *unit;
55
56 METER_PER_SECOND = *LocalPointer<MeasureUnit>(MeasureUnit::createMeterPerSecond(status));
57 DAY = *LocalPointer<MeasureUnit>(MeasureUnit::createDay(status));
58 SQUARE_METER = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMeter(status));
59 FAHRENHEIT = *LocalPointer<MeasureUnit>(MeasureUnit::createFahrenheit(status));
60 SECOND = *LocalPointer<MeasureUnit>(MeasureUnit::createSecond(status));
61 POUND = *LocalPointer<MeasureUnit>(MeasureUnit::createPound(status));
62 POUND_FORCE = *LocalPointer<MeasureUnit>(MeasureUnit::createPoundForce(status));
63 SQUARE_MILE = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMile(status));
64 SQUARE_INCH = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareInch(status));
65 JOULE = *LocalPointer<MeasureUnit>(MeasureUnit::createJoule(status));
66 FURLONG = *LocalPointer<MeasureUnit>(MeasureUnit::createFurlong(status));
67 KELVIN = *LocalPointer<MeasureUnit>(MeasureUnit::createKelvin(status));
68
69 MATHSANB = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("mathsanb", status));
70 LATN = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("latn", status));
71 }
72
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)73 void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
74 if (exec) {
75 logln("TestSuite NumberFormatterApiTest: ");
76 }
77 TESTCASE_AUTO_BEGIN;
78 TESTCASE_AUTO(notationSimple);
79 TESTCASE_AUTO(notationScientific);
80 TESTCASE_AUTO(notationCompact);
81 TESTCASE_AUTO(unitMeasure);
82 TESTCASE_AUTO(unitCompoundMeasure);
83 TESTCASE_AUTO(unitSkeletons);
84 TESTCASE_AUTO(unitUsage);
85 TESTCASE_AUTO(unitUsageErrorCodes);
86 TESTCASE_AUTO(unitUsageSkeletons);
87 TESTCASE_AUTO(unitCurrency);
88 TESTCASE_AUTO(unitPercent);
89 if (!quick) {
90 // Slow test: run in exhaustive mode only
91 TESTCASE_AUTO(percentParity);
92 }
93 TESTCASE_AUTO(roundingFraction);
94 TESTCASE_AUTO(roundingFigures);
95 TESTCASE_AUTO(roundingFractionFigures);
96 TESTCASE_AUTO(roundingOther);
97 TESTCASE_AUTO(grouping);
98 TESTCASE_AUTO(padding);
99 TESTCASE_AUTO(integerWidth);
100 TESTCASE_AUTO(symbols);
101 // TODO: Add this method if currency symbols override support is added.
102 //TESTCASE_AUTO(symbolsOverride);
103 TESTCASE_AUTO(sign);
104 TESTCASE_AUTO(signNearZero);
105 TESTCASE_AUTO(signCoverage);
106 TESTCASE_AUTO(decimal);
107 TESTCASE_AUTO(scale);
108 TESTCASE_AUTO(locale);
109 TESTCASE_AUTO(skeletonUserGuideExamples);
110 TESTCASE_AUTO(formatTypes);
111 TESTCASE_AUTO(fieldPositionLogic);
112 TESTCASE_AUTO(fieldPositionCoverage);
113 TESTCASE_AUTO(toFormat);
114 TESTCASE_AUTO(errors);
115 if (!quick) {
116 // Slow test: run in exhaustive mode only
117 // (somewhat slow to check all permutations of settings)
118 TESTCASE_AUTO(validRanges);
119 }
120 TESTCASE_AUTO(copyMove);
121 TESTCASE_AUTO(localPointerCAPI);
122 TESTCASE_AUTO(toObject);
123 TESTCASE_AUTO(toDecimalNumber);
124 TESTCASE_AUTO(microPropsInternals);
125 TESTCASE_AUTO_END;
126 }
127
notationSimple()128 void NumberFormatterApiTest::notationSimple() {
129 assertFormatDescending(
130 u"Basic",
131 u"",
132 u"",
133 NumberFormatter::with(),
134 Locale::getEnglish(),
135 u"87,650",
136 u"8,765",
137 u"876.5",
138 u"87.65",
139 u"8.765",
140 u"0.8765",
141 u"0.08765",
142 u"0.008765",
143 u"0");
144
145 assertFormatDescendingBig(
146 u"Big Simple",
147 u"notation-simple",
148 u"",
149 NumberFormatter::with().notation(Notation::simple()),
150 Locale::getEnglish(),
151 u"87,650,000",
152 u"8,765,000",
153 u"876,500",
154 u"87,650",
155 u"8,765",
156 u"876.5",
157 u"87.65",
158 u"8.765",
159 u"0");
160
161 assertFormatSingle(
162 u"Basic with Negative Sign",
163 u"",
164 u"",
165 NumberFormatter::with(),
166 Locale::getEnglish(),
167 -9876543.21,
168 u"-9,876,543.21");
169 }
170
171
notationScientific()172 void NumberFormatterApiTest::notationScientific() {
173 assertFormatDescending(
174 u"Scientific",
175 u"scientific",
176 u"E0",
177 NumberFormatter::with().notation(Notation::scientific()),
178 Locale::getEnglish(),
179 u"8.765E4",
180 u"8.765E3",
181 u"8.765E2",
182 u"8.765E1",
183 u"8.765E0",
184 u"8.765E-1",
185 u"8.765E-2",
186 u"8.765E-3",
187 u"0E0");
188
189 assertFormatDescending(
190 u"Engineering",
191 u"engineering",
192 u"EE0",
193 NumberFormatter::with().notation(Notation::engineering()),
194 Locale::getEnglish(),
195 u"87.65E3",
196 u"8.765E3",
197 u"876.5E0",
198 u"87.65E0",
199 u"8.765E0",
200 u"876.5E-3",
201 u"87.65E-3",
202 u"8.765E-3",
203 u"0E0");
204
205 assertFormatDescending(
206 u"Scientific sign always shown",
207 u"scientific/sign-always",
208 u"E+!0",
209 NumberFormatter::with().notation(
210 Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS)),
211 Locale::getEnglish(),
212 u"8.765E+4",
213 u"8.765E+3",
214 u"8.765E+2",
215 u"8.765E+1",
216 u"8.765E+0",
217 u"8.765E-1",
218 u"8.765E-2",
219 u"8.765E-3",
220 u"0E+0");
221
222 assertFormatDescending(
223 u"Scientific min exponent digits",
224 u"scientific/*ee",
225 u"E00",
226 NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)),
227 Locale::getEnglish(),
228 u"8.765E04",
229 u"8.765E03",
230 u"8.765E02",
231 u"8.765E01",
232 u"8.765E00",
233 u"8.765E-01",
234 u"8.765E-02",
235 u"8.765E-03",
236 u"0E00");
237
238 assertFormatSingle(
239 u"Scientific Negative",
240 u"scientific",
241 u"E0",
242 NumberFormatter::with().notation(Notation::scientific()),
243 Locale::getEnglish(),
244 -1000000,
245 u"-1E6");
246
247 assertFormatSingle(
248 u"Scientific Infinity",
249 u"scientific",
250 u"E0",
251 NumberFormatter::with().notation(Notation::scientific()),
252 Locale::getEnglish(),
253 -uprv_getInfinity(),
254 u"-∞");
255
256 assertFormatSingle(
257 u"Scientific NaN",
258 u"scientific",
259 u"E0",
260 NumberFormatter::with().notation(Notation::scientific()),
261 Locale::getEnglish(),
262 uprv_getNaN(),
263 u"NaN");
264 }
265
notationCompact()266 void NumberFormatterApiTest::notationCompact() {
267 assertFormatDescending(
268 u"Compact Short",
269 u"compact-short",
270 u"K",
271 NumberFormatter::with().notation(Notation::compactShort()),
272 Locale::getEnglish(),
273 u"88K",
274 u"8.8K",
275 u"876",
276 u"88",
277 u"8.8",
278 u"0.88",
279 u"0.088",
280 u"0.0088",
281 u"0");
282
283 assertFormatDescending(
284 u"Compact Long",
285 u"compact-long",
286 u"KK",
287 NumberFormatter::with().notation(Notation::compactLong()),
288 Locale::getEnglish(),
289 u"88 thousand",
290 u"8.8 thousand",
291 u"876",
292 u"88",
293 u"8.8",
294 u"0.88",
295 u"0.088",
296 u"0.0088",
297 u"0");
298
299 assertFormatDescending(
300 u"Compact Short Currency",
301 u"compact-short currency/USD",
302 u"K currency/USD",
303 NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
304 Locale::getEnglish(),
305 u"$88K",
306 u"$8.8K",
307 u"$876",
308 u"$88",
309 u"$8.8",
310 u"$0.88",
311 u"$0.088",
312 u"$0.0088",
313 u"$0");
314
315 assertFormatDescending(
316 u"Compact Short with ISO Currency",
317 u"compact-short currency/USD unit-width-iso-code",
318 u"K currency/USD unit-width-iso-code",
319 NumberFormatter::with().notation(Notation::compactShort())
320 .unit(USD)
321 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
322 Locale::getEnglish(),
323 u"USD 88K",
324 u"USD 8.8K",
325 u"USD 876",
326 u"USD 88",
327 u"USD 8.8",
328 u"USD 0.88",
329 u"USD 0.088",
330 u"USD 0.0088",
331 u"USD 0");
332
333 assertFormatDescending(
334 u"Compact Short with Long Name Currency",
335 u"compact-short currency/USD unit-width-full-name",
336 u"K currency/USD unit-width-full-name",
337 NumberFormatter::with().notation(Notation::compactShort())
338 .unit(USD)
339 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
340 Locale::getEnglish(),
341 u"88K US dollars",
342 u"8.8K US dollars",
343 u"876 US dollars",
344 u"88 US dollars",
345 u"8.8 US dollars",
346 u"0.88 US dollars",
347 u"0.088 US dollars",
348 u"0.0088 US dollars",
349 u"0 US dollars");
350
351 // Note: Most locales don't have compact long currency, so this currently falls back to short.
352 // This test case should be fixed when proper compact long currency patterns are added.
353 assertFormatDescending(
354 u"Compact Long Currency",
355 u"compact-long currency/USD",
356 u"KK currency/USD",
357 NumberFormatter::with().notation(Notation::compactLong()).unit(USD),
358 Locale::getEnglish(),
359 u"$88K", // should be something like "$88 thousand"
360 u"$8.8K",
361 u"$876",
362 u"$88",
363 u"$8.8",
364 u"$0.88",
365 u"$0.088",
366 u"$0.0088",
367 u"$0");
368
369 // Note: Most locales don't have compact long currency, so this currently falls back to short.
370 // This test case should be fixed when proper compact long currency patterns are added.
371 assertFormatDescending(
372 u"Compact Long with ISO Currency",
373 u"compact-long currency/USD unit-width-iso-code",
374 u"KK currency/USD unit-width-iso-code",
375 NumberFormatter::with().notation(Notation::compactLong())
376 .unit(USD)
377 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
378 Locale::getEnglish(),
379 u"USD 88K", // should be something like "USD 88 thousand"
380 u"USD 8.8K",
381 u"USD 876",
382 u"USD 88",
383 u"USD 8.8",
384 u"USD 0.88",
385 u"USD 0.088",
386 u"USD 0.0088",
387 u"USD 0");
388
389 // TODO: This behavior could be improved and should be revisited.
390 assertFormatDescending(
391 u"Compact Long with Long Name Currency",
392 u"compact-long currency/USD unit-width-full-name",
393 u"KK currency/USD unit-width-full-name",
394 NumberFormatter::with().notation(Notation::compactLong())
395 .unit(USD)
396 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
397 Locale::getEnglish(),
398 u"88 thousand US dollars",
399 u"8.8 thousand US dollars",
400 u"876 US dollars",
401 u"88 US dollars",
402 u"8.8 US dollars",
403 u"0.88 US dollars",
404 u"0.088 US dollars",
405 u"0.0088 US dollars",
406 u"0 US dollars");
407
408 assertFormatSingle(
409 u"Compact Plural One",
410 u"compact-long",
411 u"KK",
412 NumberFormatter::with().notation(Notation::compactLong()),
413 Locale::createFromName("es"),
414 1000000,
415 u"1 millón");
416
417 assertFormatSingle(
418 u"Compact Plural Other",
419 u"compact-long",
420 u"KK",
421 NumberFormatter::with().notation(Notation::compactLong()),
422 Locale::createFromName("es"),
423 2000000,
424 u"2 millones");
425
426 assertFormatSingle(
427 u"Compact with Negative Sign",
428 u"compact-short",
429 u"K",
430 NumberFormatter::with().notation(Notation::compactShort()),
431 Locale::getEnglish(),
432 -9876543.21,
433 u"-9.9M");
434
435 assertFormatSingle(
436 u"Compact Rounding",
437 u"compact-short",
438 u"K",
439 NumberFormatter::with().notation(Notation::compactShort()),
440 Locale::getEnglish(),
441 990000,
442 u"990K");
443
444 assertFormatSingle(
445 u"Compact Rounding",
446 u"compact-short",
447 u"K",
448 NumberFormatter::with().notation(Notation::compactShort()),
449 Locale::getEnglish(),
450 999000,
451 u"999K");
452
453 assertFormatSingle(
454 u"Compact Rounding",
455 u"compact-short",
456 u"K",
457 NumberFormatter::with().notation(Notation::compactShort()),
458 Locale::getEnglish(),
459 999900,
460 u"1M");
461
462 assertFormatSingle(
463 u"Compact Rounding",
464 u"compact-short",
465 u"K",
466 NumberFormatter::with().notation(Notation::compactShort()),
467 Locale::getEnglish(),
468 9900000,
469 u"9.9M");
470
471 assertFormatSingle(
472 u"Compact Rounding",
473 u"compact-short",
474 u"K",
475 NumberFormatter::with().notation(Notation::compactShort()),
476 Locale::getEnglish(),
477 9990000,
478 u"10M");
479
480 assertFormatSingle(
481 u"Compact in zh-Hant-HK",
482 u"compact-short",
483 u"K",
484 NumberFormatter::with().notation(Notation::compactShort()),
485 Locale("zh-Hant-HK"),
486 1e7,
487 u"10M");
488
489 assertFormatSingle(
490 u"Compact in zh-Hant",
491 u"compact-short",
492 u"K",
493 NumberFormatter::with().notation(Notation::compactShort()),
494 Locale("zh-Hant"),
495 1e7,
496 u"1000\u842C");
497
498 if (!logKnownIssue("21258", "StandardPlural cannot handle keywords 1, 0")) {
499 assertFormatSingle(
500 u"Compact with plural form =1 (ICU-21258)",
501 u"compact-long",
502 u"K",
503 NumberFormatter::with().notation(Notation::compactLong()),
504 Locale("fr-FR"),
505 1e3,
506 u"mille");
507 }
508
509 assertFormatSingle(
510 u"Compact Infinity",
511 u"compact-short",
512 u"K",
513 NumberFormatter::with().notation(Notation::compactShort()),
514 Locale::getEnglish(),
515 -uprv_getInfinity(),
516 u"-∞");
517
518 assertFormatSingle(
519 u"Compact NaN",
520 u"compact-short",
521 u"K",
522 NumberFormatter::with().notation(Notation::compactShort()),
523 Locale::getEnglish(),
524 uprv_getNaN(),
525 u"NaN");
526
527 // NOTE: There is no API for compact custom data in C++
528 // and thus no "Compact Somali No Figure" test
529 }
530
unitMeasure()531 void NumberFormatterApiTest::unitMeasure() {
532 IcuTestErrorCode status(*this, "unitMeasure()");
533
534 assertFormatDescending(
535 u"Meters Short and unit() method",
536 u"measure-unit/length-meter",
537 u"unit/meter",
538 NumberFormatter::with().unit(MeasureUnit::getMeter()),
539 Locale::getEnglish(),
540 u"87,650 m",
541 u"8,765 m",
542 u"876.5 m",
543 u"87.65 m",
544 u"8.765 m",
545 u"0.8765 m",
546 u"0.08765 m",
547 u"0.008765 m",
548 u"0 m");
549
550 assertFormatDescending(
551 u"Meters Long and adoptUnit() method",
552 u"measure-unit/length-meter unit-width-full-name",
553 u"unit/meter unit-width-full-name",
554 NumberFormatter::with().adoptUnit(new MeasureUnit(METER))
555 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
556 Locale::getEnglish(),
557 u"87,650 meters",
558 u"8,765 meters",
559 u"876.5 meters",
560 u"87.65 meters",
561 u"8.765 meters",
562 u"0.8765 meters",
563 u"0.08765 meters",
564 u"0.008765 meters",
565 u"0 meters");
566
567 assertFormatDescending(
568 u"Compact Meters Long",
569 u"compact-long measure-unit/length-meter unit-width-full-name",
570 u"KK unit/meter unit-width-full-name",
571 NumberFormatter::with().notation(Notation::compactLong())
572 .unit(METER)
573 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
574 Locale::getEnglish(),
575 u"88 thousand meters",
576 u"8.8 thousand meters",
577 u"876 meters",
578 u"88 meters",
579 u"8.8 meters",
580 u"0.88 meters",
581 u"0.088 meters",
582 u"0.0088 meters",
583 u"0 meters");
584
585 // // TODO(ICU-20941): Support formatting for not-built-in units
586 // assertFormatDescending(
587 // u"Hectometers",
588 // u"measure-unit/length-hectometer",
589 // u"unit/hectometer",
590 // NumberFormatter::with().unit(MeasureUnit::forIdentifier("hectometer", status)),
591 // Locale::getEnglish(),
592 // u"87,650 hm",
593 // u"8,765 hm",
594 // u"876.5 hm",
595 // u"87.65 hm",
596 // u"8.765 hm",
597 // u"0.8765 hm",
598 // u"0.08765 hm",
599 // u"0.008765 hm",
600 // u"0 hm");
601
602 // TODO: Implement Measure in C++
603 // assertFormatSingleMeasure(
604 // u"Meters with Measure Input",
605 // NumberFormatter::with().unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
606 // Locale::getEnglish(),
607 // new Measure(5.43, new MeasureUnit(METER)),
608 // u"5.43 meters");
609
610 // TODO: Implement Measure in C++
611 // assertFormatSingleMeasure(
612 // u"Measure format method takes precedence over fluent chain",
613 // NumberFormatter::with().unit(METER),
614 // Locale::getEnglish(),
615 // new Measure(5.43, USD),
616 // u"$5.43");
617
618 assertFormatSingle(
619 u"Meters with Negative Sign",
620 u"measure-unit/length-meter",
621 u"unit/meter",
622 NumberFormatter::with().unit(METER),
623 Locale::getEnglish(),
624 -9876543.21,
625 u"-9,876,543.21 m");
626
627 // The locale string "सान" appears only in brx.txt:
628 assertFormatSingle(
629 u"Interesting Data Fallback 1",
630 u"measure-unit/duration-day unit-width-full-name",
631 u"unit/day unit-width-full-name",
632 NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
633 Locale::createFromName("brx"),
634 5.43,
635 u"5.43 सान");
636
637 // Requires following the alias from unitsNarrow to unitsShort:
638 assertFormatSingle(
639 u"Interesting Data Fallback 2",
640 u"measure-unit/duration-day unit-width-narrow",
641 u"unit/day unit-width-narrow",
642 NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
643 Locale::createFromName("brx"),
644 5.43,
645 u"5.43 d");
646
647 // en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
648 // requiring fallback to the root.
649 assertFormatSingle(
650 u"Interesting Data Fallback 3",
651 u"measure-unit/area-square-meter unit-width-narrow",
652 u"unit/square-meter unit-width-narrow",
653 NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
654 Locale::createFromName("en-GB"),
655 5.43,
656 u"5.43m²");
657
658 // Try accessing a narrow unit directly from root.
659 assertFormatSingle(
660 u"Interesting Data Fallback 4",
661 u"measure-unit/area-square-meter unit-width-narrow",
662 u"unit/square-meter unit-width-narrow",
663 NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW),
664 Locale::createFromName("root"),
665 5.43,
666 u"5.43 m²");
667
668 // es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
669 // NOTE: This example is in the documentation.
670 assertFormatSingle(
671 u"Difference between Narrow and Short (Narrow Version)",
672 u"measure-unit/temperature-fahrenheit unit-width-narrow",
673 u"unit/fahrenheit unit-width-narrow",
674 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_NARROW),
675 Locale("es-US"),
676 5.43,
677 u"5.43°");
678
679 assertFormatSingle(
680 u"Difference between Narrow and Short (Short Version)",
681 u"measure-unit/temperature-fahrenheit unit-width-short",
682 u"unit/fahrenheit unit-width-short",
683 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_SHORT),
684 Locale("es-US"),
685 5.43,
686 u"5.43 °F");
687
688 assertFormatSingle(
689 u"MeasureUnit form without {0} in CLDR pattern",
690 u"measure-unit/temperature-kelvin unit-width-full-name",
691 u"unit/kelvin unit-width-full-name",
692 NumberFormatter::with().unit(KELVIN).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
693 Locale("es-MX"),
694 1,
695 u"kelvin");
696
697 assertFormatSingle(
698 u"MeasureUnit form without {0} in CLDR pattern and wide base form",
699 u"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
700 u"unit/kelvin .00000000000000000000 unit-width-full-name",
701 NumberFormatter::with().precision(Precision::fixedFraction(20))
702 .unit(KELVIN)
703 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
704 Locale("es-MX"),
705 1,
706 u"kelvin");
707
708 assertFormatSingle(
709 u"Person unit not in short form",
710 u"measure-unit/duration-year-person unit-width-full-name",
711 u"unit/year-person unit-width-full-name",
712 NumberFormatter::with().unit(MeasureUnit::getYearPerson())
713 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
714 Locale("es-MX"),
715 5,
716 u"5 a\u00F1os");
717
718 assertFormatSingle(
719 u"Mixed unit",
720 u"unit/yard-and-foot-and-inch",
721 u"unit/yard-and-foot-and-inch",
722 NumberFormatter::with()
723 .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status)),
724 Locale("en-US"),
725 3.65,
726 "3 yd, 1 ft, 11.4 in");
727
728 assertFormatSingle(
729 u"Mixed unit, Scientific",
730 u"unit/yard-and-foot-and-inch E0",
731 u"unit/yard-and-foot-and-inch E0",
732 NumberFormatter::with()
733 .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status))
734 .notation(Notation::scientific()),
735 Locale("en-US"),
736 3.65,
737 "3 yd, 1 ft, 1.14E1 in");
738
739 assertFormatSingle(
740 u"Mixed Unit (Narrow Version)",
741 u"unit/metric-ton-and-kilogram-and-gram unit-width-narrow",
742 u"unit/metric-ton-and-kilogram-and-gram unit-width-narrow",
743 NumberFormatter::with()
744 .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
745 .unitWidth(UNUM_UNIT_WIDTH_NARROW),
746 Locale("en-US"),
747 4.28571,
748 u"4t 285kg 710g");
749
750 assertFormatSingle(
751 u"Mixed Unit (Short Version)",
752 u"unit/metric-ton-and-kilogram-and-gram unit-width-short",
753 u"unit/metric-ton-and-kilogram-and-gram unit-width-short",
754 NumberFormatter::with()
755 .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
756 .unitWidth(UNUM_UNIT_WIDTH_SHORT),
757 Locale("en-US"),
758 4.28571,
759 u"4 t, 285 kg, 710 g");
760
761 assertFormatSingle(
762 u"Mixed Unit (Full Name Version)",
763 u"unit/metric-ton-and-kilogram-and-gram unit-width-full-name",
764 u"unit/metric-ton-and-kilogram-and-gram unit-width-full-name",
765 NumberFormatter::with()
766 .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status))
767 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
768 Locale("en-US"),
769 4.28571,
770 u"4 metric tons, 285 kilograms, 710 grams");
771
772 assertFormatSingle(
773 u"Testing \"1 foot 12 inches\"",
774 u"unit/foot-and-inch @### unit-width-full-name",
775 u"unit/foot-and-inch @### unit-width-full-name",
776 NumberFormatter::with()
777 .unit(MeasureUnit::forIdentifier("foot-and-inch", status))
778 .precision(Precision::maxSignificantDigits(4))
779 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
780 Locale("en-US"),
781 1.9999,
782 u"2 feet, 0 inches");
783
784 assertFormatSingle(
785 u"Negative numbers: temperature",
786 u"measure-unit/temperature-celsius",
787 u"unit/celsius",
788 NumberFormatter::with().unit(MeasureUnit::forIdentifier("celsius", status)),
789 Locale("nl-NL"),
790 -6.5,
791 u"-6,5\u00B0C");
792
793 assertFormatSingle(
794 u"Negative numbers: time",
795 u"unit/hour-and-minute-and-second",
796 u"unit/hour-and-minute-and-second",
797 NumberFormatter::with().unit(MeasureUnit::forIdentifier("hour-and-minute-and-second", status)),
798 Locale("de-DE"),
799 -1.24,
800 u"-1 Std., 14 Min. und 24 Sek.");
801
802 assertFormatSingle(
803 u"Zero out the unit field",
804 u"",
805 u"",
806 NumberFormatter::with().unit(KELVIN).unit(MeasureUnit()),
807 Locale("en"),
808 100,
809 u"100");
810 }
811
unitCompoundMeasure()812 void NumberFormatterApiTest::unitCompoundMeasure() {
813 IcuTestErrorCode status(*this, "unitCompoundMeasure()");
814
815 assertFormatDescending(
816 u"Meters Per Second Short (unit that simplifies) and perUnit method",
817 u"measure-unit/length-meter per-measure-unit/duration-second",
818 u"unit/meter-per-second",
819 NumberFormatter::with().unit(METER).perUnit(SECOND),
820 Locale::getEnglish(),
821 u"87,650 m/s",
822 u"8,765 m/s",
823 u"876.5 m/s",
824 u"87.65 m/s",
825 u"8.765 m/s",
826 u"0.8765 m/s",
827 u"0.08765 m/s",
828 u"0.008765 m/s",
829 u"0 m/s");
830
831 assertFormatDescending(
832 u"Meters Per Second Short, built-in m/s",
833 u"measure-unit/speed-meter-per-second",
834 u"unit/meter-per-second",
835 NumberFormatter::with().unit(METER_PER_SECOND),
836 Locale::getEnglish(),
837 u"87,650 m/s",
838 u"8,765 m/s",
839 u"876.5 m/s",
840 u"87.65 m/s",
841 u"8.765 m/s",
842 u"0.8765 m/s",
843 u"0.08765 m/s",
844 u"0.008765 m/s",
845 u"0 m/s");
846
847 assertFormatDescending(
848 u"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method",
849 u"measure-unit/mass-pound per-measure-unit/area-square-mile",
850 u"unit/pound-per-square-mile",
851 NumberFormatter::with().unit(POUND).adoptPerUnit(new MeasureUnit(SQUARE_MILE)),
852 Locale::getEnglish(),
853 u"87,650 lb/mi²",
854 u"8,765 lb/mi²",
855 u"876.5 lb/mi²",
856 u"87.65 lb/mi²",
857 u"8.765 lb/mi²",
858 u"0.8765 lb/mi²",
859 u"0.08765 lb/mi²",
860 u"0.008765 lb/mi²",
861 u"0 lb/mi²");
862
863 assertFormatDescending(
864 u"Joules Per Furlong Short (unit with no simplifications or special patterns)",
865 u"measure-unit/energy-joule per-measure-unit/length-furlong",
866 u"unit/joule-per-furlong",
867 NumberFormatter::with().unit(JOULE).perUnit(FURLONG),
868 Locale::getEnglish(),
869 u"87,650 J/fur",
870 u"8,765 J/fur",
871 u"876.5 J/fur",
872 u"87.65 J/fur",
873 u"8.765 J/fur",
874 u"0.8765 J/fur",
875 u"0.08765 J/fur",
876 u"0.008765 J/fur",
877 u"0 J/fur");
878
879 // TODO(ICU-20941): Support constructions such as this one.
880 // assertFormatDescending(
881 // u"Joules Per Furlong Short with unit identifier via API",
882 // u"measure-unit/energy-joule per-measure-unit/length-furlong",
883 // u"unit/joule-per-furlong",
884 // NumberFormatter::with().unit(MeasureUnit::forIdentifier("joule-per-furlong", status)),
885 // Locale::getEnglish(),
886 // u"87,650 J/fur",
887 // u"8,765 J/fur",
888 // u"876.5 J/fur",
889 // u"87.65 J/fur",
890 // u"8.765 J/fur",
891 // u"0.8765 J/fur",
892 // u"0.08765 J/fur",
893 // u"0.008765 J/fur",
894 // u"0 J/fur");
895
896 assertFormatDescending(
897 u"Pounds per Square Inch: composed",
898 u"measure-unit/force-pound-force per-measure-unit/area-square-inch",
899 u"unit/pound-force-per-square-inch",
900 NumberFormatter::with().unit(POUND_FORCE).perUnit(SQUARE_INCH),
901 Locale::getEnglish(),
902 u"87,650 psi",
903 u"8,765 psi",
904 u"876.5 psi",
905 u"87.65 psi",
906 u"8.765 psi",
907 u"0.8765 psi",
908 u"0.08765 psi",
909 u"0.008765 psi",
910 u"0 psi");
911
912 assertFormatDescending(
913 u"Pounds per Square Inch: built-in",
914 u"measure-unit/force-pound-force per-measure-unit/area-square-inch",
915 u"unit/pound-force-per-square-inch",
916 NumberFormatter::with().unit(MeasureUnit::getPoundPerSquareInch()),
917 Locale::getEnglish(),
918 u"87,650 psi",
919 u"8,765 psi",
920 u"876.5 psi",
921 u"87.65 psi",
922 u"8.765 psi",
923 u"0.8765 psi",
924 u"0.08765 psi",
925 u"0.008765 psi",
926 u"0 psi");
927
928 assertFormatSingle(
929 u"m/s/s simplifies to m/s^2",
930 u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
931 u"unit/meter-per-square-second",
932 NumberFormatter::with().unit(METER_PER_SECOND).perUnit(SECOND),
933 Locale("en-GB"),
934 2.4,
935 u"2.4 m/s\u00B2");
936
937 assertFormatSingle(
938 u"Negative numbers: acceleration",
939 u"measure-unit/acceleration-meter-per-square-second",
940 u"unit/meter-per-second-second",
941 NumberFormatter::with().unit(MeasureUnit::forIdentifier("meter-per-pow2-second", status)),
942 Locale("af-ZA"),
943 -9.81,
944 u"-9,81 m/s\u00B2");
945
946 // Testing the rejection of invalid specifications
947
948 // If .unit() is not given a built-in type, .perUnit() is not allowed
949 // (because .unit is now flexible enough to handle compound units,
950 // .perUnit() is supported for backward compatibility).
951 LocalizedNumberFormatter nf = NumberFormatter::with()
952 .unit(MeasureUnit::forIdentifier("furlong-pascal", status))
953 .perUnit(METER)
954 .locale("en-GB");
955 status.assertSuccess(); // Error is only returned once we try to format.
956 FormattedNumber num = nf.formatDouble(2.4, status);
957 if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
958 errln(UnicodeString("Expected failure, got: \"") +
959 nf.formatDouble(2.4, status).toString(status) + "\".");
960 status.assertSuccess();
961 }
962
963 // .perUnit() may only be passed a built-in type, or something that combines
964 // to a built-in type together with .unit().
965 MeasureUnit SQUARE_SECOND = MeasureUnit::forIdentifier("square-second", status);
966 nf = NumberFormatter::with().unit(FURLONG).perUnit(SQUARE_SECOND).locale("en-GB");
967 status.assertSuccess(); // Error is only returned once we try to format.
968 num = nf.formatDouble(2.4, status);
969 if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) {
970 errln(UnicodeString("Expected failure, got: \"") +
971 nf.formatDouble(2.4, status).toString(status) + "\".");
972 status.assertSuccess();
973 }
974 // As above, "square-second" is not a built-in type, however this time,
975 // meter-per-square-second is a built-in type.
976 assertFormatSingle(
977 u"meter per square-second works as a composed unit",
978 u"measure-unit/speed-meter-per-second per-measure-unit/duration-second",
979 u"unit/meter-per-square-second",
980 NumberFormatter::with().unit(METER).perUnit(SQUARE_SECOND),
981 Locale("en-GB"),
982 2.4,
983 u"2.4 m/s\u00B2");
984 }
985
986 // TODO: merge these tests into numbertest_skeletons.cpp instead of here:
unitSkeletons()987 void NumberFormatterApiTest::unitSkeletons() {
988 const struct TestCase {
989 const char *msg;
990 const char16_t *inputSkeleton;
991 const char16_t *normalizedSkeleton;
992 } cases[] = {
993 {"old-form built-in compound unit", //
994 u"measure-unit/speed-meter-per-second", //
995 u"unit/meter-per-second"},
996
997 {"old-form compound construction, converts to built-in", //
998 u"measure-unit/length-meter per-measure-unit/duration-second", //
999 u"unit/meter-per-second"},
1000
1001 {"old-form compound construction which does not simplify to a built-in", //
1002 u"measure-unit/energy-joule per-measure-unit/length-meter", //
1003 u"unit/joule-per-meter"},
1004
1005 {"old-form compound-compound ugliness resolves neatly", //
1006 u"measure-unit/speed-meter-per-second per-measure-unit/duration-second", //
1007 u"unit/meter-per-square-second"},
1008
1009 {"short-form built-in units stick with the built-in", //
1010 u"unit/meter-per-second", //
1011 u"unit/meter-per-second"},
1012
1013 {"short-form compound units stay as is", //
1014 u"unit/square-meter-per-square-meter", //
1015 u"unit/square-meter-per-square-meter"},
1016
1017 {"short-form compound units stay as is", //
1018 u"unit/joule-per-furlong", //
1019 u"unit/joule-per-furlong"},
1020
1021 {"short-form that doesn't consist of built-in units", //
1022 u"unit/hectometer-per-second", //
1023 u"unit/hectometer-per-second"},
1024
1025 {"short-form that doesn't consist of built-in units", //
1026 u"unit/meter-per-hectosecond", //
1027 u"unit/meter-per-hectosecond"},
1028
1029 {"percent compound skeletons handled correctly", //
1030 u"unit/percent-per-meter", //
1031 u"unit/percent-per-meter"},
1032
1033 {"permille compound skeletons handled correctly", //
1034 u"measure-unit/concentr-permille per-measure-unit/length-meter", //
1035 u"unit/permille-per-meter"},
1036
1037 {"percent simple unit is not actually considered a unit", //
1038 u"unit/percent", //
1039 u"percent"},
1040
1041 {"permille simple unit is not actually considered a unit", //
1042 u"measure-unit/concentr-permille", //
1043 u"permille"},
1044
1045 // // TODO: binary prefixes not supported yet!
1046 // {"Round-trip example from icu-units#35", //
1047 // u"unit/kibijoule-per-furlong", //
1048 // u"unit/kibijoule-per-furlong"},
1049 };
1050 for (auto &cas : cases) {
1051 IcuTestErrorCode status(*this, cas.msg);
1052 auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status);
1053 if (status.errIfFailureAndReset("NumberFormatter::forSkeleton failed")) {
1054 continue;
1055 }
1056 assertEquals( //
1057 UnicodeString(TRUE, cas.inputSkeleton, -1) + u" normalization", //
1058 cas.normalizedSkeleton, //
1059 nf.toSkeleton(status));
1060 status.errIfFailureAndReset("NumberFormatter::toSkeleton failed");
1061 }
1062
1063 const struct FailCase {
1064 const char *msg;
1065 const char16_t *inputSkeleton;
1066 UErrorCode expectedForSkelStatus;
1067 UErrorCode expectedToSkelStatus;
1068 } failCases[] = {
1069 {"Parsing measure-unit/* results in failure if not built-in unit",
1070 u"measure-unit/hectometer", //
1071 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1072 U_ZERO_ERROR},
1073
1074 {"Parsing per-measure-unit/* results in failure if not built-in unit",
1075 u"measure-unit/meter per-measure-unit/hectosecond", //
1076 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1077 U_ZERO_ERROR},
1078
1079 {"\"currency/EUR measure-unit/length-meter\" fails, conflicting skeleton.",
1080 u"currency/EUR measure-unit/length-meter", //
1081 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1082 U_ZERO_ERROR},
1083
1084 {"\"measure-unit/length-meter currency/EUR\" fails, conflicting skeleton.",
1085 u"measure-unit/length-meter currency/EUR", //
1086 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1087 U_ZERO_ERROR},
1088
1089 {"\"currency/EUR per-measure-unit/meter\" fails, conflicting skeleton.",
1090 u"currency/EUR per-measure-unit/length-meter", //
1091 U_NUMBER_SKELETON_SYNTAX_ERROR, //
1092 U_ZERO_ERROR},
1093 };
1094 for (auto &cas : failCases) {
1095 IcuTestErrorCode status(*this, cas.msg);
1096 auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status);
1097 if (status.expectErrorAndReset(cas.expectedForSkelStatus, cas.msg)) {
1098 continue;
1099 }
1100 nf.toSkeleton(status);
1101 status.expectErrorAndReset(cas.expectedToSkelStatus, cas.msg);
1102 }
1103
1104 IcuTestErrorCode status(*this, "unitSkeletons");
1105 assertEquals( //
1106 ".unit(METER_PER_SECOND) normalization", //
1107 u"unit/meter-per-second", //
1108 NumberFormatter::with().unit(METER_PER_SECOND).toSkeleton(status));
1109 assertEquals( //
1110 ".unit(METER).perUnit(SECOND) normalization", //
1111 u"unit/meter-per-second",
1112 NumberFormatter::with().unit(METER).perUnit(SECOND).toSkeleton(status));
1113 assertEquals( //
1114 ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", //
1115 u"unit/hectometer",
1116 NumberFormatter::with()
1117 .unit(MeasureUnit::forIdentifier("hectometer", status))
1118 .toSkeleton(status));
1119 assertEquals( //
1120 ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", //
1121 u"unit/meter-per-hectosecond",
1122 NumberFormatter::with()
1123 .unit(METER)
1124 .perUnit(MeasureUnit::forIdentifier("hectosecond", status))
1125 .toSkeleton(status));
1126
1127 status.assertSuccess();
1128 assertEquals( //
1129 ".unit(CURRENCY) produces a currency/CURRENCY skeleton", //
1130 u"currency/GBP", //
1131 NumberFormatter::with().unit(GBP).toSkeleton(status));
1132 status.assertSuccess();
1133 // .unit(CURRENCY).perUnit(ANYTHING) is not supported.
1134 NumberFormatter::with().unit(GBP).perUnit(METER).toSkeleton(status);
1135 status.expectErrorAndReset(U_UNSUPPORTED_ERROR);
1136 }
1137
unitUsage()1138 void NumberFormatterApiTest::unitUsage() {
1139 IcuTestErrorCode status(*this, "unitUsage()");
1140 UnlocalizedNumberFormatter unloc_formatter;
1141 LocalizedNumberFormatter formatter;
1142 FormattedNumber formattedNum;
1143 UnicodeString uTestCase;
1144
1145 status.assertSuccess();
1146 formattedNum =
1147 NumberFormatter::with().usage("road").locale(Locale::getEnglish()).formatInt(1, status);
1148 status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1149
1150 unloc_formatter = NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter());
1151
1152 uTestCase = u"unitUsage() en-ZA road";
1153 formatter = unloc_formatter.locale("en-ZA");
1154 formattedNum = formatter.formatDouble(321, status);
1155 status.errIfFailureAndReset("unitUsage() en-ZA road formatDouble");
1156 assertTrue(
1157 uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1158 MeasureUnit::getMeter() == formattedNum.getOutputUnit(status));
1159 assertEquals(uTestCase, "300 m", formattedNum.toString(status));
1160 {
1161 static const UFieldPosition expectedFieldPositions[] = {
1162 {UNUM_INTEGER_FIELD, 0, 3},
1163 {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
1164 assertNumberFieldPositions(
1165 (uTestCase + u" field positions").getTerminatedBuffer(),
1166 formattedNum,
1167 expectedFieldPositions,
1168 UPRV_LENGTHOF(expectedFieldPositions));
1169 }
1170 assertFormatDescendingBig(
1171 uTestCase.getTerminatedBuffer(),
1172 u"measure-unit/length-meter usage/road",
1173 u"unit/meter usage/road",
1174 unloc_formatter,
1175 Locale("en-ZA"),
1176 u"87\u00A0650 km",
1177 u"8\u00A0765 km",
1178 u"876 km", // 6.5 rounds down, 7.5 rounds up.
1179 u"88 km",
1180 u"8,8 km",
1181 u"900 m",
1182 u"90 m",
1183 u"10 m",
1184 u"0 m");
1185
1186 uTestCase = u"unitUsage() en-GB road";
1187 formatter = unloc_formatter.locale("en-GB");
1188 formattedNum = formatter.formatDouble(321, status);
1189 status.errIfFailureAndReset("unitUsage() en-GB road, formatDouble(...)");
1190 assertTrue(
1191 uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1192 MeasureUnit::getYard() == formattedNum.getOutputUnit(status));
1193 status.errIfFailureAndReset("unitUsage() en-GB road, getOutputUnit(...)");
1194 assertEquals(uTestCase, "350 yd", formattedNum.toString(status));
1195 status.errIfFailureAndReset("unitUsage() en-GB road, toString(...)");
1196 {
1197 static const UFieldPosition expectedFieldPositions[] = {
1198 {UNUM_INTEGER_FIELD, 0, 3},
1199 {UNUM_MEASURE_UNIT_FIELD, 4, 6}};
1200 assertNumberFieldPositions(
1201 (uTestCase + u" field positions").getTerminatedBuffer(),
1202 formattedNum,
1203 expectedFieldPositions,
1204 UPRV_LENGTHOF(expectedFieldPositions));
1205 }
1206 assertFormatDescendingBig(
1207 uTestCase.getTerminatedBuffer(),
1208 u"measure-unit/length-meter usage/road",
1209 u"unit/meter usage/road",
1210 unloc_formatter,
1211 Locale("en-GB"),
1212 u"54,463 mi",
1213 u"5,446 mi",
1214 u"545 mi",
1215 u"54 mi",
1216 u"5.4 mi",
1217 u"0.54 mi",
1218 u"96 yd",
1219 u"9.6 yd",
1220 u"0 yd");
1221
1222 uTestCase = u"unitUsage() en-US road";
1223 formatter = unloc_formatter.locale("en-US");
1224 formattedNum = formatter.formatDouble(321, status);
1225 status.errIfFailureAndReset("unitUsage() en-US road, formatDouble(...)");
1226 assertTrue(
1227 uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1228 MeasureUnit::getFoot() == formattedNum.getOutputUnit(status));
1229 status.errIfFailureAndReset("unitUsage() en-US road, getOutputUnit(...)");
1230 assertEquals(uTestCase, "1,050 ft", formattedNum.toString(status));
1231 status.errIfFailureAndReset("unitUsage() en-US road, toString(...)");
1232 {
1233 static const UFieldPosition expectedFieldPositions[] = {
1234 {UNUM_GROUPING_SEPARATOR_FIELD, 1, 2},
1235 {UNUM_INTEGER_FIELD, 0, 5},
1236 {UNUM_MEASURE_UNIT_FIELD, 6, 8}};
1237 assertNumberFieldPositions(
1238 (uTestCase + u" field positions").getTerminatedBuffer(),
1239 formattedNum,
1240 expectedFieldPositions,
1241 UPRV_LENGTHOF(expectedFieldPositions));
1242 }
1243 assertFormatDescendingBig(
1244 uTestCase.getTerminatedBuffer(),
1245 u"measure-unit/length-meter usage/road",
1246 u"unit/meter usage/road",
1247 unloc_formatter,
1248 Locale("en-US"),
1249 u"54,463 mi",
1250 u"5,446 mi",
1251 u"545 mi",
1252 u"54 mi",
1253 u"5.4 mi",
1254 u"0.54 mi",
1255 u"300 ft",
1256 u"30 ft",
1257 u"0 ft");
1258
1259 unloc_formatter = NumberFormatter::with().usage("person").unit(MeasureUnit::getKilogram());
1260 uTestCase = u"unitUsage() en-GB person";
1261 formatter = unloc_formatter.locale("en-GB");
1262 formattedNum = formatter.formatDouble(80, status);
1263 status.errIfFailureAndReset("unitUsage() en-GB person formatDouble");
1264 assertTrue(
1265 uTestCase + ", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"",
1266 MeasureUnit::forIdentifier("stone-and-pound", status) == formattedNum.getOutputUnit(status));
1267 status.errIfFailureAndReset("unitUsage() en-GB person - formattedNum.getOutputUnit(status)");
1268 assertEquals(uTestCase, "12 st, 8.4 lb", formattedNum.toString(status));
1269 status.errIfFailureAndReset("unitUsage() en-GB person, toString(...)");
1270 {
1271 static const UFieldPosition expectedFieldPositions[] = {
1272 // // Desired output: TODO(icu-units#67)
1273 // {UNUM_INTEGER_FIELD, 0, 2},
1274 // {UNUM_MEASURE_UNIT_FIELD, 3, 5},
1275 // {ULISTFMT_LITERAL_FIELD, 5, 6},
1276 // {UNUM_INTEGER_FIELD, 7, 8},
1277 // {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
1278 // {UNUM_FRACTION_FIELD, 9, 10},
1279 // {UNUM_MEASURE_UNIT_FIELD, 11, 13}};
1280
1281 // Current output: rather no fields than wrong fields
1282 {UNUM_INTEGER_FIELD, 7, 8},
1283 {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
1284 {UNUM_FRACTION_FIELD, 9, 10},
1285 };
1286 assertNumberFieldPositions(
1287 (uTestCase + u" field positions").getTerminatedBuffer(),
1288 formattedNum,
1289 expectedFieldPositions,
1290 UPRV_LENGTHOF(expectedFieldPositions));
1291 }
1292 assertFormatDescending(
1293 uTestCase.getTerminatedBuffer(),
1294 u"measure-unit/mass-kilogram usage/person",
1295 u"unit/kilogram usage/person",
1296 unloc_formatter,
1297 Locale("en-GB"),
1298 u"13,802 st, 7.2 lb",
1299 u"1,380 st, 3.5 lb",
1300 u"138 st, 0.35 lb",
1301 u"13 st, 11 lb",
1302 u"1 st, 5.3 lb",
1303 u"1 lb, 15 oz",
1304 u"0 lb, 3.1 oz",
1305 u"0 lb, 0.31 oz",
1306 u"0 lb, 0 oz");
1307
1308 assertFormatDescending(
1309 uTestCase.getTerminatedBuffer(),
1310 u"usage/person unit-width-narrow measure-unit/mass-kilogram",
1311 u"usage/person unit-width-narrow unit/kilogram",
1312 unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_NARROW),
1313 Locale("en-GB"),
1314 u"13,802st 7.2lb",
1315 u"1,380st 3.5lb",
1316 u"138st 0.35lb",
1317 u"13st 11lb",
1318 u"1st 5.3lb",
1319 u"1lb 15oz",
1320 u"0lb 3.1oz",
1321 u"0lb 0.31oz",
1322 u"0lb 0oz");
1323
1324 assertFormatDescending(
1325 uTestCase.getTerminatedBuffer(),
1326 u"usage/person unit-width-short measure-unit/mass-kilogram",
1327 u"usage/person unit-width-short unit/kilogram",
1328 unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_SHORT),
1329 Locale("en-GB"),
1330 u"13,802 st, 7.2 lb",
1331 u"1,380 st, 3.5 lb",
1332 u"138 st, 0.35 lb",
1333 u"13 st, 11 lb",
1334 u"1 st, 5.3 lb",
1335 u"1 lb, 15 oz",
1336 u"0 lb, 3.1 oz",
1337 u"0 lb, 0.31 oz",
1338 u"0 lb, 0 oz");
1339
1340 assertFormatDescending(
1341 uTestCase.getTerminatedBuffer(),
1342 u"usage/person unit-width-full-name measure-unit/mass-kilogram",
1343 u"usage/person unit-width-full-name unit/kilogram",
1344 unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1345 Locale("en-GB"),
1346 u"13,802 stone, 7.2 pounds",
1347 u"1,380 stone, 3.5 pounds",
1348 u"138 stone, 0.35 pounds",
1349 u"13 stone, 11 pounds",
1350 u"1 stone, 5.3 pounds",
1351 u"1 pound, 15 ounces",
1352 u"0 pounds, 3.1 ounces",
1353 u"0 pounds, 0.31 ounces",
1354 u"0 pounds, 0 ounces");
1355
1356 assertFormatDescendingBig(
1357 u"Scientific notation with Usage: possible when using a reasonable Precision",
1358 u"scientific @### usage/default measure-unit/area-square-meter unit-width-full-name",
1359 u"scientific @### usage/default unit/square-meter unit-width-full-name",
1360 NumberFormatter::with()
1361 .unit(SQUARE_METER)
1362 .usage("default")
1363 .notation(Notation::scientific())
1364 .precision(Precision::minMaxSignificantDigits(1, 4))
1365 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1366 Locale("en-ZA"),
1367 u"8,765E1 square kilometres",
1368 u"8,765E0 square kilometres",
1369 u"8,765E1 hectares",
1370 u"8,765E0 hectares",
1371 u"8,765E3 square metres",
1372 u"8,765E2 square metres",
1373 u"8,765E1 square metres",
1374 u"8,765E0 square metres",
1375 u"0E0 square centimetres");
1376
1377 assertFormatSingle(
1378 u"Negative numbers: minute-and-second",
1379 u"measure-unit/duration-second usage/media",
1380 u"unit/second usage/media",
1381 NumberFormatter::with().unit(SECOND).usage("media"),
1382 Locale("nl-NL"),
1383 -77.7,
1384 u"-1 min, 18 sec");
1385
1386 assertFormatSingle(
1387 u"Rounding Mode propagates: rounding down",
1388 u"usage/road measure-unit/length-centimeter rounding-mode-floor",
1389 u"usage/road unit/centimeter rounding-mode-floor",
1390 NumberFormatter::with()
1391 .unit(MeasureUnit::forIdentifier("centimeter", status))
1392 .usage("road")
1393 .roundingMode(UNUM_ROUND_FLOOR),
1394 Locale("en-ZA"),
1395 34500,
1396 u"300 m");
1397
1398 assertFormatSingle(
1399 u"Rounding Mode propagates: rounding up",
1400 u"usage/road measure-unit/length-centimeter rounding-mode-ceiling",
1401 u"usage/road unit/centimeter rounding-mode-ceiling",
1402 NumberFormatter::with()
1403 .unit(MeasureUnit::forIdentifier("centimeter", status))
1404 .usage("road")
1405 .roundingMode(UNUM_ROUND_CEILING),
1406 Locale("en-ZA"),
1407 30500,
1408 u"350 m");
1409
1410 // TODO(icu-units#38): improve unit testing coverage. E.g. add vehicle-fuel
1411 // triggering inversion conversion code. Test with 0 too, to see
1412 // divide-by-zero behaviour.
1413 }
1414
unitUsageErrorCodes()1415 void NumberFormatterApiTest::unitUsageErrorCodes() {
1416 IcuTestErrorCode status(*this, "unitUsageErrorCodes()");
1417 UnlocalizedNumberFormatter unloc_formatter;
1418
1419 unloc_formatter = NumberFormatter::forSkeleton(u"unit/foobar", status);
1420 // This gives an error, because foobar is an invalid unit:
1421 status.expectErrorAndReset(U_NUMBER_SKELETON_SYNTAX_ERROR);
1422
1423 unloc_formatter = NumberFormatter::forSkeleton(u"usage/foobar", status);
1424 // This does not give an error, because usage is not looked up yet.
1425 status.errIfFailureAndReset("Expected behaviour: no immediate error for invalid usage");
1426 unloc_formatter.locale("en-GB").formatInt(1, status);
1427 // Lacking a unit results in a failure. The skeleton is "incomplete", but we
1428 // support adding the unit via the fluent API, so it is not an error until
1429 // we build the formatting pipeline itself.
1430 status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
1431 // Adding the unit as part of the fluent chain leads to success.
1432 unloc_formatter.unit(MeasureUnit::getMeter()).locale("en-GB").formatInt(1, status);
1433 status.assertSuccess();
1434 }
1435
1436 // Tests for the "skeletons" field in unitPreferenceData, as well as precision
1437 // and notation overrides.
unitUsageSkeletons()1438 void NumberFormatterApiTest::unitUsageSkeletons() {
1439 IcuTestErrorCode status(*this, "unitUsageSkeletons()");
1440
1441 assertFormatSingle(
1442 u"Default >300m road preference skeletons round to 50m",
1443 u"usage/road measure-unit/length-meter",
1444 u"usage/road unit/meter",
1445 NumberFormatter::with().unit(METER).usage("road"),
1446 Locale("en-ZA"),
1447 321,
1448 u"300 m");
1449
1450 assertFormatSingle(
1451 u"Precision can be overridden: override takes precedence",
1452 u"usage/road measure-unit/length-meter @#",
1453 u"usage/road unit/meter @#",
1454 NumberFormatter::with()
1455 .unit(METER)
1456 .usage("road")
1457 .precision(Precision::maxSignificantDigits(2)),
1458 Locale("en-ZA"),
1459 321,
1460 u"320 m");
1461
1462 assertFormatSingle(
1463 u"Compact notation with Usage: bizarre, but possible (short)",
1464 u"compact-short usage/road measure-unit/length-meter",
1465 u"compact-short usage/road unit/meter",
1466 NumberFormatter::with()
1467 .unit(METER)
1468 .usage("road")
1469 .notation(Notation::compactShort()),
1470 Locale("en-ZA"),
1471 987654321,
1472 u"988K km");
1473
1474 assertFormatSingle(
1475 u"Compact notation with Usage: bizarre, but possible (short, precision override)",
1476 u"compact-short usage/road measure-unit/length-meter @#",
1477 u"compact-short usage/road unit/meter @#",
1478 NumberFormatter::with()
1479 .unit(METER)
1480 .usage("road")
1481 .notation(Notation::compactShort())
1482 .precision(Precision::maxSignificantDigits(2)),
1483 Locale("en-ZA"),
1484 987654321,
1485 u"990K km");
1486
1487 assertFormatSingle(
1488 u"Compact notation with Usage: unusual but possible (long)",
1489 u"compact-long usage/road measure-unit/length-meter @#",
1490 u"compact-long usage/road unit/meter @#",
1491 NumberFormatter::with()
1492 .unit(METER)
1493 .usage("road")
1494 .notation(Notation::compactLong())
1495 .precision(Precision::maxSignificantDigits(2)),
1496 Locale("en-ZA"),
1497 987654321,
1498 u"990 thousand km");
1499
1500 assertFormatSingle(
1501 u"Compact notation with Usage: unusual but possible (long, precision override)",
1502 u"compact-long usage/road measure-unit/length-meter @#",
1503 u"compact-long usage/road unit/meter @#",
1504 NumberFormatter::with()
1505 .unit(METER)
1506 .usage("road")
1507 .notation(Notation::compactLong())
1508 .precision(Precision::maxSignificantDigits(2)),
1509 Locale("en-ZA"),
1510 987654321,
1511 u"990 thousand km");
1512
1513 assertFormatSingle(
1514 u"Scientific notation, not recommended, requires precision override for road",
1515 u"scientific usage/road measure-unit/length-meter",
1516 u"scientific usage/road unit/meter",
1517 NumberFormatter::with().unit(METER).usage("road").notation(Notation::scientific()),
1518 Locale("en-ZA"),
1519 321.45,
1520 // Rounding to the nearest "50" is not exponent-adjusted in scientific notation:
1521 u"0E2 m");
1522
1523 assertFormatSingle(
1524 u"Scientific notation with Usage: possible when using a reasonable Precision",
1525 u"scientific usage/road measure-unit/length-meter @###",
1526 u"scientific usage/road unit/meter @###",
1527 NumberFormatter::with()
1528 .unit(METER)
1529 .usage("road")
1530 .notation(Notation::scientific())
1531 .precision(Precision::maxSignificantDigits(4)),
1532 Locale("en-ZA"),
1533 321.45, // 0.45 rounds down, 0.55 rounds up.
1534 u"3,214E2 m");
1535
1536 assertFormatSingle(
1537 u"Scientific notation with Usage: possible when using a reasonable Precision",
1538 u"scientific usage/default measure-unit/length-astronomical-unit unit-width-full-name",
1539 u"scientific usage/default unit/astronomical-unit unit-width-full-name",
1540 NumberFormatter::with()
1541 .unit(MeasureUnit::forIdentifier("astronomical-unit", status))
1542 .usage("default")
1543 .notation(Notation::scientific())
1544 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1545 Locale("en-ZA"),
1546 1e20,
1547 u"1,5E28 kilometres");
1548
1549 status.assertSuccess();
1550 }
1551
unitCurrency()1552 void NumberFormatterApiTest::unitCurrency() {
1553 assertFormatDescending(
1554 u"Currency",
1555 u"currency/GBP",
1556 u"currency/GBP",
1557 NumberFormatter::with().unit(GBP),
1558 Locale::getEnglish(),
1559 u"£87,650.00",
1560 u"£8,765.00",
1561 u"£876.50",
1562 u"£87.65",
1563 u"£8.76",
1564 u"£0.88",
1565 u"£0.09",
1566 u"£0.01",
1567 u"£0.00");
1568
1569 assertFormatDescending(
1570 u"Currency ISO",
1571 u"currency/GBP unit-width-iso-code",
1572 u"currency/GBP unit-width-iso-code",
1573 NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
1574 Locale::getEnglish(),
1575 u"GBP 87,650.00",
1576 u"GBP 8,765.00",
1577 u"GBP 876.50",
1578 u"GBP 87.65",
1579 u"GBP 8.76",
1580 u"GBP 0.88",
1581 u"GBP 0.09",
1582 u"GBP 0.01",
1583 u"GBP 0.00");
1584
1585 assertFormatDescending(
1586 u"Currency Long Name",
1587 u"currency/GBP unit-width-full-name",
1588 u"currency/GBP unit-width-full-name",
1589 NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
1590 Locale::getEnglish(),
1591 u"87,650.00 British pounds",
1592 u"8,765.00 British pounds",
1593 u"876.50 British pounds",
1594 u"87.65 British pounds",
1595 u"8.76 British pounds",
1596 u"0.88 British pounds",
1597 u"0.09 British pounds",
1598 u"0.01 British pounds",
1599 u"0.00 British pounds");
1600
1601 assertFormatDescending(
1602 u"Currency Hidden",
1603 u"currency/GBP unit-width-hidden",
1604 u"currency/GBP unit-width-hidden",
1605 NumberFormatter::with().unit(GBP).unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
1606 Locale::getEnglish(),
1607 u"87,650.00",
1608 u"8,765.00",
1609 u"876.50",
1610 u"87.65",
1611 u"8.76",
1612 u"0.88",
1613 u"0.09",
1614 u"0.01",
1615 u"0.00");
1616
1617 // TODO: Implement Measure in C++
1618 // assertFormatSingleMeasure(
1619 // u"Currency with CurrencyAmount Input",
1620 // NumberFormatter::with(),
1621 // Locale::getEnglish(),
1622 // new CurrencyAmount(5.43, GBP),
1623 // u"£5.43");
1624
1625 // TODO: Enable this test when DecimalFormat wrapper is done.
1626 // assertFormatSingle(
1627 // u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat(
1628 // PatternStringParser.parseToProperties("0 ¤¤¤"),
1629 // DecimalFormatSymbols.getInstance(Locale::getEnglish()),
1630 // null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds");
1631
1632 assertFormatSingle(
1633 u"Currency with Negative Sign",
1634 u"currency/GBP",
1635 u"currency/GBP",
1636 NumberFormatter::with().unit(GBP),
1637 Locale::getEnglish(),
1638 -9876543.21,
1639 u"-£9,876,543.21");
1640
1641 // The full currency symbol is not shown in NARROW format.
1642 // NOTE: This example is in the documentation.
1643 assertFormatSingle(
1644 u"Currency Difference between Narrow and Short (Narrow Version)",
1645 u"currency/USD unit-width-narrow",
1646 u"currency/USD unit-width-narrow",
1647 NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_NARROW),
1648 Locale("en-CA"),
1649 5.43,
1650 u"$5.43");
1651
1652 assertFormatSingle(
1653 u"Currency Difference between Narrow and Short (Short Version)",
1654 u"currency/USD unit-width-short",
1655 u"currency/USD unit-width-short",
1656 NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1657 Locale("en-CA"),
1658 5.43,
1659 u"US$5.43");
1660
1661 assertFormatSingle(
1662 u"Currency Difference between Formal and Short (Formal Version)",
1663 u"currency/TWD unit-width-formal",
1664 u"currency/TWD unit-width-formal",
1665 NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_FORMAL),
1666 Locale("zh-TW"),
1667 5.43,
1668 u"NT$5.43");
1669
1670 assertFormatSingle(
1671 u"Currency Difference between Formal and Short (Short Version)",
1672 u"currency/TWD unit-width-short",
1673 u"currency/TWD unit-width-short",
1674 NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1675 Locale("zh-TW"),
1676 5.43,
1677 u"$5.43");
1678
1679 assertFormatSingle(
1680 u"Currency Difference between Variant and Short (Formal Version)",
1681 u"currency/TRY unit-width-variant",
1682 u"currency/TRY unit-width-variant",
1683 NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_VARIANT),
1684 Locale("tr-TR"),
1685 5.43,
1686 u"TL\u00A05,43");
1687
1688 assertFormatSingle(
1689 u"Currency Difference between Variant and Short (Short Version)",
1690 u"currency/TRY unit-width-short",
1691 u"currency/TRY unit-width-short",
1692 NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1693 Locale("tr-TR"),
1694 5.43,
1695 u"₺5,43");
1696
1697 assertFormatSingle(
1698 u"Currency-dependent format (Control)",
1699 u"currency/USD unit-width-short",
1700 u"currency/USD unit-width-short",
1701 NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1702 Locale("ca"),
1703 444444.55,
1704 u"444.444,55 USD");
1705
1706 assertFormatSingle(
1707 u"Currency-dependent format (Test)",
1708 u"currency/ESP unit-width-short",
1709 u"currency/ESP unit-width-short",
1710 NumberFormatter::with().unit(ESP).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1711 Locale("ca"),
1712 444444.55,
1713 u"₧ 444.445");
1714
1715 assertFormatSingle(
1716 u"Currency-dependent symbols (Control)",
1717 u"currency/USD unit-width-short",
1718 u"currency/USD unit-width-short",
1719 NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1720 Locale("pt-PT"),
1721 444444.55,
1722 u"444 444,55 US$");
1723
1724 // NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero-
1725 // width space), and they set the decimal separator to the $ symbol.
1726 assertFormatSingle(
1727 u"Currency-dependent symbols (Test Short)",
1728 u"currency/PTE unit-width-short",
1729 u"currency/PTE unit-width-short",
1730 NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_SHORT),
1731 Locale("pt-PT"),
1732 444444.55,
1733 u"444,444$55 \u200B");
1734
1735 assertFormatSingle(
1736 u"Currency-dependent symbols (Test Narrow)",
1737 u"currency/PTE unit-width-narrow",
1738 u"currency/PTE unit-width-narrow",
1739 NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_NARROW),
1740 Locale("pt-PT"),
1741 444444.55,
1742 u"444,444$55 \u200B");
1743
1744 assertFormatSingle(
1745 u"Currency-dependent symbols (Test ISO Code)",
1746 u"currency/PTE unit-width-iso-code",
1747 u"currency/PTE unit-width-iso-code",
1748 NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
1749 Locale("pt-PT"),
1750 444444.55,
1751 u"444,444$55 PTE");
1752
1753 assertFormatSingle(
1754 u"Plural form depending on visible digits (ICU-20499)",
1755 u"currency/RON unit-width-full-name",
1756 u"currency/RON unit-width-full-name",
1757 NumberFormatter::with().unit(RON).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1758 Locale("ro-RO"),
1759 24,
1760 u"24,00 lei românești");
1761
1762 assertFormatSingle(
1763 u"Currency spacing in suffix (ICU-20954)",
1764 u"currency/CNY",
1765 u"currency/CNY",
1766 NumberFormatter::with().unit(CNY),
1767 Locale("lu"),
1768 123.12,
1769 u"123,12 CN¥");
1770 }
1771
unitPercent()1772 void NumberFormatterApiTest::unitPercent() {
1773 assertFormatDescending(
1774 u"Percent",
1775 u"percent",
1776 u"%",
1777 NumberFormatter::with().unit(NoUnit::percent()),
1778 Locale::getEnglish(),
1779 u"87,650%",
1780 u"8,765%",
1781 u"876.5%",
1782 u"87.65%",
1783 u"8.765%",
1784 u"0.8765%",
1785 u"0.08765%",
1786 u"0.008765%",
1787 u"0%");
1788
1789 assertFormatDescending(
1790 u"Permille",
1791 u"permille",
1792 u"permille",
1793 NumberFormatter::with().unit(NoUnit::permille()),
1794 Locale::getEnglish(),
1795 u"87,650‰",
1796 u"8,765‰",
1797 u"876.5‰",
1798 u"87.65‰",
1799 u"8.765‰",
1800 u"0.8765‰",
1801 u"0.08765‰",
1802 u"0.008765‰",
1803 u"0‰");
1804
1805 assertFormatSingle(
1806 u"NoUnit Base",
1807 u"base-unit",
1808 u"",
1809 NumberFormatter::with().unit(NoUnit::base()),
1810 Locale::getEnglish(),
1811 51423,
1812 u"51,423");
1813
1814 assertFormatSingle(
1815 u"Percent with Negative Sign",
1816 u"percent",
1817 u"%",
1818 NumberFormatter::with().unit(NoUnit::percent()),
1819 Locale::getEnglish(),
1820 -98.7654321,
1821 u"-98.765432%");
1822
1823 // ICU-20923
1824 assertFormatDescendingBig(
1825 u"Compact Percent",
1826 u"compact-short percent",
1827 u"K %",
1828 NumberFormatter::with()
1829 .notation(Notation::compactShort())
1830 .unit(NoUnit::percent()),
1831 Locale::getEnglish(),
1832 u"88M%",
1833 u"8.8M%",
1834 u"876K%",
1835 u"88K%",
1836 u"8.8K%",
1837 u"876%",
1838 u"88%",
1839 u"8.8%",
1840 u"0%");
1841
1842 // ICU-20923
1843 assertFormatDescendingBig(
1844 u"Compact Percent with Scale",
1845 u"compact-short percent scale/100",
1846 u"K %x100",
1847 NumberFormatter::with()
1848 .notation(Notation::compactShort())
1849 .unit(NoUnit::percent())
1850 .scale(Scale::powerOfTen(2)),
1851 Locale::getEnglish(),
1852 u"8.8B%",
1853 u"876M%",
1854 u"88M%",
1855 u"8.8M%",
1856 u"876K%",
1857 u"88K%",
1858 u"8.8K%",
1859 u"876%",
1860 u"0%");
1861
1862 // ICU-20923
1863 assertFormatDescendingBig(
1864 u"Compact Percent Long Name",
1865 u"compact-short percent unit-width-full-name",
1866 u"K % unit-width-full-name",
1867 NumberFormatter::with()
1868 .notation(Notation::compactShort())
1869 .unit(NoUnit::percent())
1870 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1871 Locale::getEnglish(),
1872 u"88M percent",
1873 u"8.8M percent",
1874 u"876K percent",
1875 u"88K percent",
1876 u"8.8K percent",
1877 u"876 percent",
1878 u"88 percent",
1879 u"8.8 percent",
1880 u"0 percent");
1881
1882 assertFormatSingle(
1883 u"Per Percent",
1884 u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",
1885 u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name",
1886 NumberFormatter::with()
1887 .unit(MeasureUnit::getMeter())
1888 .perUnit(MeasureUnit::getPercent())
1889 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
1890 Locale::getEnglish(),
1891 50,
1892 u"50 meters per percent");
1893 }
1894
percentParity()1895 void NumberFormatterApiTest::percentParity() {
1896 IcuTestErrorCode status(*this, "percentParity");
1897 UnlocalizedNumberFormatter uNoUnitPercent = NumberFormatter::with().unit(NoUnit::percent());
1898 UnlocalizedNumberFormatter uNoUnitPermille = NumberFormatter::with().unit(NoUnit::permille());
1899 UnlocalizedNumberFormatter uMeasurePercent = NumberFormatter::with().unit(MeasureUnit::getPercent());
1900 UnlocalizedNumberFormatter uMeasurePermille = NumberFormatter::with().unit(MeasureUnit::getPermille());
1901
1902 int32_t localeCount;
1903 auto locales = Locale::getAvailableLocales(localeCount);
1904 for (int32_t i=0; i<localeCount; i++) {
1905 auto& locale = locales[i];
1906 UnicodeString sNoUnitPercent = uNoUnitPercent.locale(locale)
1907 .formatDouble(50, status).toString(status);
1908 UnicodeString sNoUnitPermille = uNoUnitPermille.locale(locale)
1909 .formatDouble(50, status).toString(status);
1910 UnicodeString sMeasurePercent = uMeasurePercent.locale(locale)
1911 .formatDouble(50, status).toString(status);
1912 UnicodeString sMeasurePermille = uMeasurePermille.locale(locale)
1913 .formatDouble(50, status).toString(status);
1914
1915 assertEquals(u"Percent, locale " + UnicodeString(locale.getName()),
1916 sNoUnitPercent, sMeasurePercent);
1917 assertEquals(u"Permille, locale " + UnicodeString(locale.getName()),
1918 sNoUnitPermille, sMeasurePermille);
1919 }
1920 }
1921
roundingFraction()1922 void NumberFormatterApiTest::roundingFraction() {
1923 assertFormatDescending(
1924 u"Integer",
1925 u"precision-integer",
1926 u".",
1927 NumberFormatter::with().precision(Precision::integer()),
1928 Locale::getEnglish(),
1929 u"87,650",
1930 u"8,765",
1931 u"876",
1932 u"88",
1933 u"9",
1934 u"1",
1935 u"0",
1936 u"0",
1937 u"0");
1938
1939 assertFormatDescending(
1940 u"Fixed Fraction",
1941 u".000",
1942 u".000",
1943 NumberFormatter::with().precision(Precision::fixedFraction(3)),
1944 Locale::getEnglish(),
1945 u"87,650.000",
1946 u"8,765.000",
1947 u"876.500",
1948 u"87.650",
1949 u"8.765",
1950 u"0.876",
1951 u"0.088",
1952 u"0.009",
1953 u"0.000");
1954
1955 assertFormatDescending(
1956 u"Min Fraction",
1957 u".0*",
1958 u".0+",
1959 NumberFormatter::with().precision(Precision::minFraction(1)),
1960 Locale::getEnglish(),
1961 u"87,650.0",
1962 u"8,765.0",
1963 u"876.5",
1964 u"87.65",
1965 u"8.765",
1966 u"0.8765",
1967 u"0.08765",
1968 u"0.008765",
1969 u"0.0");
1970
1971 assertFormatDescending(
1972 u"Max Fraction",
1973 u".#",
1974 u".#",
1975 NumberFormatter::with().precision(Precision::maxFraction(1)),
1976 Locale::getEnglish(),
1977 u"87,650",
1978 u"8,765",
1979 u"876.5",
1980 u"87.6",
1981 u"8.8",
1982 u"0.9",
1983 u"0.1",
1984 u"0",
1985 u"0");
1986
1987 assertFormatDescending(
1988 u"Min/Max Fraction",
1989 u".0##",
1990 u".0##",
1991 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)),
1992 Locale::getEnglish(),
1993 u"87,650.0",
1994 u"8,765.0",
1995 u"876.5",
1996 u"87.65",
1997 u"8.765",
1998 u"0.876",
1999 u"0.088",
2000 u"0.009",
2001 u"0.0");
2002 }
2003
roundingFigures()2004 void NumberFormatterApiTest::roundingFigures() {
2005 assertFormatSingle(
2006 u"Fixed Significant",
2007 u"@@@",
2008 u"@@@",
2009 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
2010 Locale::getEnglish(),
2011 -98,
2012 u"-98.0");
2013
2014 assertFormatSingle(
2015 u"Fixed Significant Rounding",
2016 u"@@@",
2017 u"@@@",
2018 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
2019 Locale::getEnglish(),
2020 -98.7654321,
2021 u"-98.8");
2022
2023 assertFormatSingle(
2024 u"Fixed Significant Zero",
2025 u"@@@",
2026 u"@@@",
2027 NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)),
2028 Locale::getEnglish(),
2029 0,
2030 u"0.00");
2031
2032 assertFormatSingle(
2033 u"Min Significant",
2034 u"@@*",
2035 u"@@+",
2036 NumberFormatter::with().precision(Precision::minSignificantDigits(2)),
2037 Locale::getEnglish(),
2038 -9,
2039 u"-9.0");
2040
2041 assertFormatSingle(
2042 u"Max Significant",
2043 u"@###",
2044 u"@###",
2045 NumberFormatter::with().precision(Precision::maxSignificantDigits(4)),
2046 Locale::getEnglish(),
2047 98.7654321,
2048 u"98.77");
2049
2050 assertFormatSingle(
2051 u"Min/Max Significant",
2052 u"@@@#",
2053 u"@@@#",
2054 NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)),
2055 Locale::getEnglish(),
2056 9.99999,
2057 u"10.0");
2058
2059 assertFormatSingle(
2060 u"Fixed Significant on zero with lots of integer width",
2061 u"@ integer-width/+000",
2062 u"@ 000",
2063 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
2064 .integerWidth(IntegerWidth::zeroFillTo(3)),
2065 Locale::getEnglish(),
2066 0,
2067 "000");
2068
2069 assertFormatSingle(
2070 u"Fixed Significant on zero with zero integer width",
2071 u"@ integer-width/*",
2072 u"@ integer-width/+",
2073 NumberFormatter::with().precision(Precision::fixedSignificantDigits(1))
2074 .integerWidth(IntegerWidth::zeroFillTo(0)),
2075 Locale::getEnglish(),
2076 0,
2077 "0");
2078 }
2079
roundingFractionFigures()2080 void NumberFormatterApiTest::roundingFractionFigures() {
2081 assertFormatDescending(
2082 u"Basic Significant", // for comparison
2083 u"@#",
2084 u"@#",
2085 NumberFormatter::with().precision(Precision::maxSignificantDigits(2)),
2086 Locale::getEnglish(),
2087 u"88,000",
2088 u"8,800",
2089 u"880",
2090 u"88",
2091 u"8.8",
2092 u"0.88",
2093 u"0.088",
2094 u"0.0088",
2095 u"0");
2096
2097 assertFormatDescending(
2098 u"FracSig minMaxFrac minSig",
2099 u".0#/@@@*",
2100 u".0#/@@@+",
2101 NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)),
2102 Locale::getEnglish(),
2103 u"87,650.0",
2104 u"8,765.0",
2105 u"876.5",
2106 u"87.65",
2107 u"8.76",
2108 u"0.876", // minSig beats maxFrac
2109 u"0.0876", // minSig beats maxFrac
2110 u"0.00876", // minSig beats maxFrac
2111 u"0.0");
2112
2113 assertFormatDescending(
2114 u"FracSig minMaxFrac maxSig A",
2115 u".0##/@#",
2116 u".0##/@#",
2117 NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)),
2118 Locale::getEnglish(),
2119 u"88,000.0", // maxSig beats maxFrac
2120 u"8,800.0", // maxSig beats maxFrac
2121 u"880.0", // maxSig beats maxFrac
2122 u"88.0", // maxSig beats maxFrac
2123 u"8.8", // maxSig beats maxFrac
2124 u"0.88", // maxSig beats maxFrac
2125 u"0.088",
2126 u"0.009",
2127 u"0.0");
2128
2129 assertFormatDescending(
2130 u"FracSig minMaxFrac maxSig B",
2131 u".00/@#",
2132 u".00/@#",
2133 NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)),
2134 Locale::getEnglish(),
2135 u"88,000.00", // maxSig beats maxFrac
2136 u"8,800.00", // maxSig beats maxFrac
2137 u"880.00", // maxSig beats maxFrac
2138 u"88.00", // maxSig beats maxFrac
2139 u"8.80", // maxSig beats maxFrac
2140 u"0.88",
2141 u"0.09",
2142 u"0.01",
2143 u"0.00");
2144
2145 assertFormatSingle(
2146 u"FracSig with trailing zeros A",
2147 u".00/@@@*",
2148 u".00/@@@+",
2149 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
2150 Locale::getEnglish(),
2151 0.1,
2152 u"0.10");
2153
2154 assertFormatSingle(
2155 u"FracSig with trailing zeros B",
2156 u".00/@@@*",
2157 u".00/@@@+",
2158 NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)),
2159 Locale::getEnglish(),
2160 0.0999999,
2161 u"0.10");
2162 }
2163
roundingOther()2164 void NumberFormatterApiTest::roundingOther() {
2165 assertFormatDescending(
2166 u"Rounding None",
2167 u"precision-unlimited",
2168 u".+",
2169 NumberFormatter::with().precision(Precision::unlimited()),
2170 Locale::getEnglish(),
2171 u"87,650",
2172 u"8,765",
2173 u"876.5",
2174 u"87.65",
2175 u"8.765",
2176 u"0.8765",
2177 u"0.08765",
2178 u"0.008765",
2179 u"0");
2180
2181 assertFormatDescending(
2182 u"Increment",
2183 u"precision-increment/0.5",
2184 u"precision-increment/0.5",
2185 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)),
2186 Locale::getEnglish(),
2187 u"87,650.0",
2188 u"8,765.0",
2189 u"876.5",
2190 u"87.5",
2191 u"9.0",
2192 u"1.0",
2193 u"0.0",
2194 u"0.0",
2195 u"0.0");
2196
2197 assertFormatDescending(
2198 u"Increment with Min Fraction",
2199 u"precision-increment/0.50",
2200 u"precision-increment/0.50",
2201 NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)),
2202 Locale::getEnglish(),
2203 u"87,650.00",
2204 u"8,765.00",
2205 u"876.50",
2206 u"87.50",
2207 u"9.00",
2208 u"1.00",
2209 u"0.00",
2210 u"0.00",
2211 u"0.00");
2212
2213 assertFormatDescending(
2214 u"Strange Increment",
2215 u"precision-increment/3.140",
2216 u"precision-increment/3.140",
2217 NumberFormatter::with().precision(Precision::increment(3.14).withMinFraction(3)),
2218 Locale::getEnglish(),
2219 u"87,649.960",
2220 u"8,763.740",
2221 u"876.060",
2222 u"87.920",
2223 u"9.420",
2224 u"0.000",
2225 u"0.000",
2226 u"0.000",
2227 u"0.000");
2228
2229 assertFormatDescending(
2230 u"Increment Resolving to Power of 10",
2231 u"precision-increment/0.010",
2232 u"precision-increment/0.010",
2233 NumberFormatter::with().precision(Precision::increment(0.01).withMinFraction(3)),
2234 Locale::getEnglish(),
2235 u"87,650.000",
2236 u"8,765.000",
2237 u"876.500",
2238 u"87.650",
2239 u"8.760",
2240 u"0.880",
2241 u"0.090",
2242 u"0.010",
2243 u"0.000");
2244
2245 assertFormatDescending(
2246 u"Currency Standard",
2247 u"currency/CZK precision-currency-standard",
2248 u"currency/CZK precision-currency-standard",
2249 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD))
2250 .unit(CZK),
2251 Locale::getEnglish(),
2252 u"CZK 87,650.00",
2253 u"CZK 8,765.00",
2254 u"CZK 876.50",
2255 u"CZK 87.65",
2256 u"CZK 8.76",
2257 u"CZK 0.88",
2258 u"CZK 0.09",
2259 u"CZK 0.01",
2260 u"CZK 0.00");
2261
2262 assertFormatDescending(
2263 u"Currency Cash",
2264 u"currency/CZK precision-currency-cash",
2265 u"currency/CZK precision-currency-cash",
2266 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
2267 .unit(CZK),
2268 Locale::getEnglish(),
2269 u"CZK 87,650",
2270 u"CZK 8,765",
2271 u"CZK 876",
2272 u"CZK 88",
2273 u"CZK 9",
2274 u"CZK 1",
2275 u"CZK 0",
2276 u"CZK 0",
2277 u"CZK 0");
2278
2279 assertFormatDescending(
2280 u"Currency Cash with Nickel Rounding",
2281 u"currency/CAD precision-currency-cash",
2282 u"currency/CAD precision-currency-cash",
2283 NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH))
2284 .unit(CAD),
2285 Locale::getEnglish(),
2286 u"CA$87,650.00",
2287 u"CA$8,765.00",
2288 u"CA$876.50",
2289 u"CA$87.65",
2290 u"CA$8.75",
2291 u"CA$0.90",
2292 u"CA$0.10",
2293 u"CA$0.00",
2294 u"CA$0.00");
2295
2296 assertFormatDescending(
2297 u"Currency not in top-level fluent chain",
2298 u"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
2299 u".",
2300 NumberFormatter::with().precision(
2301 Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH).withCurrency(CZK)),
2302 Locale::getEnglish(),
2303 u"87,650",
2304 u"8,765",
2305 u"876",
2306 u"88",
2307 u"9",
2308 u"1",
2309 u"0",
2310 u"0",
2311 u"0");
2312
2313 // NOTE: Other tests cover the behavior of the other rounding modes.
2314 assertFormatDescending(
2315 u"Rounding Mode CEILING",
2316 u"precision-integer rounding-mode-ceiling",
2317 u". rounding-mode-ceiling",
2318 NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING),
2319 Locale::getEnglish(),
2320 u"87,650",
2321 u"8,765",
2322 u"877",
2323 u"88",
2324 u"9",
2325 u"1",
2326 u"1",
2327 u"1",
2328 u"0");
2329
2330 assertFormatSingle(
2331 u"ICU-20974 Double.MIN_NORMAL",
2332 u"scientific",
2333 u"E0",
2334 NumberFormatter::with().notation(Notation::scientific()),
2335 Locale::getEnglish(),
2336 DBL_MIN,
2337 u"2.225074E-308");
2338
2339 #ifndef DBL_TRUE_MIN
2340 #define DBL_TRUE_MIN 4.9E-324
2341 #endif
2342
2343 // Note: this behavior is intentionally different from Java; see
2344 // https://github.com/google/double-conversion/issues/126
2345 assertFormatSingle(
2346 u"ICU-20974 Double.MIN_VALUE",
2347 u"scientific",
2348 u"E0",
2349 NumberFormatter::with().notation(Notation::scientific()),
2350 Locale::getEnglish(),
2351 DBL_TRUE_MIN,
2352 u"5E-324");
2353 }
2354
grouping()2355 void NumberFormatterApiTest::grouping() {
2356 assertFormatDescendingBig(
2357 u"Western Grouping",
2358 u"group-auto",
2359 u"",
2360 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
2361 Locale::getEnglish(),
2362 u"87,650,000",
2363 u"8,765,000",
2364 u"876,500",
2365 u"87,650",
2366 u"8,765",
2367 u"876.5",
2368 u"87.65",
2369 u"8.765",
2370 u"0");
2371
2372 assertFormatDescendingBig(
2373 u"Indic Grouping",
2374 u"group-auto",
2375 u"",
2376 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
2377 Locale("en-IN"),
2378 u"8,76,50,000",
2379 u"87,65,000",
2380 u"8,76,500",
2381 u"87,650",
2382 u"8,765",
2383 u"876.5",
2384 u"87.65",
2385 u"8.765",
2386 u"0");
2387
2388 assertFormatDescendingBig(
2389 u"Western Grouping, Min 2",
2390 u"group-min2",
2391 u",?",
2392 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
2393 Locale::getEnglish(),
2394 u"87,650,000",
2395 u"8,765,000",
2396 u"876,500",
2397 u"87,650",
2398 u"8765",
2399 u"876.5",
2400 u"87.65",
2401 u"8.765",
2402 u"0");
2403
2404 assertFormatDescendingBig(
2405 u"Indic Grouping, Min 2",
2406 u"group-min2",
2407 u",?",
2408 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
2409 Locale("en-IN"),
2410 u"8,76,50,000",
2411 u"87,65,000",
2412 u"8,76,500",
2413 u"87,650",
2414 u"8765",
2415 u"876.5",
2416 u"87.65",
2417 u"8.765",
2418 u"0");
2419
2420 assertFormatDescendingBig(
2421 u"No Grouping",
2422 u"group-off",
2423 u",_",
2424 NumberFormatter::with().grouping(UNUM_GROUPING_OFF),
2425 Locale("en-IN"),
2426 u"87650000",
2427 u"8765000",
2428 u"876500",
2429 u"87650",
2430 u"8765",
2431 u"876.5",
2432 u"87.65",
2433 u"8.765",
2434 u"0");
2435
2436 assertFormatDescendingBig(
2437 u"Indic locale with THOUSANDS grouping",
2438 u"group-thousands",
2439 u"group-thousands",
2440 NumberFormatter::with().grouping(UNUM_GROUPING_THOUSANDS),
2441 Locale("en-IN"),
2442 u"87,650,000",
2443 u"8,765,000",
2444 u"876,500",
2445 u"87,650",
2446 u"8,765",
2447 u"876.5",
2448 u"87.65",
2449 u"8.765",
2450 u"0");
2451
2452 // NOTE: Polish is interesting because it has minimumGroupingDigits=2 in locale data
2453 // (Most locales have either 1 or 2)
2454 // If this test breaks due to data changes, find another locale that has minimumGroupingDigits.
2455 assertFormatDescendingBig(
2456 u"Polish Grouping",
2457 u"group-auto",
2458 u"",
2459 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO),
2460 Locale("pl"),
2461 u"87 650 000",
2462 u"8 765 000",
2463 u"876 500",
2464 u"87 650",
2465 u"8765",
2466 u"876,5",
2467 u"87,65",
2468 u"8,765",
2469 u"0");
2470
2471 assertFormatDescendingBig(
2472 u"Polish Grouping, Min 2",
2473 u"group-min2",
2474 u",?",
2475 NumberFormatter::with().grouping(UNUM_GROUPING_MIN2),
2476 Locale("pl"),
2477 u"87 650 000",
2478 u"8 765 000",
2479 u"876 500",
2480 u"87 650",
2481 u"8765",
2482 u"876,5",
2483 u"87,65",
2484 u"8,765",
2485 u"0");
2486
2487 assertFormatDescendingBig(
2488 u"Polish Grouping, Always",
2489 u"group-on-aligned",
2490 u",!",
2491 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED),
2492 Locale("pl"),
2493 u"87 650 000",
2494 u"8 765 000",
2495 u"876 500",
2496 u"87 650",
2497 u"8 765",
2498 u"876,5",
2499 u"87,65",
2500 u"8,765",
2501 u"0");
2502
2503 // NOTE: Bulgarian is interesting because it has no grouping in the default currency format.
2504 // If this test breaks due to data changes, find another locale that has no default grouping.
2505 assertFormatDescendingBig(
2506 u"Bulgarian Currency Grouping",
2507 u"currency/USD group-auto",
2508 u"currency/USD",
2509 NumberFormatter::with().grouping(UNUM_GROUPING_AUTO).unit(USD),
2510 Locale("bg"),
2511 u"87650000,00 щ.д.",
2512 u"8765000,00 щ.д.",
2513 u"876500,00 щ.д.",
2514 u"87650,00 щ.д.",
2515 u"8765,00 щ.д.",
2516 u"876,50 щ.д.",
2517 u"87,65 щ.д.",
2518 u"8,76 щ.д.",
2519 u"0,00 щ.д.");
2520
2521 assertFormatDescendingBig(
2522 u"Bulgarian Currency Grouping, Always",
2523 u"currency/USD group-on-aligned",
2524 u"currency/USD ,!",
2525 NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED).unit(USD),
2526 Locale("bg"),
2527 u"87 650 000,00 щ.д.",
2528 u"8 765 000,00 щ.д.",
2529 u"876 500,00 щ.д.",
2530 u"87 650,00 щ.д.",
2531 u"8 765,00 щ.д.",
2532 u"876,50 щ.д.",
2533 u"87,65 щ.д.",
2534 u"8,76 щ.д.",
2535 u"0,00 щ.д.");
2536
2537 MacroProps macros;
2538 macros.grouper = Grouper(4, 1, 3, UNUM_GROUPING_COUNT);
2539 assertFormatDescendingBig(
2540 u"Custom Grouping via Internal API",
2541 nullptr,
2542 nullptr,
2543 NumberFormatter::with().macros(macros),
2544 Locale::getEnglish(),
2545 u"8,7,6,5,0000",
2546 u"8,7,6,5000",
2547 u"876500",
2548 u"87650",
2549 u"8765",
2550 u"876.5",
2551 u"87.65",
2552 u"8.765",
2553 u"0");
2554 }
2555
padding()2556 void NumberFormatterApiTest::padding() {
2557 assertFormatDescending(
2558 u"Padding",
2559 nullptr,
2560 nullptr,
2561 NumberFormatter::with().padding(Padder::none()),
2562 Locale::getEnglish(),
2563 u"87,650",
2564 u"8,765",
2565 u"876.5",
2566 u"87.65",
2567 u"8.765",
2568 u"0.8765",
2569 u"0.08765",
2570 u"0.008765",
2571 u"0");
2572
2573 assertFormatDescending(
2574 u"Padding",
2575 nullptr,
2576 nullptr,
2577 NumberFormatter::with().padding(
2578 Padder::codePoints(
2579 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
2580 Locale::getEnglish(),
2581 u"**87,650",
2582 u"***8,765",
2583 u"***876.5",
2584 u"***87.65",
2585 u"***8.765",
2586 u"**0.8765",
2587 u"*0.08765",
2588 u"0.008765",
2589 u"*******0");
2590
2591 assertFormatDescending(
2592 u"Padding with code points",
2593 nullptr,
2594 nullptr,
2595 NumberFormatter::with().padding(
2596 Padder::codePoints(
2597 0x101E4, 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
2598 Locale::getEnglish(),
2599 u"87,650",
2600 u"8,765",
2601 u"876.5",
2602 u"87.65",
2603 u"8.765",
2604 u"0.8765",
2605 u"0.08765",
2606 u"0.008765",
2607 u"0");
2608
2609 assertFormatDescending(
2610 u"Padding with wide digits",
2611 nullptr,
2612 nullptr,
2613 NumberFormatter::with().padding(
2614 Padder::codePoints(
2615 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX))
2616 .adoptSymbols(new NumberingSystem(MATHSANB)),
2617 Locale::getEnglish(),
2618 u"**,",
2619 u"***,",
2620 u"***.",
2621 u"***.",
2622 u"***.",
2623 u"**.",
2624 u"*.",
2625 u".",
2626 u"*******");
2627
2628 assertFormatDescending(
2629 u"Padding with currency spacing",
2630 nullptr,
2631 nullptr,
2632 NumberFormatter::with().padding(
2633 Padder::codePoints(
2634 '*', 10, PadPosition::UNUM_PAD_AFTER_PREFIX))
2635 .unit(GBP)
2636 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
2637 Locale::getEnglish(),
2638 u"GBP 87,650.00",
2639 u"GBP 8,765.00",
2640 u"GBP*876.50",
2641 u"GBP**87.65",
2642 u"GBP***8.76",
2643 u"GBP***0.88",
2644 u"GBP***0.09",
2645 u"GBP***0.01",
2646 u"GBP***0.00");
2647
2648 assertFormatSingle(
2649 u"Pad Before Prefix",
2650 nullptr,
2651 nullptr,
2652 NumberFormatter::with().padding(
2653 Padder::codePoints(
2654 '*', 8, PadPosition::UNUM_PAD_BEFORE_PREFIX)),
2655 Locale::getEnglish(),
2656 -88.88,
2657 u"**-88.88");
2658
2659 assertFormatSingle(
2660 u"Pad After Prefix",
2661 nullptr,
2662 nullptr,
2663 NumberFormatter::with().padding(
2664 Padder::codePoints(
2665 '*', 8, PadPosition::UNUM_PAD_AFTER_PREFIX)),
2666 Locale::getEnglish(),
2667 -88.88,
2668 u"-**88.88");
2669
2670 assertFormatSingle(
2671 u"Pad Before Suffix",
2672 nullptr,
2673 nullptr,
2674 NumberFormatter::with().padding(
2675 Padder::codePoints(
2676 '*', 8, PadPosition::UNUM_PAD_BEFORE_SUFFIX)).unit(NoUnit::percent()),
2677 Locale::getEnglish(),
2678 88.88,
2679 u"88.88**%");
2680
2681 assertFormatSingle(
2682 u"Pad After Suffix",
2683 nullptr,
2684 nullptr,
2685 NumberFormatter::with().padding(
2686 Padder::codePoints(
2687 '*', 8, PadPosition::UNUM_PAD_AFTER_SUFFIX)).unit(NoUnit::percent()),
2688 Locale::getEnglish(),
2689 88.88,
2690 u"88.88%**");
2691
2692 assertFormatSingle(
2693 u"Currency Spacing with Zero Digit Padding Broken",
2694 nullptr,
2695 nullptr,
2696 NumberFormatter::with().padding(
2697 Padder::codePoints(
2698 '0', 12, PadPosition::UNUM_PAD_AFTER_PREFIX))
2699 .unit(GBP)
2700 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE),
2701 Locale::getEnglish(),
2702 514.23,
2703 u"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
2704 }
2705
integerWidth()2706 void NumberFormatterApiTest::integerWidth() {
2707 assertFormatDescending(
2708 u"Integer Width Default",
2709 u"integer-width/+0",
2710 u"0",
2711 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1)),
2712 Locale::getEnglish(),
2713 u"87,650",
2714 u"8,765",
2715 u"876.5",
2716 u"87.65",
2717 u"8.765",
2718 u"0.8765",
2719 u"0.08765",
2720 u"0.008765",
2721 u"0");
2722
2723 assertFormatDescending(
2724 u"Integer Width Zero Fill 0",
2725 u"integer-width/*",
2726 u"integer-width/+",
2727 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(0)),
2728 Locale::getEnglish(),
2729 u"87,650",
2730 u"8,765",
2731 u"876.5",
2732 u"87.65",
2733 u"8.765",
2734 u".8765",
2735 u".08765",
2736 u".008765",
2737 u"0"); // see ICU-20844
2738
2739 assertFormatDescending(
2740 u"Integer Width Zero Fill 3",
2741 u"integer-width/+000",
2742 u"000",
2743 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(3)),
2744 Locale::getEnglish(),
2745 u"87,650",
2746 u"8,765",
2747 u"876.5",
2748 u"087.65",
2749 u"008.765",
2750 u"000.8765",
2751 u"000.08765",
2752 u"000.008765",
2753 u"000");
2754
2755 assertFormatDescending(
2756 u"Integer Width Max 3",
2757 u"integer-width/##0",
2758 u"integer-width/##0",
2759 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(1).truncateAt(3)),
2760 Locale::getEnglish(),
2761 u"650",
2762 u"765",
2763 u"876.5",
2764 u"87.65",
2765 u"8.765",
2766 u"0.8765",
2767 u"0.08765",
2768 u"0.008765",
2769 u"0");
2770
2771 assertFormatDescending(
2772 u"Integer Width Fixed 2",
2773 u"integer-width/00",
2774 u"integer-width/00",
2775 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
2776 Locale::getEnglish(),
2777 u"50",
2778 u"65",
2779 u"76.5",
2780 u"87.65",
2781 u"08.765",
2782 u"00.8765",
2783 u"00.08765",
2784 u"00.008765",
2785 u"00");
2786
2787 assertFormatDescending(
2788 u"Integer Width Compact",
2789 u"compact-short integer-width/000",
2790 u"compact-short integer-width/000",
2791 NumberFormatter::with()
2792 .notation(Notation::compactShort())
2793 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
2794 Locale::getEnglish(),
2795 u"088K",
2796 u"008.8K",
2797 u"876",
2798 u"088",
2799 u"008.8",
2800 u"000.88",
2801 u"000.088",
2802 u"000.0088",
2803 u"000");
2804
2805 assertFormatDescending(
2806 u"Integer Width Scientific",
2807 u"scientific integer-width/000",
2808 u"scientific integer-width/000",
2809 NumberFormatter::with()
2810 .notation(Notation::scientific())
2811 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
2812 Locale::getEnglish(),
2813 u"008.765E4",
2814 u"008.765E3",
2815 u"008.765E2",
2816 u"008.765E1",
2817 u"008.765E0",
2818 u"008.765E-1",
2819 u"008.765E-2",
2820 u"008.765E-3",
2821 u"000E0");
2822
2823 assertFormatDescending(
2824 u"Integer Width Engineering",
2825 u"engineering integer-width/000",
2826 u"engineering integer-width/000",
2827 NumberFormatter::with()
2828 .notation(Notation::engineering())
2829 .integerWidth(IntegerWidth::zeroFillTo(3).truncateAt(3)),
2830 Locale::getEnglish(),
2831 u"087.65E3",
2832 u"008.765E3",
2833 u"876.5E0",
2834 u"087.65E0",
2835 u"008.765E0",
2836 u"876.5E-3",
2837 u"087.65E-3",
2838 u"008.765E-3",
2839 u"000E0");
2840
2841 assertFormatSingle(
2842 u"Integer Width Remove All A",
2843 u"integer-width/00",
2844 u"integer-width/00",
2845 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
2846 "en",
2847 2500,
2848 u"00");
2849
2850 assertFormatSingle(
2851 u"Integer Width Remove All B",
2852 u"integer-width/00",
2853 u"integer-width/00",
2854 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
2855 "en",
2856 25000,
2857 u"00");
2858
2859 assertFormatSingle(
2860 u"Integer Width Remove All B, Bytes Mode",
2861 u"integer-width/00",
2862 u"integer-width/00",
2863 NumberFormatter::with().integerWidth(IntegerWidth::zeroFillTo(2).truncateAt(2)),
2864 "en",
2865 // Note: this double produces all 17 significant digits
2866 10000000000000002000.0,
2867 u"00");
2868 }
2869
symbols()2870 void NumberFormatterApiTest::symbols() {
2871 assertFormatDescending(
2872 u"French Symbols with Japanese Data 1",
2873 nullptr,
2874 nullptr,
2875 NumberFormatter::with().symbols(FRENCH_SYMBOLS),
2876 Locale::getJapan(),
2877 u"87\u202F650",
2878 u"8\u202F765",
2879 u"876,5",
2880 u"87,65",
2881 u"8,765",
2882 u"0,8765",
2883 u"0,08765",
2884 u"0,008765",
2885 u"0");
2886
2887 assertFormatSingle(
2888 u"French Symbols with Japanese Data 2",
2889 nullptr,
2890 nullptr,
2891 NumberFormatter::with().notation(Notation::compactShort()).symbols(FRENCH_SYMBOLS),
2892 Locale::getJapan(),
2893 12345,
2894 u"1,2\u4E07");
2895
2896 assertFormatDescending(
2897 u"Latin Numbering System with Arabic Data",
2898 u"currency/USD latin",
2899 u"currency/USD latin",
2900 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
2901 Locale("ar"),
2902 u"US$ 87,650.00",
2903 u"US$ 8,765.00",
2904 u"US$ 876.50",
2905 u"US$ 87.65",
2906 u"US$ 8.76",
2907 u"US$ 0.88",
2908 u"US$ 0.09",
2909 u"US$ 0.01",
2910 u"US$ 0.00");
2911
2912 assertFormatDescending(
2913 u"Math Numbering System with French Data",
2914 u"numbering-system/mathsanb",
2915 u"numbering-system/mathsanb",
2916 NumberFormatter::with().adoptSymbols(new NumberingSystem(MATHSANB)),
2917 Locale::getFrench(),
2918 u"\u202F",
2919 u"\u202F",
2920 u",",
2921 u",",
2922 u",",
2923 u",",
2924 u",",
2925 u",",
2926 u"");
2927
2928 assertFormatSingle(
2929 u"Swiss Symbols (used in documentation)",
2930 nullptr,
2931 nullptr,
2932 NumberFormatter::with().symbols(SWISS_SYMBOLS),
2933 Locale::getEnglish(),
2934 12345.67,
2935 u"12’345.67");
2936
2937 assertFormatSingle(
2938 u"Myanmar Symbols (used in documentation)",
2939 nullptr,
2940 nullptr,
2941 NumberFormatter::with().symbols(MYANMAR_SYMBOLS),
2942 Locale::getEnglish(),
2943 12345.67,
2944 u"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
2945
2946 // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
2947
2948 assertFormatSingle(
2949 u"Currency symbol should precede number in ar with NS latn",
2950 u"currency/USD latin",
2951 u"currency/USD latin",
2952 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
2953 Locale("ar"),
2954 12345.67,
2955 u"US$ 12,345.67");
2956
2957 assertFormatSingle(
2958 u"Currency symbol should precede number in ar@numbers=latn",
2959 u"currency/USD",
2960 u"currency/USD",
2961 NumberFormatter::with().unit(USD),
2962 Locale("ar@numbers=latn"),
2963 12345.67,
2964 u"US$ 12,345.67");
2965
2966 assertFormatSingle(
2967 u"Currency symbol should follow number in ar-EG with NS arab",
2968 u"currency/USD",
2969 u"currency/USD",
2970 NumberFormatter::with().unit(USD),
2971 Locale("ar-EG"),
2972 12345.67,
2973 u"١٢٬٣٤٥٫٦٧ US$");
2974
2975 assertFormatSingle(
2976 u"Currency symbol should follow number in ar@numbers=arab",
2977 u"currency/USD",
2978 u"currency/USD",
2979 NumberFormatter::with().unit(USD),
2980 Locale("ar@numbers=arab"),
2981 12345.67,
2982 u"١٢٬٣٤٥٫٦٧ US$");
2983
2984 assertFormatSingle(
2985 u"NumberingSystem in API should win over @numbers keyword",
2986 u"currency/USD latin",
2987 u"currency/USD latin",
2988 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).unit(USD),
2989 Locale("ar@numbers=arab"),
2990 12345.67,
2991 u"US$ 12,345.67");
2992
2993 UErrorCode status = U_ZERO_ERROR;
2994 assertEquals(
2995 "NumberingSystem in API should win over @numbers keyword in reverse order",
2996 u"US$ 12,345.67",
2997 NumberFormatter::withLocale(Locale("ar@numbers=arab")).adoptSymbols(new NumberingSystem(LATN))
2998 .unit(USD)
2999 .formatDouble(12345.67, status)
3000 .toString(status));
3001
3002 DecimalFormatSymbols symbols = SWISS_SYMBOLS;
3003 UnlocalizedNumberFormatter f = NumberFormatter::with().symbols(symbols);
3004 symbols.setSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol, u"!", status);
3005 assertFormatSingle(
3006 u"Symbols object should be copied",
3007 nullptr,
3008 nullptr,
3009 f,
3010 Locale::getEnglish(),
3011 12345.67,
3012 u"12’345.67");
3013
3014 assertFormatSingle(
3015 u"The last symbols setter wins",
3016 u"latin",
3017 u"latin",
3018 NumberFormatter::with().symbols(symbols).adoptSymbols(new NumberingSystem(LATN)),
3019 Locale::getEnglish(),
3020 12345.67,
3021 u"12,345.67");
3022
3023 assertFormatSingle(
3024 u"The last symbols setter wins",
3025 nullptr,
3026 nullptr,
3027 NumberFormatter::with().adoptSymbols(new NumberingSystem(LATN)).symbols(symbols),
3028 Locale::getEnglish(),
3029 12345.67,
3030 u"12!345.67");
3031 }
3032
3033 // TODO: Enable if/when currency symbol override is added.
3034 //void NumberFormatterTest::symbolsOverride() {
3035 // DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(Locale::getEnglish());
3036 // dfs.setCurrencySymbol("@");
3037 // dfs.setInternationalCurrencySymbol("foo");
3038 // assertFormatSingle(
3039 // u"Custom Short Currency Symbol",
3040 // NumberFormatter::with().unit(Currency.getInstance("XXX")).symbols(dfs),
3041 // Locale::getEnglish(),
3042 // 12.3,
3043 // u"@ 12.30");
3044 //}
3045
sign()3046 void NumberFormatterApiTest::sign() {
3047 assertFormatSingle(
3048 u"Sign Auto Positive",
3049 u"sign-auto",
3050 u"",
3051 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
3052 Locale::getEnglish(),
3053 444444,
3054 u"444,444");
3055
3056 assertFormatSingle(
3057 u"Sign Auto Negative",
3058 u"sign-auto",
3059 u"",
3060 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
3061 Locale::getEnglish(),
3062 -444444,
3063 u"-444,444");
3064
3065 assertFormatSingle(
3066 u"Sign Auto Zero",
3067 u"sign-auto",
3068 u"",
3069 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_AUTO),
3070 Locale::getEnglish(),
3071 0,
3072 u"0");
3073
3074 assertFormatSingle(
3075 u"Sign Always Positive",
3076 u"sign-always",
3077 u"+!",
3078 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
3079 Locale::getEnglish(),
3080 444444,
3081 u"+444,444");
3082
3083 assertFormatSingle(
3084 u"Sign Always Negative",
3085 u"sign-always",
3086 u"+!",
3087 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
3088 Locale::getEnglish(),
3089 -444444,
3090 u"-444,444");
3091
3092 assertFormatSingle(
3093 u"Sign Always Zero",
3094 u"sign-always",
3095 u"+!",
3096 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS),
3097 Locale::getEnglish(),
3098 0,
3099 u"+0");
3100
3101 assertFormatSingle(
3102 u"Sign Never Positive",
3103 u"sign-never",
3104 u"+_",
3105 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
3106 Locale::getEnglish(),
3107 444444,
3108 u"444,444");
3109
3110 assertFormatSingle(
3111 u"Sign Never Negative",
3112 u"sign-never",
3113 u"+_",
3114 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
3115 Locale::getEnglish(),
3116 -444444,
3117 u"444,444");
3118
3119 assertFormatSingle(
3120 u"Sign Never Zero",
3121 u"sign-never",
3122 u"+_",
3123 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_NEVER),
3124 Locale::getEnglish(),
3125 0,
3126 u"0");
3127
3128 assertFormatSingle(
3129 u"Sign Accounting Positive",
3130 u"currency/USD sign-accounting",
3131 u"currency/USD ()",
3132 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
3133 Locale::getEnglish(),
3134 444444,
3135 u"$444,444.00");
3136
3137 assertFormatSingle(
3138 u"Sign Accounting Negative",
3139 u"currency/USD sign-accounting",
3140 u"currency/USD ()",
3141 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
3142 Locale::getEnglish(),
3143 -444444,
3144 u"($444,444.00)");
3145
3146 assertFormatSingle(
3147 u"Sign Accounting Zero",
3148 u"currency/USD sign-accounting",
3149 u"currency/USD ()",
3150 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING).unit(USD),
3151 Locale::getEnglish(),
3152 0,
3153 u"$0.00");
3154
3155 assertFormatSingle(
3156 u"Sign Accounting-Always Positive",
3157 u"currency/USD sign-accounting-always",
3158 u"currency/USD ()!",
3159 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
3160 Locale::getEnglish(),
3161 444444,
3162 u"+$444,444.00");
3163
3164 assertFormatSingle(
3165 u"Sign Accounting-Always Negative",
3166 u"currency/USD sign-accounting-always",
3167 u"currency/USD ()!",
3168 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
3169 Locale::getEnglish(),
3170 -444444,
3171 u"($444,444.00)");
3172
3173 assertFormatSingle(
3174 u"Sign Accounting-Always Zero",
3175 u"currency/USD sign-accounting-always",
3176 u"currency/USD ()!",
3177 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS).unit(USD),
3178 Locale::getEnglish(),
3179 0,
3180 u"+$0.00");
3181
3182 assertFormatSingle(
3183 u"Sign Except-Zero Positive",
3184 u"sign-except-zero",
3185 u"+?",
3186 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
3187 Locale::getEnglish(),
3188 444444,
3189 u"+444,444");
3190
3191 assertFormatSingle(
3192 u"Sign Except-Zero Negative",
3193 u"sign-except-zero",
3194 u"+?",
3195 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
3196 Locale::getEnglish(),
3197 -444444,
3198 u"-444,444");
3199
3200 assertFormatSingle(
3201 u"Sign Except-Zero Zero",
3202 u"sign-except-zero",
3203 u"+?",
3204 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO),
3205 Locale::getEnglish(),
3206 0,
3207 u"0");
3208
3209 assertFormatSingle(
3210 u"Sign Accounting-Except-Zero Positive",
3211 u"currency/USD sign-accounting-except-zero",
3212 u"currency/USD ()?",
3213 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
3214 Locale::getEnglish(),
3215 444444,
3216 u"+$444,444.00");
3217
3218 assertFormatSingle(
3219 u"Sign Accounting-Except-Zero Negative",
3220 u"currency/USD sign-accounting-except-zero",
3221 u"currency/USD ()?",
3222 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
3223 Locale::getEnglish(),
3224 -444444,
3225 u"($444,444.00)");
3226
3227 assertFormatSingle(
3228 u"Sign Accounting-Except-Zero Zero",
3229 u"currency/USD sign-accounting-except-zero",
3230 u"currency/USD ()?",
3231 NumberFormatter::with().sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO).unit(USD),
3232 Locale::getEnglish(),
3233 0,
3234 u"$0.00");
3235
3236 assertFormatSingle(
3237 u"Sign Accounting Negative Hidden",
3238 u"currency/USD unit-width-hidden sign-accounting",
3239 u"currency/USD unit-width-hidden ()",
3240 NumberFormatter::with()
3241 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3242 .unit(USD)
3243 .unitWidth(UNUM_UNIT_WIDTH_HIDDEN),
3244 Locale::getEnglish(),
3245 -444444,
3246 u"(444,444.00)");
3247
3248 assertFormatSingle(
3249 u"Sign Accounting Negative Narrow",
3250 u"currency/USD unit-width-narrow sign-accounting",
3251 u"currency/USD unit-width-narrow ()",
3252 NumberFormatter::with()
3253 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3254 .unit(USD)
3255 .unitWidth(UNUM_UNIT_WIDTH_NARROW),
3256 Locale::getCanada(),
3257 -444444,
3258 u"($444,444.00)");
3259
3260 assertFormatSingle(
3261 u"Sign Accounting Negative Short",
3262 u"currency/USD sign-accounting",
3263 u"currency/USD ()",
3264 NumberFormatter::with()
3265 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3266 .unit(USD)
3267 .unitWidth(UNUM_UNIT_WIDTH_SHORT),
3268 Locale::getCanada(),
3269 -444444,
3270 u"(US$444,444.00)");
3271
3272 assertFormatSingle(
3273 u"Sign Accounting Negative Iso Code",
3274 u"currency/USD unit-width-iso-code sign-accounting",
3275 u"currency/USD unit-width-iso-code ()",
3276 NumberFormatter::with()
3277 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3278 .unit(USD)
3279 .unitWidth(UNUM_UNIT_WIDTH_ISO_CODE),
3280 Locale::getCanada(),
3281 -444444,
3282 u"(USD 444,444.00)");
3283
3284 // Note: CLDR does not provide an accounting pattern for long name currency.
3285 // We fall back to normal currency format. This may change in the future.
3286 assertFormatSingle(
3287 u"Sign Accounting Negative Full Name",
3288 u"currency/USD unit-width-full-name sign-accounting",
3289 u"currency/USD unit-width-full-name ()",
3290 NumberFormatter::with()
3291 .sign(UNumberSignDisplay::UNUM_SIGN_ACCOUNTING)
3292 .unit(USD)
3293 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3294 Locale::getCanada(),
3295 -444444,
3296 u"-444,444.00 US dollars");
3297 }
3298
signNearZero()3299 void NumberFormatterApiTest::signNearZero() {
3300 // https://unicode-org.atlassian.net/browse/ICU-20709
3301 IcuTestErrorCode status(*this, "signNearZero");
3302 const struct TestCase {
3303 UNumberSignDisplay sign;
3304 double input;
3305 const char16_t* expected;
3306 } cases[] = {
3307 { UNUM_SIGN_AUTO, 1.1, u"1" },
3308 { UNUM_SIGN_AUTO, 0.9, u"1" },
3309 { UNUM_SIGN_AUTO, 0.1, u"0" },
3310 { UNUM_SIGN_AUTO, -0.1, u"-0" }, // interesting case
3311 { UNUM_SIGN_AUTO, -0.9, u"-1" },
3312 { UNUM_SIGN_AUTO, -1.1, u"-1" },
3313 { UNUM_SIGN_ALWAYS, 1.1, u"+1" },
3314 { UNUM_SIGN_ALWAYS, 0.9, u"+1" },
3315 { UNUM_SIGN_ALWAYS, 0.1, u"+0" },
3316 { UNUM_SIGN_ALWAYS, -0.1, u"-0" },
3317 { UNUM_SIGN_ALWAYS, -0.9, u"-1" },
3318 { UNUM_SIGN_ALWAYS, -1.1, u"-1" },
3319 { UNUM_SIGN_EXCEPT_ZERO, 1.1, u"+1" },
3320 { UNUM_SIGN_EXCEPT_ZERO, 0.9, u"+1" },
3321 { UNUM_SIGN_EXCEPT_ZERO, 0.1, u"0" }, // interesting case
3322 { UNUM_SIGN_EXCEPT_ZERO, -0.1, u"0" }, // interesting case
3323 { UNUM_SIGN_EXCEPT_ZERO, -0.9, u"-1" },
3324 { UNUM_SIGN_EXCEPT_ZERO, -1.1, u"-1" },
3325 };
3326 for (auto& cas : cases) {
3327 auto sign = cas.sign;
3328 auto input = cas.input;
3329 auto expected = cas.expected;
3330 auto actual = NumberFormatter::with()
3331 .sign(sign)
3332 .precision(Precision::integer())
3333 .locale(Locale::getUS())
3334 .formatDouble(input, status)
3335 .toString(status);
3336 assertEquals(
3337 DoubleToUnicodeString(input) + " @ SignDisplay " + Int64ToUnicodeString(sign),
3338 expected, actual);
3339 }
3340 }
3341
signCoverage()3342 void NumberFormatterApiTest::signCoverage() {
3343 // https://unicode-org.atlassian.net/browse/ICU-20708
3344 IcuTestErrorCode status(*this, "signCoverage");
3345 const struct TestCase {
3346 UNumberSignDisplay sign;
3347 const char16_t* expectedStrings[8];
3348 } cases[] = {
3349 { UNUM_SIGN_AUTO, { u"-∞", u"-1", u"-0", u"0", u"1", u"∞", u"NaN", u"-NaN" } },
3350 { UNUM_SIGN_ALWAYS, { u"-∞", u"-1", u"-0", u"+0", u"+1", u"+∞", u"+NaN", u"-NaN" } },
3351 { UNUM_SIGN_NEVER, { u"∞", u"1", u"0", u"0", u"1", u"∞", u"NaN", u"NaN" } },
3352 { UNUM_SIGN_EXCEPT_ZERO, { u"-∞", u"-1", u"0", u"0", u"+1", u"+∞", u"NaN", u"NaN" } },
3353 };
3354 double negNaN = std::copysign(uprv_getNaN(), -0.0);
3355 const double inputs[] = {
3356 -uprv_getInfinity(), -1, -0.0, 0, 1, uprv_getInfinity(), uprv_getNaN(), negNaN
3357 };
3358 for (auto& cas : cases) {
3359 auto sign = cas.sign;
3360 for (int32_t i = 0; i < UPRV_LENGTHOF(inputs); i++) {
3361 auto input = inputs[i];
3362 auto expected = cas.expectedStrings[i];
3363 auto actual = NumberFormatter::with()
3364 .sign(sign)
3365 .locale(Locale::getUS())
3366 .formatDouble(input, status)
3367 .toString(status);
3368 assertEquals(
3369 DoubleToUnicodeString(input) + " " + Int64ToUnicodeString(sign),
3370 expected, actual);
3371 }
3372 }
3373 }
3374
decimal()3375 void NumberFormatterApiTest::decimal() {
3376 assertFormatDescending(
3377 u"Decimal Default",
3378 u"decimal-auto",
3379 u"",
3380 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_AUTO),
3381 Locale::getEnglish(),
3382 u"87,650",
3383 u"8,765",
3384 u"876.5",
3385 u"87.65",
3386 u"8.765",
3387 u"0.8765",
3388 u"0.08765",
3389 u"0.008765",
3390 u"0");
3391
3392 assertFormatDescending(
3393 u"Decimal Always Shown",
3394 u"decimal-always",
3395 u"decimal-always",
3396 NumberFormatter::with().decimal(UNumberDecimalSeparatorDisplay::UNUM_DECIMAL_SEPARATOR_ALWAYS),
3397 Locale::getEnglish(),
3398 u"87,650.",
3399 u"8,765.",
3400 u"876.5",
3401 u"87.65",
3402 u"8.765",
3403 u"0.8765",
3404 u"0.08765",
3405 u"0.008765",
3406 u"0.");
3407 }
3408
scale()3409 void NumberFormatterApiTest::scale() {
3410 assertFormatDescending(
3411 u"Multiplier None",
3412 u"scale/1",
3413 u"",
3414 NumberFormatter::with().scale(Scale::none()),
3415 Locale::getEnglish(),
3416 u"87,650",
3417 u"8,765",
3418 u"876.5",
3419 u"87.65",
3420 u"8.765",
3421 u"0.8765",
3422 u"0.08765",
3423 u"0.008765",
3424 u"0");
3425
3426 assertFormatDescending(
3427 u"Multiplier Power of Ten",
3428 u"scale/1000000",
3429 u"scale/1E6",
3430 NumberFormatter::with().scale(Scale::powerOfTen(6)),
3431 Locale::getEnglish(),
3432 u"87,650,000,000",
3433 u"8,765,000,000",
3434 u"876,500,000",
3435 u"87,650,000",
3436 u"8,765,000",
3437 u"876,500",
3438 u"87,650",
3439 u"8,765",
3440 u"0");
3441
3442 assertFormatDescending(
3443 u"Multiplier Arbitrary Double",
3444 u"scale/5.2",
3445 u"scale/5.2",
3446 NumberFormatter::with().scale(Scale::byDouble(5.2)),
3447 Locale::getEnglish(),
3448 u"455,780",
3449 u"45,578",
3450 u"4,557.8",
3451 u"455.78",
3452 u"45.578",
3453 u"4.5578",
3454 u"0.45578",
3455 u"0.045578",
3456 u"0");
3457
3458 assertFormatDescending(
3459 u"Multiplier Arbitrary BigDecimal",
3460 u"scale/5.2",
3461 u"scale/5.2",
3462 NumberFormatter::with().scale(Scale::byDecimal({"5.2", -1})),
3463 Locale::getEnglish(),
3464 u"455,780",
3465 u"45,578",
3466 u"4,557.8",
3467 u"455.78",
3468 u"45.578",
3469 u"4.5578",
3470 u"0.45578",
3471 u"0.045578",
3472 u"0");
3473
3474 assertFormatDescending(
3475 u"Multiplier Arbitrary Double And Power Of Ten",
3476 u"scale/5200",
3477 u"scale/5200",
3478 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(5.2, 3)),
3479 Locale::getEnglish(),
3480 u"455,780,000",
3481 u"45,578,000",
3482 u"4,557,800",
3483 u"455,780",
3484 u"45,578",
3485 u"4,557.8",
3486 u"455.78",
3487 u"45.578",
3488 u"0");
3489
3490 assertFormatDescending(
3491 u"Multiplier Zero",
3492 u"scale/0",
3493 u"scale/0",
3494 NumberFormatter::with().scale(Scale::byDouble(0)),
3495 Locale::getEnglish(),
3496 u"0",
3497 u"0",
3498 u"0",
3499 u"0",
3500 u"0",
3501 u"0",
3502 u"0",
3503 u"0",
3504 u"0");
3505
3506 assertFormatSingle(
3507 u"Multiplier Skeleton Scientific Notation and Percent",
3508 u"percent scale/1E2",
3509 u"%x100",
3510 NumberFormatter::with().unit(NoUnit::percent()).scale(Scale::powerOfTen(2)),
3511 Locale::getEnglish(),
3512 0.5,
3513 u"50%");
3514
3515 assertFormatSingle(
3516 u"Negative Multiplier",
3517 u"scale/-5.2",
3518 u"scale/-5.2",
3519 NumberFormatter::with().scale(Scale::byDouble(-5.2)),
3520 Locale::getEnglish(),
3521 2,
3522 u"-10.4");
3523
3524 assertFormatSingle(
3525 u"Negative One Multiplier",
3526 u"scale/-1",
3527 u"scale/-1",
3528 NumberFormatter::with().scale(Scale::byDouble(-1)),
3529 Locale::getEnglish(),
3530 444444,
3531 u"-444,444");
3532
3533 assertFormatSingle(
3534 u"Two-Type Multiplier with Overlap",
3535 u"scale/10000",
3536 u"scale/1E4",
3537 NumberFormatter::with().scale(Scale::byDoubleAndPowerOfTen(100, 2)),
3538 Locale::getEnglish(),
3539 2,
3540 u"20,000");
3541 }
3542
locale()3543 void NumberFormatterApiTest::locale() {
3544 // Coverage for the locale setters.
3545 UErrorCode status = U_ZERO_ERROR;
3546 UnicodeString actual = NumberFormatter::withLocale(Locale::getFrench()).formatInt(1234, status)
3547 .toString(status);
3548 assertEquals("Locale withLocale()", u"1\u202f234", actual);
3549 }
3550
skeletonUserGuideExamples()3551 void NumberFormatterApiTest::skeletonUserGuideExamples() {
3552 IcuTestErrorCode status(*this, "skeletonUserGuideExamples");
3553
3554 // Test the skeleton examples in userguide/format_parse/numbers/skeletons.md
3555 struct TestCase {
3556 const char16_t* skeleton;
3557 const char16_t* conciseSkeleton;
3558 double input;
3559 const char16_t* expected;
3560 } cases[] = {
3561 {u"percent", u"%", 25, u"25%"},
3562 {u".00", u".00", 25, u"25.00"},
3563 {u"percent .00", u"% .00", 25, u"25.00%"},
3564 {u"scale/100", u"scale/100", 0.3, u"30"},
3565 {u"percent scale/100", u"%x100", 0.3, u"30%"},
3566 {u"measure-unit/length-meter", u"unit/meter", 5, u"5 m"},
3567 {u"measure-unit/length-meter unit-width-full-name", u"unit/meter unit-width-full-name", 5, u"5 meters"},
3568 {u"currency/CAD", u"currency/CAD", 10, u"CA$10.00"},
3569 {u"currency/CAD unit-width-narrow", u"currency/CAD unit-width-narrow", 10, u"$10.00"},
3570 {u"compact-short", u"K", 5000, u"5K"},
3571 {u"compact-long", u"KK", 5000, u"5 thousand"},
3572 {u"compact-short currency/CAD", u"K currency/CAD", 5000, u"CA$5K"},
3573 {u"", u"", 5000, u"5,000"},
3574 {u"group-min2", u",?", 5000, u"5000"},
3575 {u"group-min2", u",?", 15000, u"15,000"},
3576 {u"sign-always", u"+!", 60, u"+60"},
3577 {u"sign-always", u"+!", 0, u"+0"},
3578 {u"sign-except-zero", u"+?", 60, u"+60"},
3579 {u"sign-except-zero", u"+?", 0, u"0"},
3580 {u"sign-accounting currency/CAD", u"() currency/CAD", -40, u"(CA$40.00)"}
3581 };
3582
3583 for (const auto& cas : cases) {
3584 status.setScope(cas.skeleton);
3585 FormattedNumber actual = NumberFormatter::forSkeleton(cas.skeleton, status)
3586 .locale("en-US")
3587 .formatDouble(cas.input, status);
3588 assertEquals(cas.skeleton, cas.expected, actual.toTempString(status));
3589 status.errIfFailureAndReset();
3590 FormattedNumber actualConcise = NumberFormatter::forSkeleton(cas.conciseSkeleton, status)
3591 .locale("en-US")
3592 .formatDouble(cas.input, status);
3593 assertEquals(cas.conciseSkeleton, cas.expected, actualConcise.toTempString(status));
3594 status.errIfFailureAndReset();
3595 }
3596 }
3597
formatTypes()3598 void NumberFormatterApiTest::formatTypes() {
3599 UErrorCode status = U_ZERO_ERROR;
3600 LocalizedNumberFormatter formatter = NumberFormatter::withLocale(Locale::getEnglish());
3601
3602 // Double
3603 assertEquals("Format double", "514.23", formatter.formatDouble(514.23, status).toString(status));
3604
3605 // Int64
3606 assertEquals("Format int64", "51,423", formatter.formatDouble(51423L, status).toString(status));
3607
3608 // decNumber
3609 UnicodeString actual = formatter.formatDecimal("98765432123456789E1", status).toString(status);
3610 assertEquals("Format decNumber", u"987,654,321,234,567,890", actual);
3611
3612 // Also test proper DecimalQuantity bytes storage when all digits are in the fraction.
3613 // The number needs to have exactly 40 digits, which is the size of the default buffer.
3614 // (issue discovered by the address sanitizer in C++)
3615 static const char* str = "0.009876543210987654321098765432109876543211";
3616 actual = formatter.precision(Precision::unlimited()).formatDecimal(str, status).toString(status);
3617 assertEquals("Format decNumber to 40 digits", str, actual);
3618 }
3619
fieldPositionLogic()3620 void NumberFormatterApiTest::fieldPositionLogic() {
3621 IcuTestErrorCode status(*this, "fieldPositionLogic");
3622
3623 const char16_t* message = u"Field position logic test";
3624
3625 FormattedNumber fmtd = assertFormatSingle(
3626 message,
3627 u"",
3628 u"",
3629 NumberFormatter::with(),
3630 Locale::getEnglish(),
3631 -9876543210.12,
3632 u"-9,876,543,210.12");
3633
3634 static const UFieldPosition expectedFieldPositions[] = {
3635 // field, begin index, end index
3636 {UNUM_SIGN_FIELD, 0, 1},
3637 {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
3638 {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
3639 {UNUM_GROUPING_SEPARATOR_FIELD, 10, 11},
3640 {UNUM_INTEGER_FIELD, 1, 14},
3641 {UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
3642 {UNUM_FRACTION_FIELD, 15, 17}};
3643
3644 assertNumberFieldPositions(
3645 message,
3646 fmtd,
3647 expectedFieldPositions,
3648 UPRV_LENGTHOF(expectedFieldPositions));
3649
3650 // Test the iteration functionality of nextFieldPosition
3651 ConstrainedFieldPosition actual;
3652 actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD);
3653 int32_t i = 1;
3654 while (fmtd.nextPosition(actual, status)) {
3655 UFieldPosition expected = expectedFieldPositions[i++];
3656 assertEquals(
3657 UnicodeString(u"Next for grouping, field, case #") + Int64ToUnicodeString(i),
3658 expected.field,
3659 actual.getField());
3660 assertEquals(
3661 UnicodeString(u"Next for grouping, begin index, case #") + Int64ToUnicodeString(i),
3662 expected.beginIndex,
3663 actual.getStart());
3664 assertEquals(
3665 UnicodeString(u"Next for grouping, end index, case #") + Int64ToUnicodeString(i),
3666 expected.endIndex,
3667 actual.getLimit());
3668 }
3669 assertEquals(u"Should have seen all grouping separators", 4, i);
3670
3671 // Make sure strings without fraction do not contain fraction field
3672 actual.reset();
3673 actual.constrainField(UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD);
3674 fmtd = NumberFormatter::withLocale("en").formatInt(5, status);
3675 assertFalse(u"No fraction part in an integer", fmtd.nextPosition(actual, status));
3676 }
3677
fieldPositionCoverage()3678 void NumberFormatterApiTest::fieldPositionCoverage() {
3679 IcuTestErrorCode status(*this, "fieldPositionCoverage");
3680
3681 {
3682 const char16_t* message = u"Measure unit field position basic";
3683 FormattedNumber result = assertFormatSingle(
3684 message,
3685 u"measure-unit/temperature-fahrenheit",
3686 u"unit/fahrenheit",
3687 NumberFormatter::with().unit(FAHRENHEIT),
3688 Locale::getEnglish(),
3689 68,
3690 u"68°F");
3691 static const UFieldPosition expectedFieldPositions[] = {
3692 // field, begin index, end index
3693 {UNUM_INTEGER_FIELD, 0, 2},
3694 {UNUM_MEASURE_UNIT_FIELD, 2, 4}};
3695 assertNumberFieldPositions(
3696 message,
3697 result,
3698 expectedFieldPositions,
3699 UPRV_LENGTHOF(expectedFieldPositions));
3700 }
3701
3702 {
3703 const char16_t* message = u"Measure unit field position with compound unit";
3704 FormattedNumber result = assertFormatSingle(
3705 message,
3706 u"measure-unit/temperature-fahrenheit per-measure-unit/duration-day",
3707 u"unit/fahrenheit-per-day",
3708 NumberFormatter::with().unit(FAHRENHEIT).perUnit(DAY),
3709 Locale::getEnglish(),
3710 68,
3711 u"68°F/d");
3712 static const UFieldPosition expectedFieldPositions[] = {
3713 // field, begin index, end index
3714 {UNUM_INTEGER_FIELD, 0, 2},
3715 // coverage for old enum:
3716 {DecimalFormat::kMeasureUnitField, 2, 6}};
3717 assertNumberFieldPositions(
3718 message,
3719 result,
3720 expectedFieldPositions,
3721 UPRV_LENGTHOF(expectedFieldPositions));
3722 }
3723
3724 {
3725 const char16_t* message = u"Measure unit field position with spaces";
3726 FormattedNumber result = assertFormatSingle(
3727 message,
3728 u"measure-unit/length-meter unit-width-full-name",
3729 u"unit/meter unit-width-full-name",
3730 NumberFormatter::with().unit(METER).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3731 Locale::getEnglish(),
3732 68,
3733 u"68 meters");
3734 static const UFieldPosition expectedFieldPositions[] = {
3735 // field, begin index, end index
3736 {UNUM_INTEGER_FIELD, 0, 2},
3737 // note: field starts after the space
3738 {UNUM_MEASURE_UNIT_FIELD, 3, 9}};
3739 assertNumberFieldPositions(
3740 message,
3741 result,
3742 expectedFieldPositions,
3743 UPRV_LENGTHOF(expectedFieldPositions));
3744 }
3745
3746 {
3747 const char16_t* message = u"Measure unit field position with prefix and suffix, composed m/s";
3748 FormattedNumber result = assertFormatSingle(
3749 message,
3750 u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
3751 u"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
3752 NumberFormatter::with().unit(METER).perUnit(SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3753 "ky", // locale with the interesting data
3754 68,
3755 u"секундасына 68 метр");
3756 static const UFieldPosition expectedFieldPositions[] = {
3757 // field, begin index, end index
3758 {UNUM_MEASURE_UNIT_FIELD, 0, 11},
3759 {UNUM_INTEGER_FIELD, 12, 14},
3760 {UNUM_MEASURE_UNIT_FIELD, 15, 19}};
3761 assertNumberFieldPositions(
3762 message,
3763 result,
3764 expectedFieldPositions,
3765 UPRV_LENGTHOF(expectedFieldPositions));
3766 }
3767
3768 {
3769 const char16_t* message = u"Measure unit field position with prefix and suffix, built-in m/s";
3770 FormattedNumber result = assertFormatSingle(
3771 message,
3772 u"measure-unit/speed-meter-per-second unit-width-full-name",
3773 u"unit/meter-per-second unit-width-full-name",
3774 NumberFormatter::with().unit(METER_PER_SECOND).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3775 "ky", // locale with the interesting data
3776 68,
3777 u"секундасына 68 метр");
3778 static const UFieldPosition expectedFieldPositions[] = {
3779 // field, begin index, end index
3780 {UNUM_MEASURE_UNIT_FIELD, 0, 11},
3781 {UNUM_INTEGER_FIELD, 12, 14},
3782 {UNUM_MEASURE_UNIT_FIELD, 15, 19}};
3783 assertNumberFieldPositions(
3784 message,
3785 result,
3786 expectedFieldPositions,
3787 UPRV_LENGTHOF(expectedFieldPositions));
3788 }
3789
3790 {
3791 const char16_t* message = u"Measure unit field position with inner spaces";
3792 FormattedNumber result = assertFormatSingle(
3793 message,
3794 u"measure-unit/temperature-fahrenheit unit-width-full-name",
3795 u"unit/fahrenheit unit-width-full-name",
3796 NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3797 "vi", // locale with the interesting data
3798 68,
3799 u"68 độ F");
3800 static const UFieldPosition expectedFieldPositions[] = {
3801 // field, begin index, end index
3802 {UNUM_INTEGER_FIELD, 0, 2},
3803 // Should trim leading/trailing spaces, but not inner spaces:
3804 {UNUM_MEASURE_UNIT_FIELD, 3, 7}};
3805 assertNumberFieldPositions(
3806 message,
3807 result,
3808 expectedFieldPositions,
3809 UPRV_LENGTHOF(expectedFieldPositions));
3810 }
3811
3812 {
3813 // Data: other{"{0} K"} == "\u200E{0} K"
3814 // If that data changes, try to find another example of a non-empty unit prefix/suffix
3815 // that is also all ignorables (whitespace and bidi control marks).
3816 const char16_t* message = u"Measure unit field position with fully ignorable prefix";
3817 FormattedNumber result = assertFormatSingle(
3818 message,
3819 u"measure-unit/temperature-kelvin",
3820 u"unit/kelvin",
3821 NumberFormatter::with().unit(KELVIN),
3822 "fa", // locale with the interesting data
3823 68,
3824 u"۶۸ K");
3825 static const UFieldPosition expectedFieldPositions[] = {
3826 // field, begin index, end index
3827 {UNUM_INTEGER_FIELD, 1, 3},
3828 {UNUM_MEASURE_UNIT_FIELD, 4, 5}};
3829 assertNumberFieldPositions(
3830 message,
3831 result,
3832 expectedFieldPositions,
3833 UPRV_LENGTHOF(expectedFieldPositions));
3834 }
3835
3836 {
3837 const char16_t* message = u"Compact field basic";
3838 FormattedNumber result = assertFormatSingle(
3839 message,
3840 u"compact-short",
3841 u"K",
3842 NumberFormatter::with().notation(Notation::compactShort()),
3843 Locale::getUS(),
3844 65000,
3845 u"65K");
3846 static const UFieldPosition expectedFieldPositions[] = {
3847 // field, begin index, end index
3848 {UNUM_INTEGER_FIELD, 0, 2},
3849 {UNUM_COMPACT_FIELD, 2, 3}};
3850 assertNumberFieldPositions(
3851 message,
3852 result,
3853 expectedFieldPositions,
3854 UPRV_LENGTHOF(expectedFieldPositions));
3855 }
3856
3857 {
3858 const char16_t* message = u"Compact field with spaces";
3859 FormattedNumber result = assertFormatSingle(
3860 message,
3861 u"compact-long",
3862 u"KK",
3863 NumberFormatter::with().notation(Notation::compactLong()),
3864 Locale::getUS(),
3865 65000,
3866 u"65 thousand");
3867 static const UFieldPosition expectedFieldPositions[] = {
3868 // field, begin index, end index
3869 {UNUM_INTEGER_FIELD, 0, 2},
3870 {UNUM_COMPACT_FIELD, 3, 11}};
3871 assertNumberFieldPositions(
3872 message,
3873 result,
3874 expectedFieldPositions,
3875 UPRV_LENGTHOF(expectedFieldPositions));
3876 }
3877
3878 {
3879 const char16_t* message = u"Compact field with inner space";
3880 FormattedNumber result = assertFormatSingle(
3881 message,
3882 u"compact-long",
3883 u"KK",
3884 NumberFormatter::with().notation(Notation::compactLong()),
3885 "fil", // locale with interesting data
3886 6000,
3887 u"6 na libo");
3888 static const UFieldPosition expectedFieldPositions[] = {
3889 // field, begin index, end index
3890 {UNUM_INTEGER_FIELD, 0, 1},
3891 {UNUM_COMPACT_FIELD, 2, 9}};
3892 assertNumberFieldPositions(
3893 message,
3894 result,
3895 expectedFieldPositions,
3896 UPRV_LENGTHOF(expectedFieldPositions));
3897 }
3898
3899 {
3900 const char16_t* message = u"Compact field with bidi mark";
3901 FormattedNumber result = assertFormatSingle(
3902 message,
3903 u"compact-long",
3904 u"KK",
3905 NumberFormatter::with().notation(Notation::compactLong()),
3906 "he", // locale with interesting data
3907 6000,
3908 u"\u200F6 אלף");
3909 static const UFieldPosition expectedFieldPositions[] = {
3910 // field, begin index, end index
3911 {UNUM_INTEGER_FIELD, 1, 2},
3912 {UNUM_COMPACT_FIELD, 3, 6}};
3913 assertNumberFieldPositions(
3914 message,
3915 result,
3916 expectedFieldPositions,
3917 UPRV_LENGTHOF(expectedFieldPositions));
3918 }
3919
3920 {
3921 const char16_t* message = u"Compact with currency fields";
3922 FormattedNumber result = assertFormatSingle(
3923 message,
3924 u"compact-short currency/USD",
3925 u"K currency/USD",
3926 NumberFormatter::with().notation(Notation::compactShort()).unit(USD),
3927 "sr_Latn", // locale with interesting data
3928 65000,
3929 u"65 hilj. US$");
3930 static const UFieldPosition expectedFieldPositions[] = {
3931 // field, begin index, end index
3932 {UNUM_INTEGER_FIELD, 0, 2},
3933 {UNUM_COMPACT_FIELD, 3, 8},
3934 {UNUM_CURRENCY_FIELD, 9, 12}};
3935 assertNumberFieldPositions(
3936 message,
3937 result,
3938 expectedFieldPositions,
3939 UPRV_LENGTHOF(expectedFieldPositions));
3940 }
3941
3942 {
3943 const char16_t* message = u"Currency long name fields";
3944 FormattedNumber result = assertFormatSingle(
3945 message,
3946 u"currency/USD unit-width-full-name",
3947 u"currency/USD unit-width-full-name",
3948 NumberFormatter::with().unit(USD)
3949 .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME),
3950 "en",
3951 12345,
3952 u"12,345.00 US dollars");
3953 static const UFieldPosition expectedFieldPositions[] = {
3954 // field, begin index, end index
3955 {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
3956 {UNUM_INTEGER_FIELD, 0, 6},
3957 {UNUM_DECIMAL_SEPARATOR_FIELD, 6, 7},
3958 {UNUM_FRACTION_FIELD, 7, 9},
3959 {UNUM_CURRENCY_FIELD, 10, 20}};
3960 assertNumberFieldPositions(
3961 message,
3962 result,
3963 expectedFieldPositions,
3964 UPRV_LENGTHOF(expectedFieldPositions));
3965 }
3966
3967 {
3968 const char16_t* message = u"Compact with measure unit fields";
3969 FormattedNumber result = assertFormatSingle(
3970 message,
3971 u"compact-long measure-unit/length-meter unit-width-full-name",
3972 u"KK unit/meter unit-width-full-name",
3973 NumberFormatter::with().notation(Notation::compactLong())
3974 .unit(METER)
3975 .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME),
3976 Locale::getUS(),
3977 65000,
3978 u"65 thousand meters");
3979 static const UFieldPosition expectedFieldPositions[] = {
3980 // field, begin index, end index
3981 {UNUM_INTEGER_FIELD, 0, 2},
3982 {UNUM_COMPACT_FIELD, 3, 11},
3983 {UNUM_MEASURE_UNIT_FIELD, 12, 18}};
3984 assertNumberFieldPositions(
3985 message,
3986 result,
3987 expectedFieldPositions,
3988 UPRV_LENGTHOF(expectedFieldPositions));
3989 }
3990 }
3991
toFormat()3992 void NumberFormatterApiTest::toFormat() {
3993 IcuTestErrorCode status(*this, "icuFormat");
3994 LocalizedNumberFormatter lnf = NumberFormatter::withLocale("fr")
3995 .precision(Precision::fixedFraction(3));
3996 LocalPointer<Format> format(lnf.toFormat(status), status);
3997 FieldPosition fpos(UNUM_DECIMAL_SEPARATOR_FIELD);
3998 UnicodeString sb;
3999 format->format(514.23, sb, fpos, status);
4000 assertEquals("Should correctly format number", u"514,230", sb);
4001 assertEquals("Should find decimal separator", 3, fpos.getBeginIndex());
4002 assertEquals("Should find end of decimal separator", 4, fpos.getEndIndex());
4003 assertEquals(
4004 "ICU Format should round-trip",
4005 lnf.toSkeleton(status),
4006 dynamic_cast<LocalizedNumberFormatterAsFormat*>(format.getAlias())->getNumberFormatter()
4007 .toSkeleton(status));
4008
4009 UFormattedNumberData result;
4010 result.quantity.setToDouble(514.23);
4011 lnf.formatImpl(&result, status);
4012 FieldPositionIterator fpi1;
4013 {
4014 FieldPositionIteratorHandler fpih(&fpi1, status);
4015 result.getAllFieldPositions(fpih, status);
4016 }
4017
4018 FieldPositionIterator fpi2;
4019 format->format(514.23, sb.remove(), &fpi2, status);
4020
4021 assertTrue("Should produce same field position iterator", fpi1 == fpi2);
4022 }
4023
errors()4024 void NumberFormatterApiTest::errors() {
4025 LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).precision(
4026 Precision::fixedFraction(
4027 -1));
4028
4029 // formatInt
4030 UErrorCode status = U_ZERO_ERROR;
4031 FormattedNumber fn = lnf.formatInt(1, status);
4032 assertEquals(
4033 "Should fail in formatInt method with error code for rounding",
4034 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4035 status);
4036
4037 // formatDouble
4038 status = U_ZERO_ERROR;
4039 fn = lnf.formatDouble(1.0, status);
4040 assertEquals(
4041 "Should fail in formatDouble method with error code for rounding",
4042 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4043 status);
4044
4045 // formatDecimal (decimal error)
4046 status = U_ZERO_ERROR;
4047 fn = NumberFormatter::withLocale("en").formatDecimal("1x2", status);
4048 assertEquals(
4049 "Should fail in formatDecimal method with error code for decimal number syntax",
4050 U_DECIMAL_NUMBER_SYNTAX_ERROR,
4051 status);
4052
4053 // formatDecimal (setting error)
4054 status = U_ZERO_ERROR;
4055 fn = lnf.formatDecimal("1.0", status);
4056 assertEquals(
4057 "Should fail in formatDecimal method with error code for rounding",
4058 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4059 status);
4060
4061 // Skeleton string
4062 status = U_ZERO_ERROR;
4063 UnicodeString output = lnf.toSkeleton(status);
4064 assertEquals(
4065 "Should fail on toSkeleton terminal method with correct error code",
4066 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4067 status);
4068 assertTrue(
4069 "Terminal toSkeleton on error object should be bogus",
4070 output.isBogus());
4071
4072 // FieldPosition (constrained category)
4073 status = U_ZERO_ERROR;
4074 ConstrainedFieldPosition fp;
4075 fp.constrainCategory(UFIELD_CATEGORY_NUMBER);
4076 fn.nextPosition(fp, status);
4077 assertEquals(
4078 "Should fail on FieldPosition terminal method with correct error code",
4079 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4080 status);
4081
4082 // FieldPositionIterator (no constraints)
4083 status = U_ZERO_ERROR;
4084 fp.reset();
4085 fn.nextPosition(fp, status);
4086 assertEquals(
4087 "Should fail on FieldPositoinIterator terminal method with correct error code",
4088 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4089 status);
4090
4091 // Appendable
4092 status = U_ZERO_ERROR;
4093 UnicodeStringAppendable appendable(output.remove());
4094 fn.appendTo(appendable, status);
4095 assertEquals(
4096 "Should fail on Appendable terminal method with correct error code",
4097 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4098 status);
4099
4100 // UnicodeString
4101 status = U_ZERO_ERROR;
4102 output = fn.toString(status);
4103 assertEquals(
4104 "Should fail on UnicodeString terminal method with correct error code",
4105 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4106 status);
4107 assertTrue(
4108 "Terminal UnicodeString on error object should be bogus",
4109 output.isBogus());
4110
4111 // CopyErrorTo
4112 status = U_ZERO_ERROR;
4113 lnf.copyErrorTo(status);
4114 assertEquals(
4115 "Should fail since rounder is not legal with correct error code",
4116 U_NUMBER_ARG_OUTOFBOUNDS_ERROR,
4117 status);
4118 }
4119
validRanges()4120 void NumberFormatterApiTest::validRanges() {
4121
4122 #define EXPECTED_MAX_INT_FRAC_SIG 999
4123
4124 #define VALID_RANGE_ASSERT(status, method, lowerBound, argument) UPRV_BLOCK_MACRO_BEGIN { \
4125 UErrorCode expectedStatus = ((lowerBound <= argument) && (argument <= EXPECTED_MAX_INT_FRAC_SIG)) \
4126 ? U_ZERO_ERROR \
4127 : U_NUMBER_ARG_OUTOFBOUNDS_ERROR; \
4128 assertEquals( \
4129 UnicodeString(u"Incorrect status for " #method " on input ") \
4130 + Int64ToUnicodeString(argument), \
4131 expectedStatus, \
4132 status); \
4133 } UPRV_BLOCK_MACRO_END
4134
4135 #define VALID_RANGE_ONEARG(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
4136 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
4137 UErrorCode status = U_ZERO_ERROR; \
4138 NumberFormatter::with().setting(method(argument)).copyErrorTo(status); \
4139 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
4140 } \
4141 } UPRV_BLOCK_MACRO_END
4142
4143 #define VALID_RANGE_TWOARGS(setting, method, lowerBound) UPRV_BLOCK_MACRO_BEGIN { \
4144 for (int32_t argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) { \
4145 UErrorCode status = U_ZERO_ERROR; \
4146 /* Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases */ \
4147 NumberFormatter::with().setting(method(argument, EXPECTED_MAX_INT_FRAC_SIG)).copyErrorTo(status); \
4148 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
4149 status = U_ZERO_ERROR; \
4150 /* Pass lowerBound as the first argument so arg1 <= arg2 in expected cases */ \
4151 NumberFormatter::with().setting(method(lowerBound, argument)).copyErrorTo(status); \
4152 VALID_RANGE_ASSERT(status, method, lowerBound, argument); \
4153 /* Check that first argument must be less than or equal to second argument */ \
4154 NumberFormatter::with().setting(method(argument, argument - 1)).copyErrorTo(status); \
4155 assertEquals("Incorrect status for " #method " on max < min input", \
4156 U_NUMBER_ARG_OUTOFBOUNDS_ERROR, \
4157 status); \
4158 } \
4159 } UPRV_BLOCK_MACRO_END
4160
4161 VALID_RANGE_ONEARG(precision, Precision::fixedFraction, 0);
4162 VALID_RANGE_ONEARG(precision, Precision::minFraction, 0);
4163 VALID_RANGE_ONEARG(precision, Precision::maxFraction, 0);
4164 VALID_RANGE_TWOARGS(precision, Precision::minMaxFraction, 0);
4165 VALID_RANGE_ONEARG(precision, Precision::fixedSignificantDigits, 1);
4166 VALID_RANGE_ONEARG(precision, Precision::minSignificantDigits, 1);
4167 VALID_RANGE_ONEARG(precision, Precision::maxSignificantDigits, 1);
4168 VALID_RANGE_TWOARGS(precision, Precision::minMaxSignificantDigits, 1);
4169 VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMinDigits, 1);
4170 VALID_RANGE_ONEARG(precision, Precision::fixedFraction(1).withMaxDigits, 1);
4171 VALID_RANGE_ONEARG(notation, Notation::scientific().withMinExponentDigits, 1);
4172 VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo, 0);
4173 VALID_RANGE_ONEARG(integerWidth, IntegerWidth::zeroFillTo(0).truncateAt, -1);
4174 }
4175
copyMove()4176 void NumberFormatterApiTest::copyMove() {
4177 IcuTestErrorCode status(*this, "copyMove");
4178
4179 // Default constructors
4180 LocalizedNumberFormatter l1;
4181 assertEquals("Initial behavior", u"10", l1.formatInt(10, status).toString(status), true);
4182 if (status.errDataIfFailureAndReset()) { return; }
4183 assertEquals("Initial call count", 1, l1.getCallCount());
4184 assertTrue("Initial compiled", l1.getCompiled() == nullptr);
4185
4186 // Setup
4187 l1 = NumberFormatter::withLocale("en").unit(NoUnit::percent()).threshold(3);
4188 assertEquals("Initial behavior", u"10%", l1.formatInt(10, status).toString(status));
4189 assertEquals("Initial call count", 1, l1.getCallCount());
4190 assertTrue("Initial compiled", l1.getCompiled() == nullptr);
4191 l1.formatInt(123, status);
4192 assertEquals("Still not compiled", 2, l1.getCallCount());
4193 assertTrue("Still not compiled", l1.getCompiled() == nullptr);
4194 l1.formatInt(123, status);
4195 assertEquals("Compiled", u"10%", l1.formatInt(10, status).toString(status));
4196 assertEquals("Compiled", INT32_MIN, l1.getCallCount());
4197 assertTrue("Compiled", l1.getCompiled() != nullptr);
4198
4199 // Copy constructor
4200 LocalizedNumberFormatter l2 = l1;
4201 assertEquals("[constructor] Copy behavior", u"10%", l2.formatInt(10, status).toString(status));
4202 assertEquals("[constructor] Copy should not have compiled state", 1, l2.getCallCount());
4203 assertTrue("[constructor] Copy should not have compiled state", l2.getCompiled() == nullptr);
4204
4205 // Move constructor
4206 LocalizedNumberFormatter l3 = std::move(l1);
4207 assertEquals("[constructor] Move behavior", u"10%", l3.formatInt(10, status).toString(status));
4208 assertEquals("[constructor] Move *should* have compiled state", INT32_MIN, l3.getCallCount());
4209 assertTrue("[constructor] Move *should* have compiled state", l3.getCompiled() != nullptr);
4210 assertEquals("[constructor] Source should be reset after move", 0, l1.getCallCount());
4211 assertTrue("[constructor] Source should be reset after move", l1.getCompiled() == nullptr);
4212
4213 // Reset l1 and l2 to check for macro-props copying for behavior testing
4214 // Make the test more interesting: also warm them up with a compiled formatter.
4215 l1 = NumberFormatter::withLocale("en");
4216 l1.formatInt(1, status);
4217 l1.formatInt(1, status);
4218 l1.formatInt(1, status);
4219 l2 = NumberFormatter::withLocale("en");
4220 l2.formatInt(1, status);
4221 l2.formatInt(1, status);
4222 l2.formatInt(1, status);
4223
4224 // Copy assignment
4225 l1 = l3;
4226 assertEquals("[assignment] Copy behavior", u"10%", l1.formatInt(10, status).toString(status));
4227 assertEquals("[assignment] Copy should not have compiled state", 1, l1.getCallCount());
4228 assertTrue("[assignment] Copy should not have compiled state", l1.getCompiled() == nullptr);
4229
4230 // Move assignment
4231 l2 = std::move(l3);
4232 assertEquals("[assignment] Move behavior", u"10%", l2.formatInt(10, status).toString(status));
4233 assertEquals("[assignment] Move *should* have compiled state", INT32_MIN, l2.getCallCount());
4234 assertTrue("[assignment] Move *should* have compiled state", l2.getCompiled() != nullptr);
4235 assertEquals("[assignment] Source should be reset after move", 0, l3.getCallCount());
4236 assertTrue("[assignment] Source should be reset after move", l3.getCompiled() == nullptr);
4237
4238 // Coverage tests for UnlocalizedNumberFormatter
4239 UnlocalizedNumberFormatter u1;
4240 assertEquals("Default behavior", u"10", u1.locale("en").formatInt(10, status).toString(status));
4241 u1 = u1.unit(NoUnit::percent());
4242 assertEquals("Copy assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
4243 UnlocalizedNumberFormatter u2 = u1;
4244 assertEquals("Copy constructor", u"10%", u2.locale("en").formatInt(10, status).toString(status));
4245 UnlocalizedNumberFormatter u3 = std::move(u1);
4246 assertEquals("Move constructor", u"10%", u3.locale("en").formatInt(10, status).toString(status));
4247 u1 = NumberFormatter::with();
4248 u1 = std::move(u2);
4249 assertEquals("Move assignment", u"10%", u1.locale("en").formatInt(10, status).toString(status));
4250
4251 // FormattedNumber move operators
4252 FormattedNumber result = l1.formatInt(10, status);
4253 assertEquals("FormattedNumber move constructor", u"10%", result.toString(status));
4254 result = l1.formatInt(20, status);
4255 assertEquals("FormattedNumber move assignment", u"20%", result.toString(status));
4256 }
4257
localPointerCAPI()4258 void NumberFormatterApiTest::localPointerCAPI() {
4259 // NOTE: This is also the sample code in unumberformatter.h
4260 UErrorCode ec = U_ZERO_ERROR;
4261
4262 // Setup:
4263 LocalUNumberFormatterPointer uformatter(unumf_openForSkeletonAndLocale(u"percent", -1, "en", &ec));
4264 LocalUFormattedNumberPointer uresult(unumf_openResult(&ec));
4265 if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
4266
4267 // Format a decimal number:
4268 unumf_formatDecimal(uformatter.getAlias(), "9.87E-3", -1, uresult.getAlias(), &ec);
4269 if (!assertSuccess("", ec, true, __FILE__, __LINE__)) { return; }
4270
4271 // Get the location of the percent sign:
4272 UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
4273 unumf_resultNextFieldPosition(uresult.getAlias(), &ufpos, &ec);
4274 assertEquals("Percent sign location within '0.00987%'", 7, ufpos.beginIndex);
4275 assertEquals("Percent sign location within '0.00987%'", 8, ufpos.endIndex);
4276
4277 // No need to do any cleanup since we are using LocalPointer.
4278 }
4279
toObject()4280 void NumberFormatterApiTest::toObject() {
4281 IcuTestErrorCode status(*this, "toObject");
4282
4283 // const lvalue version
4284 {
4285 LocalizedNumberFormatter lnf = NumberFormatter::withLocale("en")
4286 .precision(Precision::fixedFraction(2));
4287 LocalPointer<LocalizedNumberFormatter> lnf2(lnf.clone());
4288 assertFalse("should create successfully, const lvalue", lnf2.isNull());
4289 assertEquals("object API test, const lvalue", u"1,000.00",
4290 lnf2->formatDouble(1000, status).toString(status));
4291 }
4292
4293 // rvalue reference version
4294 {
4295 LocalPointer<LocalizedNumberFormatter> lnf(
4296 NumberFormatter::withLocale("en")
4297 .precision(Precision::fixedFraction(2))
4298 .clone());
4299 assertFalse("should create successfully, rvalue reference", lnf.isNull());
4300 assertEquals("object API test, rvalue reference", u"1,000.00",
4301 lnf->formatDouble(1000, status).toString(status));
4302 }
4303
4304 // to std::unique_ptr via constructor
4305 {
4306 std::unique_ptr<LocalizedNumberFormatter> lnf(
4307 NumberFormatter::withLocale("en")
4308 .precision(Precision::fixedFraction(2))
4309 .clone());
4310 assertTrue("should create successfully, unique_ptr", static_cast<bool>(lnf));
4311 assertEquals("object API test, unique_ptr", u"1,000.00",
4312 lnf->formatDouble(1000, status).toString(status));
4313 }
4314
4315 // to std::unique_ptr via assignment
4316 {
4317 std::unique_ptr<LocalizedNumberFormatter> lnf =
4318 NumberFormatter::withLocale("en")
4319 .precision(Precision::fixedFraction(2))
4320 .clone();
4321 assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf));
4322 assertEquals("object API test, unique_ptr B", u"1,000.00",
4323 lnf->formatDouble(1000, status).toString(status));
4324 }
4325
4326 // to LocalPointer via assignment
4327 {
4328 LocalPointer<UnlocalizedNumberFormatter> f =
4329 NumberFormatter::with().clone();
4330 }
4331
4332 // make sure no memory leaks
4333 {
4334 NumberFormatter::with().clone();
4335 }
4336 }
4337
toDecimalNumber()4338 void NumberFormatterApiTest::toDecimalNumber() {
4339 IcuTestErrorCode status(*this, "toDecimalNumber");
4340 FormattedNumber fn = NumberFormatter::withLocale("bn-BD")
4341 .scale(Scale::powerOfTen(2))
4342 .precision(Precision::maxSignificantDigits(5))
4343 .formatDouble(9.87654321e12, status);
4344 assertEquals("Should have expected localized string result",
4345 u"৯৮,৭৬,৫০,০০,০০,০০,০০০", fn.toString(status));
4346 assertEquals(u"Should have expected toDecimalNumber string result",
4347 "9.8765E+14", fn.toDecimalNumber<std::string>(status).c_str());
4348 }
4349
microPropsInternals()4350 void NumberFormatterApiTest::microPropsInternals() {
4351 // Verify copy construction and assignment operators.
4352 int64_t testValues[2] = {4, 61};
4353
4354 MicroProps mp;
4355 assertEquals("capacity", 2, mp.mixedMeasures.getCapacity());
4356 mp.mixedMeasures[0] = testValues[0];
4357 mp.mixedMeasures[1] = testValues[1];
4358 MicroProps copyConstructed(mp);
4359 MicroProps copyAssigned;
4360 int64_t *resizeResult = mp.mixedMeasures.resize(4, 4);
4361 assertTrue("Resize success", resizeResult != NULL);
4362 copyAssigned = mp;
4363
4364 assertTrue("MicroProps success status", U_SUCCESS(mp.mixedMeasures.status));
4365 assertTrue("Copy Constructed success status", U_SUCCESS(copyConstructed.mixedMeasures.status));
4366 assertTrue("Copy Assigned success status", U_SUCCESS(copyAssigned.mixedMeasures.status));
4367 assertEquals("Original values[0]", testValues[0], mp.mixedMeasures[0]);
4368 assertEquals("Original values[1]", testValues[1], mp.mixedMeasures[1]);
4369 assertEquals("Copy Constructed[0]", testValues[0], copyConstructed.mixedMeasures[0]);
4370 assertEquals("Copy Constructed[1]", testValues[1], copyConstructed.mixedMeasures[1]);
4371 assertEquals("Copy Assigned[0]", testValues[0], copyAssigned.mixedMeasures[0]);
4372 assertEquals("Copy Assigned[1]", testValues[1], copyAssigned.mixedMeasures[1]);
4373 assertEquals("Original capacity", 4, mp.mixedMeasures.getCapacity());
4374 assertEquals("Copy Constructed capacity", 2, copyConstructed.mixedMeasures.getCapacity());
4375 assertEquals("Copy Assigned capacity", 4, copyAssigned.mixedMeasures.getCapacity());
4376 }
4377
4378 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
4379 * `conciseSkeleton` against the normalized version of `uskeleton` - this does
4380 * not round-trip uskeleton itself.
4381 *
4382 * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
4383 *
4384 * If `uskeleton` is nullptr, toSkeleton is expected to return an
4385 * U_UNSUPPORTED_ERROR.
4386 */
assertFormatDescending(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,...)4387 void NumberFormatterApiTest::assertFormatDescending(
4388 const char16_t* umessage,
4389 const char16_t* uskeleton,
4390 const char16_t* conciseSkeleton,
4391 const UnlocalizedNumberFormatter& f,
4392 Locale locale,
4393 ...) {
4394 va_list args;
4395 va_start(args, locale);
4396 UnicodeString message(TRUE, umessage, -1);
4397 static double inputs[] = {87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0};
4398 const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
4399 const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
4400 IcuTestErrorCode status(*this, "assertFormatDescending");
4401 status.setScope(message);
4402 UnicodeString expecteds[10];
4403 for (int16_t i = 0; i < 9; i++) {
4404 char16_t caseNumber = u'0' + i;
4405 double d = inputs[i];
4406 UnicodeString expected = va_arg(args, const char16_t*);
4407 expecteds[i] = expected;
4408 UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
4409 assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
4410 assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
4411 UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
4412 assertSuccess(message + u": Safe Path: " + caseNumber, status);
4413 assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
4414 }
4415 if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
4416 UnicodeString skeleton(TRUE, uskeleton, -1);
4417 // Only compare normalized skeletons: the tests need not provide the normalized forms.
4418 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
4419 UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
4420 assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
4421 LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
4422 for (int32_t i = 0; i < 9; i++) {
4423 double d = inputs[i];
4424 UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
4425 assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
4426 }
4427 // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
4428 // If the concise skeleton starts with '~', disable the round-trip check.
4429 bool shouldRoundTrip = true;
4430 if (conciseSkeleton[0] == u'~') {
4431 conciseSkeleton++;
4432 shouldRoundTrip = false;
4433 }
4434 LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
4435 if (shouldRoundTrip) {
4436 assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
4437 }
4438 for (int32_t i = 0; i < 9; i++) {
4439 double d = inputs[i];
4440 UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
4441 assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
4442 }
4443 } else {
4444 assertUndefinedSkeleton(f);
4445 }
4446 }
4447
4448 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
4449 * `conciseSkeleton` against the normalized version of `uskeleton` - this does
4450 * not round-trip uskeleton itself.
4451 *
4452 * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
4453 *
4454 * If `uskeleton` is nullptr, toSkeleton is expected to return an
4455 * U_UNSUPPORTED_ERROR.
4456 */
assertFormatDescendingBig(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,...)4457 void NumberFormatterApiTest::assertFormatDescendingBig(
4458 const char16_t* umessage,
4459 const char16_t* uskeleton,
4460 const char16_t* conciseSkeleton,
4461 const UnlocalizedNumberFormatter& f,
4462 Locale locale,
4463 ...) {
4464 va_list args;
4465 va_start(args, locale);
4466 UnicodeString message(TRUE, umessage, -1);
4467 static double inputs[] = {87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0};
4468 const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
4469 const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
4470 IcuTestErrorCode status(*this, "assertFormatDescendingBig");
4471 status.setScope(message);
4472 UnicodeString expecteds[10];
4473 for (int16_t i = 0; i < 9; i++) {
4474 char16_t caseNumber = u'0' + i;
4475 double d = inputs[i];
4476 UnicodeString expected = va_arg(args, const char16_t*);
4477 expecteds[i] = expected;
4478 UnicodeString actual1 = l1.formatDouble(d, status).toString(status);
4479 assertSuccess(message + u": Unsafe Path: " + caseNumber, status);
4480 assertEquals(message + u": Unsafe Path: " + caseNumber, expected, actual1);
4481 UnicodeString actual2 = l2.formatDouble(d, status).toString(status);
4482 assertSuccess(message + u": Safe Path: " + caseNumber, status);
4483 assertEquals(message + u": Safe Path: " + caseNumber, expected, actual2);
4484 }
4485 if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
4486 UnicodeString skeleton(TRUE, uskeleton, -1);
4487 // Only compare normalized skeletons: the tests need not provide the normalized forms.
4488 // Use the normalized form to construct the testing formatter to guarantee no loss of info.
4489 UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
4490 assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
4491 LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
4492 for (int32_t i = 0; i < 9; i++) {
4493 double d = inputs[i];
4494 UnicodeString actual3 = l3.formatDouble(d, status).toString(status);
4495 assertEquals(message + ": Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual3);
4496 }
4497 // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
4498 // If the concise skeleton starts with '~', disable the round-trip check.
4499 bool shouldRoundTrip = true;
4500 if (conciseSkeleton[0] == u'~') {
4501 conciseSkeleton++;
4502 shouldRoundTrip = false;
4503 }
4504 LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
4505 if (shouldRoundTrip) {
4506 assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
4507 }
4508 for (int32_t i = 0; i < 9; i++) {
4509 double d = inputs[i];
4510 UnicodeString actual4 = l4.formatDouble(d, status).toString(status);
4511 assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expecteds[i], actual4);
4512 }
4513 } else {
4514 assertUndefinedSkeleton(f);
4515 }
4516 }
4517
4518 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
4519 * `conciseSkeleton` against the normalized version of `uskeleton` - this does
4520 * not round-trip uskeleton itself.
4521 *
4522 * If `conciseSkeleton` starts with a "~", its round-trip check is skipped.
4523 *
4524 * If `uskeleton` is nullptr, toSkeleton is expected to return an
4525 * U_UNSUPPORTED_ERROR.
4526 */
4527 FormattedNumber
assertFormatSingle(const char16_t * umessage,const char16_t * uskeleton,const char16_t * conciseSkeleton,const UnlocalizedNumberFormatter & f,Locale locale,double input,const UnicodeString & expected)4528 NumberFormatterApiTest::assertFormatSingle(
4529 const char16_t* umessage,
4530 const char16_t* uskeleton,
4531 const char16_t* conciseSkeleton,
4532 const UnlocalizedNumberFormatter& f,
4533 Locale locale,
4534 double input,
4535 const UnicodeString& expected) {
4536 UnicodeString message(TRUE, umessage, -1);
4537 const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
4538 const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
4539 IcuTestErrorCode status(*this, "assertFormatSingle");
4540 status.setScope(message);
4541 FormattedNumber result1 = l1.formatDouble(input, status);
4542 UnicodeString actual1 = result1.toString(status);
4543 assertSuccess(message + u": Unsafe Path", status);
4544 assertEquals(message + u": Unsafe Path", expected, actual1);
4545 UnicodeString actual2 = l2.formatDouble(input, status).toString(status);
4546 assertSuccess(message + u": Safe Path", status);
4547 assertEquals(message + u": Safe Path", expected, actual2);
4548 if (uskeleton != nullptr) { // if null, skeleton is declared as undefined.
4549 UnicodeString skeleton(TRUE, uskeleton, -1);
4550 // Only compare normalized skeletons: the tests need not provide the normalized forms.
4551 // Use the normalized form to construct the testing formatter to ensure no loss of info.
4552 UnicodeString normalized = NumberFormatter::forSkeleton(skeleton, status).toSkeleton(status);
4553 assertEquals(message + ": Skeleton:", normalized, f.toSkeleton(status));
4554 LocalizedNumberFormatter l3 = NumberFormatter::forSkeleton(normalized, status).locale(locale);
4555 UnicodeString actual3 = l3.formatDouble(input, status).toString(status);
4556 assertEquals(message + ": Skeleton Path: '" + normalized + "': " + input, expected, actual3);
4557 // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
4558 // If the concise skeleton starts with '~', disable the round-trip check.
4559 bool shouldRoundTrip = true;
4560 if (conciseSkeleton[0] == u'~') {
4561 conciseSkeleton++;
4562 shouldRoundTrip = false;
4563 }
4564 LocalizedNumberFormatter l4 = NumberFormatter::forSkeleton(conciseSkeleton, status).locale(locale);
4565 if (shouldRoundTrip) {
4566 assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton(status));
4567 }
4568 UnicodeString actual4 = l4.formatDouble(input, status).toString(status);
4569 assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + input, expected, actual4);
4570 } else {
4571 assertUndefinedSkeleton(f);
4572 }
4573 return result1;
4574 }
4575
assertUndefinedSkeleton(const UnlocalizedNumberFormatter & f)4576 void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter& f) {
4577 UErrorCode status = U_ZERO_ERROR;
4578 UnicodeString skeleton = f.toSkeleton(status);
4579 assertEquals(
4580 u"Expect toSkeleton to fail, but passed, producing: " + skeleton,
4581 U_UNSUPPORTED_ERROR,
4582 status);
4583 }
4584
assertNumberFieldPositions(const char16_t * message,const FormattedNumber & formattedNumber,const UFieldPosition * expectedFieldPositions,int32_t length)4585 void NumberFormatterApiTest::assertNumberFieldPositions(
4586 const char16_t* message,
4587 const FormattedNumber& formattedNumber,
4588 const UFieldPosition* expectedFieldPositions,
4589 int32_t length) {
4590 IcuTestErrorCode status(*this, "assertNumberFieldPositions");
4591
4592 // Check FormattedValue functions
4593 checkFormattedValue(
4594 message,
4595 static_cast<const FormattedValue&>(formattedNumber),
4596 formattedNumber.toString(status),
4597 UFIELD_CATEGORY_NUMBER,
4598 expectedFieldPositions,
4599 length);
4600 }
4601
4602
4603 #endif /* #if !UCONFIG_NO_FORMATTING */
4604