1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
8 #include "number_decimalquantity.h"
9 #include "number_decnum.h"
12 #include "number_utils.h"
13 #include "numbertest.h"
15 void DecimalQuantityTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) {
17 logln("TestSuite DecimalQuantityTest: ");
20 TESTCASE_AUTO(testDecimalQuantityBehaviorStandalone);
21 TESTCASE_AUTO(testSwitchStorage);
22 TESTCASE_AUTO(testCopyMove);
23 TESTCASE_AUTO(testAppend);
25 // Slow test: run in exhaustive mode only
26 TESTCASE_AUTO(testConvertToAccurateDouble);
28 TESTCASE_AUTO(testUseApproximateDoubleWhenAble);
29 TESTCASE_AUTO(testHardDoubleConversion);
30 TESTCASE_AUTO(testToDouble);
31 TESTCASE_AUTO(testMaxDigits);
32 TESTCASE_AUTO(testNickelRounding);
33 TESTCASE_AUTO(testCompactDecimalSuppressedExponent);
34 TESTCASE_AUTO(testSuppressedExponentUnchangedByInitialScaling);
38 void DecimalQuantityTest::assertDoubleEquals(UnicodeString message, double a, double b) {
44 diff = diff < 0 ? -diff : diff;
45 double bound = a < 0 ? -a * 1e-6 : a * 1e-6;
47 errln(message + u": " + DoubleToUnicodeString(a) + u" vs " + DoubleToUnicodeString(b) + u" differ by " + DoubleToUnicodeString(diff));
51 void DecimalQuantityTest::assertHealth(const DecimalQuantity &fq) {
52 const char16_t* health = fq.checkHealth();
53 if (health != nullptr) {
54 errln(UnicodeString(u"HEALTH FAILURE: ") + UnicodeString(health) + u": " + fq.toString());
59 DecimalQuantityTest::assertToStringAndHealth(const DecimalQuantity &fq, const UnicodeString &expected) {
60 UnicodeString actual = fq.toString();
61 assertEquals("DecimalQuantity toString failed", expected, actual);
65 void DecimalQuantityTest::checkDoubleBehavior(double d, bool explicitRequired) {
68 if (explicitRequired) {
69 assertTrue("Should be using approximate double", !fq.isExplicitExactDouble());
71 UnicodeString baseStr = fq.toString();
73 UnicodeString newStr = fq.toString();
74 if (explicitRequired) {
75 assertTrue("Should not be using approximate double", fq.isExplicitExactDouble());
78 UnicodeString(u"After conversion to exact BCD (double): ") + baseStr + u" vs " + newStr,
82 void DecimalQuantityTest::testDecimalQuantityBehaviorStandalone() {
83 UErrorCode status = U_ZERO_ERROR;
85 assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 0E0>");
87 assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 51423E0>");
88 fq.adjustMagnitude(-3);
89 assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 51423E-3>");
91 fq.setToLong(90909090909000L);
92 assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 90909090909E3>");
94 fq.applyMaxInteger(5);
95 assertToStringAndHealth(fq, u"<DecimalQuantity 2:0 long 9E3>");
97 assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 9E3>");
99 fq.setToDouble(987.654321);
100 assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987654321E-6>");
101 fq.roundToInfinity();
102 assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987654321E-6>");
103 fq.roundToIncrement(0.005, RoundingMode::UNUM_ROUND_HALFEVEN, status);
104 assertSuccess("Rounding to increment", status);
105 assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 987655E-3>");
106 fq.roundToMagnitude(-2, RoundingMode::UNUM_ROUND_HALFEVEN, status);
107 assertSuccess("Rounding to magnitude", status);
108 assertToStringAndHealth(fq, u"<DecimalQuantity 2:-3 long 98766E-2>");
111 void DecimalQuantityTest::testSwitchStorage() {
112 UErrorCode status = U_ZERO_ERROR;
115 fq.setToLong(1234123412341234L);
116 assertFalse("Should not be using byte array", fq.isUsingBytes());
117 assertEquals("Failed on initialize", u"1.234123412341234E+15", fq.toScientificString());
120 fq.appendDigit(5, 0, true);
121 assertTrue("Should be using byte array", fq.isUsingBytes());
122 assertEquals("Failed on multiply", u"1.2341234123412345E+16", fq.toScientificString());
125 fq.roundToMagnitude(5, RoundingMode::UNUM_ROUND_HALFEVEN, status);
126 assertSuccess("Rounding to magnitude", status);
127 assertFalse("Should not be using byte array", fq.isUsingBytes());
128 assertEquals("Failed on round", u"1.23412341234E+16", fq.toScientificString());
130 // Bytes with popFromLeft
131 fq.setToDecNumber({"999999999999999999"}, status);
132 assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 bytes 999999999999999999E0>");
133 fq.applyMaxInteger(17);
134 assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 bytes 99999999999999999E0>");
135 fq.applyMaxInteger(16);
136 assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 9999999999999999E0>");
137 fq.applyMaxInteger(15);
138 assertToStringAndHealth(fq, u"<DecimalQuantity 0:0 long 999999999999999E0>");
141 void DecimalQuantityTest::testCopyMove() {
142 // Small numbers (fits in BCD long)
145 a.setToLong(1234123412341234L);
146 DecimalQuantity b = a; // copy constructor
147 assertToStringAndHealth(a, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
148 assertToStringAndHealth(b, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
149 DecimalQuantity c(std::move(a)); // move constructor
150 assertToStringAndHealth(c, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
152 assertToStringAndHealth(c, u"<DecimalQuantity 0:0 long 54321E0>");
153 c = b; // copy assignment
154 assertToStringAndHealth(b, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
155 assertToStringAndHealth(c, u"<DecimalQuantity 0:0 long 1234123412341234E0>");
158 c = std::move(b); // move assignment
159 assertToStringAndHealth(c, u"<DecimalQuantity 0:0 long 45678E0>");
160 a = std::move(c); // move assignment to a defunct object
161 assertToStringAndHealth(a, u"<DecimalQuantity 0:0 long 45678E0>");
164 // Large numbers (requires byte allocation)
166 IcuTestErrorCode status(*this, "testCopyMove");
168 a.setToDecNumber({"1234567890123456789", -1}, status);
169 DecimalQuantity b = a; // copy constructor
170 assertToStringAndHealth(a, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
171 assertToStringAndHealth(b, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
172 DecimalQuantity c(std::move(a)); // move constructor
173 assertToStringAndHealth(c, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
174 c.setToDecNumber({"9876543210987654321", -1}, status);
175 assertToStringAndHealth(c, u"<DecimalQuantity 0:0 bytes 9876543210987654321E0>");
176 c = b; // copy assignment
177 assertToStringAndHealth(b, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
178 assertToStringAndHealth(c, u"<DecimalQuantity 0:0 bytes 1234567890123456789E0>");
179 b.setToDecNumber({"876543210987654321", -1}, status);
180 c.setToDecNumber({"987654321098765432", -1}, status);
181 c = std::move(b); // move assignment
182 assertToStringAndHealth(c, u"<DecimalQuantity 0:0 bytes 876543210987654321E0>");
183 a = std::move(c); // move assignment to a defunct object
184 assertToStringAndHealth(a, u"<DecimalQuantity 0:0 bytes 876543210987654321E0>");
188 void DecimalQuantityTest::testAppend() {
190 fq.appendDigit(1, 0, true);
191 assertEquals("Failed on append", u"1E+0", fq.toScientificString());
193 fq.appendDigit(2, 0, true);
194 assertEquals("Failed on append", u"1.2E+1", fq.toScientificString());
196 fq.appendDigit(3, 1, true);
197 assertEquals("Failed on append", u"1.203E+3", fq.toScientificString());
199 fq.appendDigit(0, 1, true);
200 assertEquals("Failed on append", u"1.203E+5", fq.toScientificString());
202 fq.appendDigit(4, 0, true);
203 assertEquals("Failed on append", u"1.203004E+6", fq.toScientificString());
205 fq.appendDigit(0, 0, true);
206 assertEquals("Failed on append", u"1.203004E+7", fq.toScientificString());
208 fq.appendDigit(5, 0, false);
209 assertEquals("Failed on append", u"1.20300405E+7", fq.toScientificString());
211 fq.appendDigit(6, 0, false);
212 assertEquals("Failed on append", u"1.203004056E+7", fq.toScientificString());
214 fq.appendDigit(7, 3, false);
215 assertEquals("Failed on append", u"1.2030040560007E+7", fq.toScientificString());
217 UnicodeString baseExpected(u"1.2030040560007");
218 for (int i = 0; i < 10; i++) {
219 fq.appendDigit(8, 0, false);
220 baseExpected.append(u'8');
221 UnicodeString expected(baseExpected);
222 expected.append(u"E+7");
223 assertEquals("Failed on append", expected, fq.toScientificString());
226 fq.appendDigit(9, 2, false);
227 baseExpected.append(u"009");
228 UnicodeString expected(baseExpected);
229 expected.append(u"E+7");
230 assertEquals("Failed on append", expected, fq.toScientificString());
234 void DecimalQuantityTest::testConvertToAccurateDouble() {
235 // based on https://github.com/google/double-conversion/issues/28
236 static double hardDoubles[] = {
237 1651087494906221570.0,
238 2.207817077636718750000000000000,
239 1.818351745605468750000000000000,
240 3.941719055175781250000000000000,
241 3.738609313964843750000000000000,
242 3.967735290527343750000000000000,
243 1.328025817871093750000000000000,
244 3.920967102050781250000000000000,
245 1.015235900878906250000000000000,
246 1.335227966308593750000000000000,
247 1.344520568847656250000000000000,
248 2.879127502441406250000000000000,
249 3.695838928222656250000000000000,
250 1.845344543457031250000000000000,
251 3.793952941894531250000000000000,
252 3.211402893066406250000000000000,
253 2.565971374511718750000000000000,
254 0.965156555175781250000000000000,
255 2.700004577636718750000000000000,
256 0.767097473144531250000000000000,
257 1.780448913574218750000000000000,
258 2.624839782714843750000000000000,
259 1.305290222167968750000000000000,
260 3.834922790527343750000000000000,};
262 static double exactDoubles[] = {
265 -5074790912492772E-327,
266 83602530019752571E-327,
267 4.503599627370496E15,
268 6.789512076111555E15,
269 9.007199254740991E15,
270 9.007199254740992E15};
272 for (double d : hardDoubles) {
273 checkDoubleBehavior(d, true);
276 for (double d : exactDoubles) {
277 checkDoubleBehavior(d, false);
280 assertDoubleEquals(u"NaN check failed", NAN, DecimalQuantity().setToDouble(NAN).toDouble());
282 u"Inf check failed", INFINITY, DecimalQuantity().setToDouble(INFINITY).toDouble());
284 u"-Inf check failed", -INFINITY, DecimalQuantity().setToDouble(-INFINITY).toDouble());
286 // Generate random doubles
287 for (int32_t i = 0; i < 10000; i++) {
289 for (int32_t j = 0; j < 8; j++) {
290 bytes[j] = static_cast<uint8_t>(rand() % 256);
293 uprv_memcpy(&d, bytes, 8);
294 if (std::isnan(d) || !std::isfinite(d)) { continue; }
295 checkDoubleBehavior(d, false);
299 void DecimalQuantityTest::testUseApproximateDoubleWhenAble() {
300 static const struct TestCase {
303 RoundingMode roundingMode;
305 } cases[] = {{1.2345678, 1, RoundingMode::UNUM_ROUND_HALFEVEN, false},
306 {1.2345678, 7, RoundingMode::UNUM_ROUND_HALFEVEN, false},
307 {1.2345678, 12, RoundingMode::UNUM_ROUND_HALFEVEN, false},
308 {1.2345678, 13, RoundingMode::UNUM_ROUND_HALFEVEN, true},
309 {1.235, 1, RoundingMode::UNUM_ROUND_HALFEVEN, false},
310 {1.235, 2, RoundingMode::UNUM_ROUND_HALFEVEN, true},
311 {1.235, 3, RoundingMode::UNUM_ROUND_HALFEVEN, false},
312 {1.000000000000001, 0, RoundingMode::UNUM_ROUND_HALFEVEN, false},
313 {1.000000000000001, 0, RoundingMode::UNUM_ROUND_CEILING, true},
314 {1.235, 1, RoundingMode::UNUM_ROUND_CEILING, false},
315 {1.235, 2, RoundingMode::UNUM_ROUND_CEILING, false},
316 {1.235, 3, RoundingMode::UNUM_ROUND_CEILING, true}};
318 UErrorCode status = U_ZERO_ERROR;
319 for (TestCase cas : cases) {
321 fq.setToDouble(cas.d);
322 assertTrue("Should be using approximate double", !fq.isExplicitExactDouble());
323 fq.roundToMagnitude(-cas.maxFrac, cas.roundingMode, status);
324 assertSuccess("Rounding to magnitude", status);
325 if (cas.usesExact != fq.isExplicitExactDouble()) {
326 errln(UnicodeString(u"Using approximate double after rounding: ") + fq.toString());
331 void DecimalQuantityTest::testHardDoubleConversion() {
332 static const struct TestCase {
334 const char16_t* expectedOutput;
336 { 512.0000000000017, u"512.0000000000017" },
337 { 4095.9999999999977, u"4095.9999999999977" },
338 { 4095.999999999998, u"4095.999999999998" },
339 { 4095.9999999999986, u"4095.9999999999986" },
340 { 4095.999999999999, u"4095.999999999999" },
341 { 4095.9999999999995, u"4095.9999999999995" },
342 { 4096.000000000001, u"4096.000000000001" },
343 { 4096.000000000002, u"4096.000000000002" },
344 { 4096.000000000003, u"4096.000000000003" },
345 { 4096.000000000004, u"4096.000000000004" },
346 { 4096.000000000005, u"4096.000000000005" },
347 { 4096.0000000000055, u"4096.0000000000055" },
348 { 4096.000000000006, u"4096.000000000006" },
349 { 4096.000000000007, u"4096.000000000007" } };
351 for (auto& cas : cases) {
353 q.setToDouble(cas.input);
355 UnicodeString actualOutput = q.toPlainString();
356 assertEquals("", cas.expectedOutput, actualOutput);
360 void DecimalQuantityTest::testToDouble() {
361 IcuTestErrorCode status(*this, "testToDouble");
362 static const struct TestCase {
363 const char* input; // char* for the decNumber constructor
367 { "514.23", 514.23 },
368 { "-3.142E-271", -3.142e-271 } };
370 for (auto& cas : cases) {
371 status.setScope(cas.input);
373 q.setToDecNumber({cas.input, -1}, status);
374 double actual = q.toDouble();
375 assertEquals("Doubles should exactly equal", cas.expected, actual);
379 void DecimalQuantityTest::testMaxDigits() {
380 IcuTestErrorCode status(*this, "testMaxDigits");
382 dq.setToDouble(876.543);
383 dq.roundToInfinity();
385 dq.applyMaxInteger(2);
386 dq.setMinFraction(0);
387 dq.roundToMagnitude(-2, UNUM_ROUND_FLOOR, status);
388 assertEquals("Should trim, toPlainString", "76.54", dq.toPlainString());
389 assertEquals("Should trim, toScientificString", "7.654E+1", dq.toScientificString());
390 assertEquals("Should trim, toLong", 76LL, dq.toLong(true));
391 assertEquals("Should trim, toFractionLong", (int64_t) 54, (int64_t) dq.toFractionLong(false));
392 assertEquals("Should trim, toDouble", 76.54, dq.toDouble());
393 // To test DecNum output, check the round-trip.
395 dq.toDecNum(dn, status);
396 DecimalQuantity copy;
397 copy.setToDecNum(dn, status);
398 assertEquals("Should trim, toDecNum", "76.54", copy.toPlainString());
401 void DecimalQuantityTest::testNickelRounding() {
402 IcuTestErrorCode status(*this, "testNickelRounding");
406 UNumberFormatRoundingMode roundingMode;
407 const char16_t* expected;
409 {1.000, -2, UNUM_ROUND_HALFEVEN, u"1"},
410 {1.001, -2, UNUM_ROUND_HALFEVEN, u"1"},
411 {1.010, -2, UNUM_ROUND_HALFEVEN, u"1"},
412 {1.020, -2, UNUM_ROUND_HALFEVEN, u"1"},
413 {1.024, -2, UNUM_ROUND_HALFEVEN, u"1"},
414 {1.025, -2, UNUM_ROUND_HALFEVEN, u"1"},
415 {1.025, -2, UNUM_ROUND_HALFDOWN, u"1"},
416 {1.025, -2, UNUM_ROUND_HALF_ODD, u"1.05"},
417 {1.025, -2, UNUM_ROUND_HALF_CEILING, u"1.05"},
418 {1.025, -2, UNUM_ROUND_HALF_FLOOR, u"1"},
419 {1.025, -2, UNUM_ROUND_HALFUP, u"1.05"},
420 {1.026, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
421 {1.030, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
422 {1.040, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
423 {1.050, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
424 {1.060, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
425 {1.070, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
426 {1.074, -2, UNUM_ROUND_HALFEVEN, u"1.05"},
427 {1.075, -2, UNUM_ROUND_HALFDOWN, u"1.05"},
428 {1.075, -2, UNUM_ROUND_HALF_ODD, u"1.05"},
429 {1.075, -2, UNUM_ROUND_HALF_CEILING, u"1.1"},
430 {1.075, -2, UNUM_ROUND_HALF_FLOOR, u"1.05"},
431 {1.075, -2, UNUM_ROUND_HALFUP, u"1.1"},
432 {1.075, -2, UNUM_ROUND_HALFEVEN, u"1.1"},
433 {1.076, -2, UNUM_ROUND_HALFEVEN, u"1.1"},
434 {1.080, -2, UNUM_ROUND_HALFEVEN, u"1.1"},
435 {1.090, -2, UNUM_ROUND_HALFEVEN, u"1.1"},
436 {1.099, -2, UNUM_ROUND_HALFEVEN, u"1.1"},
437 {1.999, -2, UNUM_ROUND_HALFEVEN, u"2"},
438 {2.25, -1, UNUM_ROUND_HALFEVEN, u"2"},
439 {2.25, -1, UNUM_ROUND_HALFUP, u"2.5"},
440 {2.75, -1, UNUM_ROUND_HALFDOWN, u"2.5"},
441 {2.75, -1, UNUM_ROUND_HALF_ODD, u"2.5"},
442 {2.75, -1, UNUM_ROUND_HALF_CEILING, u"3"},
443 {2.75, -1, UNUM_ROUND_HALF_FLOOR, u"2.5"},
444 {2.75, -1, UNUM_ROUND_HALFEVEN, u"3"},
445 {3.00, -1, UNUM_ROUND_CEILING, u"3"},
446 {3.25, -1, UNUM_ROUND_CEILING, u"3.5"},
447 {3.50, -1, UNUM_ROUND_CEILING, u"3.5"},
448 {3.75, -1, UNUM_ROUND_CEILING, u"4"},
449 {4.00, -1, UNUM_ROUND_FLOOR, u"4"},
450 {4.25, -1, UNUM_ROUND_FLOOR, u"4"},
451 {4.50, -1, UNUM_ROUND_FLOOR, u"4.5"},
452 {4.75, -1, UNUM_ROUND_FLOOR, u"4.5"},
453 {5.00, -1, UNUM_ROUND_UP, u"5"},
454 {5.25, -1, UNUM_ROUND_UP, u"5.5"},
455 {5.50, -1, UNUM_ROUND_UP, u"5.5"},
456 {5.75, -1, UNUM_ROUND_UP, u"6"},
457 {6.00, -1, UNUM_ROUND_DOWN, u"6"},
458 {6.25, -1, UNUM_ROUND_DOWN, u"6"},
459 {6.50, -1, UNUM_ROUND_DOWN, u"6.5"},
460 {6.75, -1, UNUM_ROUND_DOWN, u"6.5"},
461 {7.00, -1, UNUM_ROUND_UNNECESSARY, u"7"},
462 {7.50, -1, UNUM_ROUND_UNNECESSARY, u"7.5"},
464 for (const auto& cas : cases) {
465 UnicodeString message = DoubleToUnicodeString(cas.input) + u" @ " + Int64ToUnicodeString(cas.magnitude) + u" / " + Int64ToUnicodeString(cas.roundingMode);
466 status.setScope(message);
468 dq.setToDouble(cas.input);
469 dq.roundToNickel(cas.magnitude, cas.roundingMode, status);
470 status.errIfFailureAndReset();
471 UnicodeString actual = dq.toPlainString();
472 assertEquals(message, cas.expected, actual);
477 dq.roundToNickel(-1, UNUM_ROUND_UNNECESSARY, status);
478 status.expectErrorAndReset(U_FORMAT_INEXACT_ERROR);
481 void DecimalQuantityTest::testCompactDecimalSuppressedExponent() {
482 IcuTestErrorCode status(*this, "testCompactDecimalSuppressedExponent");
483 Locale ulocale("fr-FR");
486 UnicodeString skeleton;
488 const char16_t* expectedString;
489 int64_t expectedLong;
490 double expectedDouble;
491 const char16_t* expectedPlainString;
492 int32_t expectedSuppressedExponent;
494 // unlocalized formatter skeleton, input, string output, long output, double output, BigDecimal output, plain string, suppressed exponent
495 {u"", 123456789, u"123 456 789", 123456789L, 123456789.0, u"123456789", 0},
496 {u"compact-long", 123456789, u"123 millions", 123000000L, 123000000.0, u"123000000", 6},
497 {u"compact-short", 123456789, u"123 M", 123000000L, 123000000.0, u"123000000", 6},
498 {u"scientific", 123456789, u"1,234568E8", 123456800L, 123456800.0, u"123456800", 8},
500 {u"", 1234567, u"1 234 567", 1234567L, 1234567.0, u"1234567", 0},
501 {u"compact-long", 1234567, u"1,2 million", 1200000L, 1200000.0, u"1200000", 6},
502 {u"compact-short", 1234567, u"1,2 M", 1200000L, 1200000.0, u"1200000", 6},
503 {u"scientific", 1234567, u"1,234567E6", 1234567L, 1234567.0, u"1234567", 6},
505 {u"", 123456, u"123 456", 123456L, 123456.0, u"123456", 0},
506 {u"compact-long", 123456, u"123 mille", 123000L, 123000.0, u"123000", 3},
507 {u"compact-short", 123456, u"123 k", 123000L, 123000.0, u"123000", 3},
508 {u"scientific", 123456, u"1,23456E5", 123456L, 123456.0, u"123456", 5},
510 {u"", 123, u"123", 123L, 123.0, u"123", 0},
511 {u"compact-long", 123, u"123", 123L, 123.0, u"123", 0},
512 {u"compact-short", 123, u"123", 123L, 123.0, u"123", 0},
513 {u"scientific", 123, u"1,23E2", 123L, 123.0, u"123", 2},
515 {u"", 1.2, u"1,2", 1L, 1.2, u"1.2", 0},
516 {u"compact-long", 1.2, u"1,2", 1L, 1.2, u"1.2", 0},
517 {u"compact-short", 1.2, u"1,2", 1L, 1.2, u"1.2", 0},
518 {u"scientific", 1.2, u"1,2E0", 1L, 1.2, u"1.2", 0},
520 {u"", 0.12, u"0,12", 0L, 0.12, u"0.12", 0},
521 {u"compact-long", 0.12, u"0,12", 0L, 0.12, u"0.12", 0},
522 {u"compact-short", 0.12, u"0,12", 0L, 0.12, u"0.12", 0},
523 {u"scientific", 0.12, u"1,2E-1", 0L, 0.12, u"0.12", -1},
525 {u"", 0.012, u"0,012", 0L, 0.012, u"0.012", 0},
526 {u"compact-long", 0.012, u"0,012", 0L, 0.012, u"0.012", 0},
527 {u"compact-short", 0.012, u"0,012", 0L, 0.012, u"0.012", 0},
528 {u"scientific", 0.012, u"1,2E-2", 0L, 0.012, u"0.012", -2},
530 {u"", 999.9, u"999,9", 999L, 999.9, u"999.9", 0},
531 {u"compact-long", 999.9, u"1 millier", 1000L, 1000.0, u"1000", 3},
532 {u"compact-short", 999.9, u"1 k", 1000L, 1000.0, u"1000", 3},
533 {u"scientific", 999.9, u"9,999E2", 999L, 999.9, u"999.9", 2},
535 {u"", 1000.0, u"1 000", 1000L, 1000.0, u"1000", 0},
536 {u"compact-long", 1000.0, u"1 millier", 1000L, 1000.0, u"1000", 3},
537 {u"compact-short", 1000.0, u"1 k", 1000L, 1000.0, u"1000", 3},
538 {u"scientific", 1000.0, u"1E3", 1000L, 1000.0, u"1000", 3},
540 for (const auto& cas : cases) {
541 // test the helper methods used to compute plural operand values
543 LocalizedNumberFormatter formatter =
544 NumberFormatter::forSkeleton(cas.skeleton, status)
546 FormattedNumber fn = formatter.formatDouble(cas.input, status);
548 fn.getDecimalQuantity(dq, status);
549 UnicodeString actualString = fn.toString(status);
550 int64_t actualLong = dq.toLong();
551 double actualDouble = dq.toDouble();
552 UnicodeString actualPlainString = dq.toPlainString();
553 int32_t actualSuppressedExponent = dq.getExponent();
556 u"formatted number " + cas.skeleton + u" toString: " + cas.input,
560 u"compact decimal " + cas.skeleton + u" toLong: " + cas.input,
564 u"compact decimal " + cas.skeleton + u" toDouble: " + cas.input,
568 u"formatted number " + cas.skeleton + u" toPlainString: " + cas.input,
569 cas.expectedPlainString,
572 u"compact decimal " + cas.skeleton + u" suppressed exponent: " + cas.input,
573 cas.expectedSuppressedExponent,
574 actualSuppressedExponent);
576 // test the actual computed values of the plural operands
578 double expectedNOperand = cas.expectedDouble;
579 double expectedIOperand = cas.expectedLong;
580 double expectedEOperand = cas.expectedSuppressedExponent;
581 double actualNOperand = dq.getPluralOperand(PLURAL_OPERAND_N);
582 double actualIOperand = dq.getPluralOperand(PLURAL_OPERAND_I);
583 double actualEOperand = dq.getPluralOperand(PLURAL_OPERAND_E);
586 u"compact decimal " + cas.skeleton + u" n operand: " + cas.input,
590 u"compact decimal " + cas.skeleton + u" i operand: " + cas.input,
594 u"compact decimal " + cas.skeleton + " e operand: " + cas.input,
600 void DecimalQuantityTest::testSuppressedExponentUnchangedByInitialScaling() {
601 IcuTestErrorCode status(*this, "testCompactDecimalSuppressedExponent");
602 Locale ulocale("fr-FR");
603 LocalizedNumberFormatter withLocale = NumberFormatter::withLocale(ulocale);
604 LocalizedNumberFormatter compactLong =
605 withLocale.notation(Notation::compactLong());
606 LocalizedNumberFormatter compactScaled =
607 compactLong.scale(Scale::powerOfTen(3));
611 UnicodeString expectedString;
612 double expectedNOperand;
613 double expectedIOperand;
614 double expectedEOperand;
616 // input, compact long string output,
617 // compact n operand, compact i operand, compact e operand
618 {123456789, "123 millions", 123000000.0, 123000000.0, 6.0},
619 {1234567, "1,2 million", 1200000.0, 1200000.0, 6.0},
620 {123456, "123 mille", 123000.0, 123000.0, 3.0},
621 {123, "123", 123.0, 123.0, 0.0},
624 for (const auto& cas : cases) {
625 FormattedNumber fnCompactScaled = compactScaled.formatInt(cas.input, status);
626 DecimalQuantity dqCompactScaled;
627 fnCompactScaled.getDecimalQuantity(dqCompactScaled, status);
628 double compactScaledEOperand = dqCompactScaled.getPluralOperand(PLURAL_OPERAND_E);
630 FormattedNumber fnCompact = compactLong.formatInt(cas.input, status);
631 DecimalQuantity dqCompact;
632 fnCompact.getDecimalQuantity(dqCompact, status);
633 UnicodeString actualString = fnCompact.toString(status);
634 double compactNOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_N);
635 double compactIOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_I);
636 double compactEOperand = dqCompact.getPluralOperand(PLURAL_OPERAND_E);
638 u"formatted number " + Int64ToUnicodeString(cas.input) + " compactLong toString: ",
642 u"compact decimal " + DoubleToUnicodeString(cas.input) + ", n operand vs. expected",
643 cas.expectedNOperand,
646 u"compact decimal " + DoubleToUnicodeString(cas.input) + ", i operand vs. expected",
647 cas.expectedIOperand,
650 u"compact decimal " + DoubleToUnicodeString(cas.input) + ", e operand vs. expected",
651 cas.expectedEOperand,
654 // By scaling by 10^3 in a locale that has words / compact notation
655 // based on powers of 10^3, we guarantee that the suppressed
656 // exponent will differ by 3.
658 u"decimal " + DoubleToUnicodeString(cas.input) + ", e operand for compact vs. compact scaled",
660 compactScaledEOperand);
664 #endif /* #if !UCONFIG_NO_FORMATTING */