1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2017 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 package ohos.global.icu.dev.test.number; 5 6 import java.math.BigDecimal; 7 import java.math.BigInteger; 8 import java.math.MathContext; 9 import java.math.RoundingMode; 10 import java.text.ParseException; 11 import java.util.ArrayList; 12 import java.util.List; 13 14 import org.junit.Ignore; 15 import org.junit.Test; 16 import org.junit.runner.RunWith; 17 import org.junit.runners.JUnit4; 18 19 import ohos.global.icu.dev.impl.number.DecimalQuantity_64BitBCD; 20 import ohos.global.icu.dev.impl.number.DecimalQuantity_ByteArrayBCD; 21 import ohos.global.icu.dev.impl.number.DecimalQuantity_SimpleStorage; 22 import ohos.global.icu.dev.test.TestFmwk; 23 import ohos.global.icu.impl.FormattedStringBuilder; 24 import ohos.global.icu.impl.number.DecimalFormatProperties; 25 import ohos.global.icu.impl.number.DecimalQuantity; 26 import ohos.global.icu.impl.number.DecimalQuantity_DualStorageBCD; 27 import ohos.global.icu.impl.number.RoundingUtils; 28 import ohos.global.icu.number.FormattedNumber; 29 import ohos.global.icu.number.LocalizedNumberFormatter; 30 import ohos.global.icu.number.Notation; 31 import ohos.global.icu.number.NumberFormatter; 32 import ohos.global.icu.number.Precision; 33 import ohos.global.icu.number.Scale; 34 import ohos.global.icu.text.CompactDecimalFormat.CompactStyle; 35 import ohos.global.icu.text.DecimalFormatSymbols; 36 import ohos.global.icu.text.PluralRules.Operand; 37 import ohos.global.icu.util.ULocale; 38 39 40 41 @RunWith(JUnit4.class) 42 public class DecimalQuantityTest extends TestFmwk { 43 44 @Ignore 45 @Test testBehavior()46 public void testBehavior() throws ParseException { 47 48 // Make a list of several formatters to test the behavior of DecimalQuantity. 49 List<LocalizedNumberFormatter> formats = new ArrayList<>(); 50 51 DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(ULocale.ENGLISH); 52 53 DecimalFormatProperties properties = new DecimalFormatProperties(); 54 formats.add( 55 NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH)); 56 57 properties = new DecimalFormatProperties().setMinimumSignificantDigits(3) 58 .setMaximumSignificantDigits(3).setCompactStyle(CompactStyle.LONG); 59 formats.add( 60 NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH)); 61 62 properties = new DecimalFormatProperties().setMinimumExponentDigits(1).setMaximumIntegerDigits(3) 63 .setMaximumFractionDigits(1); 64 formats.add( 65 NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH)); 66 67 properties = new DecimalFormatProperties().setRoundingIncrement(new BigDecimal("0.5")); 68 formats.add( 69 NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH)); 70 71 String[] cases = { 72 "1.0", 73 "2.01", 74 "1234.56", 75 "3000.0", 76 "0.00026418", 77 "0.01789261", 78 "468160.0", 79 "999000.0", 80 "999900.0", 81 "999990.0", 82 "0.0", 83 "12345678901.0", 84 "-5193.48", }; 85 86 String[] hardCases = { 87 "9999999999999900.0", 88 "789000000000000000000000.0", 89 "789123123567853156372158.0", 90 "987654321987654321987654321987654321987654311987654321.0", }; 91 92 String[] doubleCases = { 93 "512.0000000000017", 94 "4095.9999999999977", 95 "4095.999999999998", 96 "4095.9999999999986", 97 "4095.999999999999", 98 "4095.9999999999995", 99 "4096.000000000001", 100 "4096.000000000002", 101 "4096.000000000003", 102 "4096.000000000004", 103 "4096.000000000005", 104 "4096.0000000000055", 105 "4096.000000000006", 106 "4096.000000000007", }; 107 108 int i = 0; 109 for (String str : cases) { 110 testDecimalQuantity(i++, str, formats, 0); 111 } 112 113 i = 0; 114 for (String str : hardCases) { 115 testDecimalQuantity(i++, str, formats, 1); 116 } 117 118 i = 0; 119 for (String str : doubleCases) { 120 testDecimalQuantity(i++, str, formats, 2); 121 } 122 } 123 testDecimalQuantity( int t, String str, List<LocalizedNumberFormatter> formats, int mode)124 static void testDecimalQuantity( 125 int t, 126 String str, 127 List<LocalizedNumberFormatter> formats, 128 int mode) { 129 if (mode == 2) { 130 assertEquals("Double is not valid", Double.toString(Double.parseDouble(str)), str); 131 } 132 133 List<DecimalQuantity> qs = new ArrayList<>(); 134 BigDecimal d = new BigDecimal(str); 135 qs.add(new DecimalQuantity_SimpleStorage(d)); 136 if (mode == 0) 137 qs.add(new DecimalQuantity_64BitBCD(d)); 138 qs.add(new DecimalQuantity_ByteArrayBCD(d)); 139 qs.add(new DecimalQuantity_DualStorageBCD(d)); 140 141 if (new BigDecimal(Double.toString(d.doubleValue())).compareTo(d) == 0) { 142 double dv = d.doubleValue(); 143 qs.add(new DecimalQuantity_SimpleStorage(dv)); 144 if (mode == 0) 145 qs.add(new DecimalQuantity_64BitBCD(dv)); 146 qs.add(new DecimalQuantity_ByteArrayBCD(dv)); 147 qs.add(new DecimalQuantity_DualStorageBCD(dv)); 148 } 149 150 if (new BigDecimal(Long.toString(d.longValue())).compareTo(d) == 0) { 151 double lv = d.longValue(); 152 qs.add(new DecimalQuantity_SimpleStorage(lv)); 153 if (mode == 0) 154 qs.add(new DecimalQuantity_64BitBCD(lv)); 155 qs.add(new DecimalQuantity_ByteArrayBCD(lv)); 156 qs.add(new DecimalQuantity_DualStorageBCD(lv)); 157 } 158 159 testDecimalQuantityExpectedOutput(qs.get(0), str); 160 161 if (qs.size() == 1) { 162 return; 163 } 164 165 for (int i = 1; i < qs.size(); i++) { 166 DecimalQuantity q0 = qs.get(0); 167 DecimalQuantity q1 = qs.get(i); 168 testDecimalQuantityExpectedOutput(q1, str); 169 testDecimalQuantityRounding(q0, q1); 170 testDecimalQuantityRoundingInterval(q0, q1); 171 testDecimalQuantityMath(q0, q1); 172 testDecimalQuantityWithFormats(q0, q1, formats); 173 } 174 } 175 testDecimalQuantityExpectedOutput(DecimalQuantity rq, String expected)176 private static void testDecimalQuantityExpectedOutput(DecimalQuantity rq, String expected) { 177 DecimalQuantity q0 = rq.createCopy(); 178 // Force an accurate double 179 q0.roundToInfinity(); 180 q0.setMinInteger(1); 181 q0.setMinFraction(1); 182 String actual = q0.toPlainString(); 183 assertEquals("Unexpected output from simple string conversion (" + q0 + ")", expected, actual); 184 } 185 186 private static final MathContext MATH_CONTEXT_HALF_EVEN = new MathContext(0, RoundingMode.HALF_EVEN); 187 private static final MathContext MATH_CONTEXT_CEILING = new MathContext(0, RoundingMode.CEILING); 188 @SuppressWarnings("unused") 189 private static final MathContext MATH_CONTEXT_FLOOR = new MathContext(0, RoundingMode.FLOOR); 190 private static final MathContext MATH_CONTEXT_PRECISION = new MathContext(3, RoundingMode.HALF_UP); 191 testDecimalQuantityRounding(DecimalQuantity rq0, DecimalQuantity rq1)192 private static void testDecimalQuantityRounding(DecimalQuantity rq0, DecimalQuantity rq1) { 193 DecimalQuantity q0 = rq0.createCopy(); 194 DecimalQuantity q1 = rq1.createCopy(); 195 q0.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN); 196 q1.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN); 197 testDecimalQuantityBehavior(q0, q1); 198 199 q0 = rq0.createCopy(); 200 q1 = rq1.createCopy(); 201 q0.roundToMagnitude(-1, MATH_CONTEXT_CEILING); 202 q1.roundToMagnitude(-1, MATH_CONTEXT_CEILING); 203 testDecimalQuantityBehavior(q0, q1); 204 205 q0 = rq0.createCopy(); 206 q1 = rq1.createCopy(); 207 q0.roundToMagnitude(-1, MATH_CONTEXT_PRECISION); 208 q1.roundToMagnitude(-1, MATH_CONTEXT_PRECISION); 209 testDecimalQuantityBehavior(q0, q1); 210 } 211 testDecimalQuantityRoundingInterval(DecimalQuantity rq0, DecimalQuantity rq1)212 private static void testDecimalQuantityRoundingInterval(DecimalQuantity rq0, DecimalQuantity rq1) { 213 DecimalQuantity q0 = rq0.createCopy(); 214 DecimalQuantity q1 = rq1.createCopy(); 215 q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN); 216 q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN); 217 testDecimalQuantityBehavior(q0, q1); 218 219 q0 = rq0.createCopy(); 220 q1 = rq1.createCopy(); 221 q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING); 222 q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING); 223 testDecimalQuantityBehavior(q0, q1); 224 } 225 testDecimalQuantityMath(DecimalQuantity rq0, DecimalQuantity rq1)226 private static void testDecimalQuantityMath(DecimalQuantity rq0, DecimalQuantity rq1) { 227 DecimalQuantity q0 = rq0.createCopy(); 228 DecimalQuantity q1 = rq1.createCopy(); 229 q0.adjustMagnitude(-3); 230 q1.adjustMagnitude(-3); 231 testDecimalQuantityBehavior(q0, q1); 232 233 q0 = rq0.createCopy(); 234 q1 = rq1.createCopy(); 235 q0.multiplyBy(new BigDecimal("3.14159")); 236 q1.multiplyBy(new BigDecimal("3.14159")); 237 testDecimalQuantityBehavior(q0, q1); 238 } 239 testDecimalQuantityWithFormats( DecimalQuantity rq0, DecimalQuantity rq1, List<LocalizedNumberFormatter> formats)240 private static void testDecimalQuantityWithFormats( 241 DecimalQuantity rq0, 242 DecimalQuantity rq1, 243 List<LocalizedNumberFormatter> formats) { 244 for (LocalizedNumberFormatter format : formats) { 245 DecimalQuantity q0 = rq0.createCopy(); 246 DecimalQuantity q1 = rq1.createCopy(); 247 FormattedStringBuilder nsb1 = new FormattedStringBuilder(); 248 FormattedStringBuilder nsb2 = new FormattedStringBuilder(); 249 format.formatImpl(q0, nsb1); 250 format.formatImpl(q1, nsb2); 251 String s1 = nsb1.toString(); 252 String s2 = nsb2.toString(); 253 assertEquals("Different output from formatter (" + q0 + ", " + q1 + ")", s1, s2); 254 } 255 } 256 testDecimalQuantityBehavior(DecimalQuantity rq0, DecimalQuantity rq1)257 private static void testDecimalQuantityBehavior(DecimalQuantity rq0, DecimalQuantity rq1) { 258 DecimalQuantity q0 = rq0.createCopy(); 259 DecimalQuantity q1 = rq1.createCopy(); 260 261 assertEquals("Different sign (" + q0 + ", " + q1 + ")", q0.isNegative(), q1.isNegative()); 262 263 assertEquals("Different fingerprint (" + q0 + ", " + q1 + ")", 264 q0.getPositionFingerprint(), 265 q1.getPositionFingerprint()); 266 267 assertDoubleEquals("Different double values (" + q0 + ", " + q1 + ")", 268 q0.toDouble(), 269 q1.toDouble()); 270 271 assertBigDecimalEquals("Different BigDecimal values (" + q0 + ", " + q1 + ")", 272 q0.toBigDecimal(), 273 q1.toBigDecimal()); 274 275 q0.roundToInfinity(); 276 q1.roundToInfinity(); 277 278 assertEquals("Different lower display magnitude", 279 q0.getLowerDisplayMagnitude(), 280 q1.getLowerDisplayMagnitude()); 281 assertEquals("Different upper display magnitude", 282 q0.getUpperDisplayMagnitude(), 283 q1.getUpperDisplayMagnitude()); 284 285 for (int m = q0.getUpperDisplayMagnitude(); m >= q0.getLowerDisplayMagnitude(); m--) { 286 assertEquals("Different digit at magnitude " + m + " (" + q0 + ", " + q1 + ")", 287 q0.getDigit(m), 288 q1.getDigit(m)); 289 } 290 291 if (rq0 instanceof DecimalQuantity_DualStorageBCD) { 292 String message = ((DecimalQuantity_DualStorageBCD) rq0).checkHealth(); 293 if (message != null) 294 errln(message); 295 } 296 if (rq1 instanceof DecimalQuantity_DualStorageBCD) { 297 String message = ((DecimalQuantity_DualStorageBCD) rq1).checkHealth(); 298 if (message != null) 299 errln(message); 300 } 301 } 302 303 @Test testSwitchStorage()304 public void testSwitchStorage() { 305 DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(); 306 307 fq.setToLong(1234123412341234L); 308 assertFalse("Should not be using byte array", fq.isUsingBytes()); 309 assertEquals("Failed on initialize", "1.234123412341234E+15", fq.toScientificString()); 310 assertNull("Failed health check", fq.checkHealth()); 311 // Long -> Bytes 312 fq.appendDigit((byte) 5, 0, true); 313 assertTrue("Should be using byte array", fq.isUsingBytes()); 314 assertEquals("Failed on multiply", "1.2341234123412345E+16", fq.toScientificString()); 315 assertNull("Failed health check", fq.checkHealth()); 316 // Bytes -> Long 317 fq.roundToMagnitude(5, MATH_CONTEXT_HALF_EVEN); 318 assertFalse("Should not be using byte array", fq.isUsingBytes()); 319 assertEquals("Failed on round", "1.23412341234E+16", fq.toScientificString()); 320 assertNull("Failed health check", fq.checkHealth()); 321 // Bytes with popFromLeft 322 fq.setToBigDecimal(new BigDecimal("999999999999999999")); 323 assertToStringAndHealth(fq, "<DecimalQuantity 0:0 bytes 999999999999999999E0>"); 324 fq.applyMaxInteger(17); 325 assertToStringAndHealth(fq, "<DecimalQuantity 0:0 bytes 99999999999999999E0>"); 326 fq.applyMaxInteger(16); 327 assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 9999999999999999E0>"); 328 fq.applyMaxInteger(15); 329 assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 999999999999999E0>"); 330 } 331 332 @Test testAppend()333 public void testAppend() { 334 DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(); 335 fq.appendDigit((byte) 1, 0, true); 336 assertEquals("Failed on append", "1E+0", fq.toScientificString()); 337 assertNull("Failed health check", fq.checkHealth()); 338 fq.appendDigit((byte) 2, 0, true); 339 assertEquals("Failed on append", "1.2E+1", fq.toScientificString()); 340 assertNull("Failed health check", fq.checkHealth()); 341 fq.appendDigit((byte) 3, 1, true); 342 assertEquals("Failed on append", "1.203E+3", fq.toScientificString()); 343 assertNull("Failed health check", fq.checkHealth()); 344 fq.appendDigit((byte) 0, 1, true); 345 assertEquals("Failed on append", "1.203E+5", fq.toScientificString()); 346 assertNull("Failed health check", fq.checkHealth()); 347 fq.appendDigit((byte) 4, 0, true); 348 assertEquals("Failed on append", "1.203004E+6", fq.toScientificString()); 349 assertNull("Failed health check", fq.checkHealth()); 350 fq.appendDigit((byte) 0, 0, true); 351 assertEquals("Failed on append", "1.203004E+7", fq.toScientificString()); 352 assertNull("Failed health check", fq.checkHealth()); 353 fq.appendDigit((byte) 5, 0, false); 354 assertEquals("Failed on append", "1.20300405E+7", fq.toScientificString()); 355 assertNull("Failed health check", fq.checkHealth()); 356 fq.appendDigit((byte) 6, 0, false); 357 assertEquals("Failed on append", "1.203004056E+7", fq.toScientificString()); 358 assertNull("Failed health check", fq.checkHealth()); 359 fq.appendDigit((byte) 7, 3, false); 360 assertEquals("Failed on append", "1.2030040560007E+7", fq.toScientificString()); 361 assertNull("Failed health check", fq.checkHealth()); 362 StringBuilder baseExpected = new StringBuilder("1.2030040560007"); 363 for (int i = 0; i < 10; i++) { 364 fq.appendDigit((byte) 8, 0, false); 365 baseExpected.append('8'); 366 StringBuilder expected = new StringBuilder(baseExpected); 367 expected.append("E+7"); 368 assertEquals("Failed on append", expected.toString(), fq.toScientificString()); 369 assertNull("Failed health check", fq.checkHealth()); 370 } 371 fq.appendDigit((byte) 9, 2, false); 372 baseExpected.append("009"); 373 StringBuilder expected = new StringBuilder(baseExpected); 374 expected.append("E+7"); 375 assertEquals("Failed on append", expected.toString(), fq.toScientificString()); 376 assertNull("Failed health check", fq.checkHealth()); 377 } 378 379 @Test testUseApproximateDoubleWhenAble()380 public void testUseApproximateDoubleWhenAble() { 381 Object[][] cases = { 382 { 1.2345678, 1, MATH_CONTEXT_HALF_EVEN, false }, 383 { 1.2345678, 7, MATH_CONTEXT_HALF_EVEN, false }, 384 { 1.2345678, 12, MATH_CONTEXT_HALF_EVEN, false }, 385 { 1.2345678, 13, MATH_CONTEXT_HALF_EVEN, true }, 386 { 1.235, 1, MATH_CONTEXT_HALF_EVEN, false }, 387 { 1.235, 2, MATH_CONTEXT_HALF_EVEN, true }, 388 { 1.235, 3, MATH_CONTEXT_HALF_EVEN, false }, 389 { 1.000000000000001, 0, MATH_CONTEXT_HALF_EVEN, false }, 390 { 1.000000000000001, 0, MATH_CONTEXT_CEILING, true }, 391 { 1.235, 1, MATH_CONTEXT_CEILING, false }, 392 { 1.235, 2, MATH_CONTEXT_CEILING, false }, 393 { 1.235, 3, MATH_CONTEXT_CEILING, true } }; 394 395 for (Object[] cas : cases) { 396 double d = (Double) cas[0]; 397 int maxFrac = (Integer) cas[1]; 398 MathContext mc = (MathContext) cas[2]; 399 boolean usesExact = (Boolean) cas[3]; 400 401 DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(d); 402 assertTrue("Should be using approximate double", !fq.explicitExactDouble); 403 fq.roundToMagnitude(-maxFrac, mc); 404 assertEquals( 405 "Using approximate double after rounding: " + d + " maxFrac=" + maxFrac + " " + mc, 406 usesExact, 407 fq.explicitExactDouble); 408 } 409 } 410 411 @Test testDecimalQuantityBehaviorStandalone()412 public void testDecimalQuantityBehaviorStandalone() { 413 DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(); 414 assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 0E0>"); 415 fq.setToInt(51423); 416 assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 51423E0>"); 417 fq.adjustMagnitude(-3); 418 assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 51423E-3>"); 419 fq.setToLong(90909090909000L); 420 assertToStringAndHealth(fq, "<DecimalQuantity 0:0 long 90909090909E3>"); 421 fq.setMinInteger(2); 422 fq.applyMaxInteger(5); 423 assertToStringAndHealth(fq, "<DecimalQuantity 2:0 long 9E3>"); 424 fq.setMinFraction(3); 425 assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 9E3>"); 426 fq.setToDouble(987.654321); 427 assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 987654321E-6>"); 428 fq.roundToInfinity(); 429 assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 987654321E-6>"); 430 fq.roundToIncrement(new BigDecimal("0.005"), MATH_CONTEXT_HALF_EVEN); 431 assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 987655E-3>"); 432 fq.roundToMagnitude(-2, MATH_CONTEXT_HALF_EVEN); 433 assertToStringAndHealth(fq, "<DecimalQuantity 2:-3 long 98766E-2>"); 434 } 435 436 @Test testFitsInLong()437 public void testFitsInLong() { 438 DecimalQuantity_DualStorageBCD quantity = new DecimalQuantity_DualStorageBCD(); 439 quantity.setToInt(0); 440 assertTrue("Zero should fit", quantity.fitsInLong()); 441 quantity.setToInt(42); 442 assertTrue("Small int should fit", quantity.fitsInLong()); 443 quantity.setToDouble(0.1); 444 assertFalse("Fraction should not fit", quantity.fitsInLong()); 445 quantity.setToDouble(42.1); 446 assertFalse("Fraction should not fit", quantity.fitsInLong()); 447 quantity.setToLong(1000000); 448 assertTrue("Large low-precision int should fit", quantity.fitsInLong()); 449 quantity.setToLong(1000000000000000000L); 450 assertTrue("10^19 should fit", quantity.fitsInLong()); 451 quantity.setToLong(1234567890123456789L); 452 assertTrue("A number between 10^19 and max long should fit", quantity.fitsInLong()); 453 quantity.setToLong(1234567890000000000L); 454 assertTrue("A number with trailing zeros less than max long should fit", quantity.fitsInLong()); 455 quantity.setToLong(9223372026854775808L); 456 assertTrue("A number less than max long but with similar digits should fit", 457 quantity.fitsInLong()); 458 quantity.setToLong(9223372036854775806L); 459 assertTrue("One less than max long should fit", quantity.fitsInLong()); 460 quantity.setToLong(9223372036854775807L); 461 assertTrue("Max long should fit", quantity.fitsInLong()); 462 quantity.setToBigInteger(new BigInteger("9223372036854775808")); 463 assertFalse("One greater than max long long should not fit", quantity.fitsInLong()); 464 quantity.setToBigInteger(new BigInteger("9223372046854775806")); 465 assertFalse("A number between max long and 10^20 should not fit", quantity.fitsInLong()); 466 quantity.setToBigInteger(new BigInteger("9223372046800000000")); 467 assertFalse("A large 10^19 number with trailing zeros should not fit", quantity.fitsInLong()); 468 quantity.setToBigInteger(new BigInteger("10000000000000000000")); 469 assertFalse("10^20 should not fit", quantity.fitsInLong()); 470 } 471 472 @Test testHardDoubleConversion()473 public void testHardDoubleConversion() { 474 // This test is somewhat duplicated from previous tests, but it is needed 475 // for ICU4C compatibility. 476 Object[][] cases = { 477 { 512.0000000000017, "512.0000000000017" }, 478 { 4095.9999999999977, "4095.9999999999977" }, 479 { 4095.999999999998, "4095.999999999998" }, 480 { 4095.9999999999986, "4095.9999999999986" }, 481 { 4095.999999999999, "4095.999999999999" }, 482 { 4095.9999999999995, "4095.9999999999995" }, 483 { 4096.000000000001, "4096.000000000001" }, 484 { 4096.000000000002, "4096.000000000002" }, 485 { 4096.000000000003, "4096.000000000003" }, 486 { 4096.000000000004, "4096.000000000004" }, 487 { 4096.000000000005, "4096.000000000005" }, 488 { 4096.0000000000055, "4096.0000000000055" }, 489 { 4096.000000000006, "4096.000000000006" }, 490 { 4096.000000000007, "4096.000000000007" } }; 491 492 for (Object[] cas : cases) { 493 double input = (Double) cas[0]; 494 String expectedOutput = (String) cas[1]; 495 496 DecimalQuantity q = new DecimalQuantity_DualStorageBCD(input); 497 q.roundToInfinity(); 498 String actualOutput = q.toPlainString(); 499 assertEquals("", expectedOutput, actualOutput); 500 } 501 } 502 503 @Test testToDouble()504 public void testToDouble() { 505 Object[][] cases = new Object[][] { 506 { "0", 0.0 }, 507 { "514.23", 514.23 }, 508 { "-3.142E-271", -3.142e-271 } 509 }; 510 511 for (Object[] cas : cases) { 512 String input = (String) cas[0]; 513 double expected = (Double) cas[1]; 514 515 DecimalQuantity q = new DecimalQuantity_DualStorageBCD(); 516 q.setToBigDecimal(new BigDecimal(input)); 517 double actual = q.toDouble(); 518 assertEquals("Doubles should exactly equal", expected, actual); 519 } 520 } 521 522 @Test testMaxDigits()523 public void testMaxDigits() { 524 DecimalQuantity_DualStorageBCD dq = new DecimalQuantity_DualStorageBCD(876.543); 525 dq.roundToInfinity(); 526 dq.setMinInteger(0); 527 dq.applyMaxInteger(2); 528 dq.setMinFraction(0); 529 dq.roundToMagnitude(-2, RoundingUtils.mathContextUnlimited(RoundingMode.FLOOR)); 530 assertEquals("Should trim, toPlainString", "76.54", dq.toPlainString()); 531 assertEquals("Should trim, toScientificString", "7.654E+1", dq.toScientificString()); 532 assertEquals("Should trim, toLong", 76, dq.toLong(true)); 533 assertEquals("Should trim, toFractionLong", 54, dq.toFractionLong(false)); 534 assertEquals("Should trim, toDouble", 76.54, dq.toDouble()); 535 assertEquals("Should trim, toBigDecimal", new BigDecimal("76.54"), dq.toBigDecimal()); 536 } 537 538 @Test testNickelRounding()539 public void testNickelRounding() { 540 Object[][] cases = new Object[][] { 541 {1.000, -2, RoundingMode.HALF_EVEN, "1"}, 542 {1.001, -2, RoundingMode.HALF_EVEN, "1"}, 543 {1.010, -2, RoundingMode.HALF_EVEN, "1"}, 544 {1.020, -2, RoundingMode.HALF_EVEN, "1"}, 545 {1.024, -2, RoundingMode.HALF_EVEN, "1"}, 546 {1.025, -2, RoundingMode.HALF_EVEN, "1"}, 547 {1.025, -2, RoundingMode.HALF_DOWN, "1"}, 548 {1.025, -2, RoundingMode.HALF_UP, "1.05"}, 549 {1.026, -2, RoundingMode.HALF_EVEN, "1.05"}, 550 {1.030, -2, RoundingMode.HALF_EVEN, "1.05"}, 551 {1.040, -2, RoundingMode.HALF_EVEN, "1.05"}, 552 {1.050, -2, RoundingMode.HALF_EVEN, "1.05"}, 553 {1.060, -2, RoundingMode.HALF_EVEN, "1.05"}, 554 {1.070, -2, RoundingMode.HALF_EVEN, "1.05"}, 555 {1.074, -2, RoundingMode.HALF_EVEN, "1.05"}, 556 {1.075, -2, RoundingMode.HALF_DOWN, "1.05"}, 557 {1.075, -2, RoundingMode.HALF_UP, "1.1"}, 558 {1.075, -2, RoundingMode.HALF_EVEN, "1.1"}, 559 {1.076, -2, RoundingMode.HALF_EVEN, "1.1"}, 560 {1.080, -2, RoundingMode.HALF_EVEN, "1.1"}, 561 {1.090, -2, RoundingMode.HALF_EVEN, "1.1"}, 562 {1.099, -2, RoundingMode.HALF_EVEN, "1.1"}, 563 {1.999, -2, RoundingMode.HALF_EVEN, "2"}, 564 {2.25, -1, RoundingMode.HALF_EVEN, "2"}, 565 {2.25, -1, RoundingMode.HALF_UP, "2.5"}, 566 {2.75, -1, RoundingMode.HALF_DOWN, "2.5"}, 567 {2.75, -1, RoundingMode.HALF_EVEN, "3"}, 568 {3.00, -1, RoundingMode.CEILING, "3"}, 569 {3.25, -1, RoundingMode.CEILING, "3.5"}, 570 {3.50, -1, RoundingMode.CEILING, "3.5"}, 571 {3.75, -1, RoundingMode.CEILING, "4"}, 572 {4.00, -1, RoundingMode.FLOOR, "4"}, 573 {4.25, -1, RoundingMode.FLOOR, "4"}, 574 {4.50, -1, RoundingMode.FLOOR, "4.5"}, 575 {4.75, -1, RoundingMode.FLOOR, "4.5"}, 576 {5.00, -1, RoundingMode.UP, "5"}, 577 {5.25, -1, RoundingMode.UP, "5.5"}, 578 {5.50, -1, RoundingMode.UP, "5.5"}, 579 {5.75, -1, RoundingMode.UP, "6"}, 580 {6.00, -1, RoundingMode.DOWN, "6"}, 581 {6.25, -1, RoundingMode.DOWN, "6"}, 582 {6.50, -1, RoundingMode.DOWN, "6.5"}, 583 {6.75, -1, RoundingMode.DOWN, "6.5"}, 584 {7.00, -1, RoundingMode.UNNECESSARY, "7"}, 585 {7.50, -1, RoundingMode.UNNECESSARY, "7.5"}, 586 }; 587 for (Object[] cas : cases) { 588 double input = (Double) cas[0]; 589 int magnitude = (Integer) cas[1]; 590 RoundingMode roundingMode = (RoundingMode) cas[2]; 591 String expected = (String) cas[3]; 592 String message = input + " @ " + magnitude + " / " + roundingMode; 593 for (int i=0; i<2; i++) { 594 DecimalQuantity dq; 595 if (i == 0) { 596 dq = new DecimalQuantity_DualStorageBCD(input); 597 } else { 598 dq = new DecimalQuantity_SimpleStorage(input); 599 } 600 dq.roundToNickel(magnitude, RoundingUtils.mathContextUnlimited(roundingMode)); 601 String actual = dq.toPlainString(); 602 assertEquals(message, expected, actual); 603 } 604 } 605 try { 606 DecimalQuantity_DualStorageBCD dq = new DecimalQuantity_DualStorageBCD(7.1); 607 dq.roundToNickel(-1, RoundingUtils.mathContextUnlimited(RoundingMode.UNNECESSARY)); 608 fail("Expected ArithmeticException"); 609 } catch (ArithmeticException expected) { 610 // pass 611 } 612 } 613 614 @Test testCompactDecimalSuppressedExponent()615 public void testCompactDecimalSuppressedExponent() { 616 ULocale locale = new ULocale("fr-FR"); 617 618 Object[][] casesData = { 619 // unlocalized formatter skeleton, input, string output, long output, double output, BigDecimal output, plain string, suppressed exponent 620 {"", 123456789, "123 456 789", 123456789L, 123456789.0, new BigDecimal("123456789"), "123456789", 0}, 621 {"compact-long", 123456789, "123 millions", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000", 6}, 622 {"compact-short", 123456789, "123 M", 123000000L, 123000000.0, new BigDecimal("123000000"), "123000000", 6}, 623 {"scientific", 123456789, "1,234568E8", 123456800L, 123456800.0, new BigDecimal("123456800"), "123456800", 8}, 624 625 {"", 1234567, "1 234 567", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567", 0}, 626 {"compact-long", 1234567, "1,2 million", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000", 6}, 627 {"compact-short", 1234567, "1,2 M", 1200000L, 1200000.0, new BigDecimal("1200000"), "1200000", 6}, 628 {"scientific", 1234567, "1,234567E6", 1234567L, 1234567.0, new BigDecimal("1234567"), "1234567", 6}, 629 630 {"", 123456, "123 456", 123456L, 123456.0, new BigDecimal("123456"), "123456", 0}, 631 {"compact-long", 123456, "123 mille", 123000L, 123000.0, new BigDecimal("123000"), "123000", 3}, 632 {"compact-short", 123456, "123 k", 123000L, 123000.0, new BigDecimal("123000"), "123000", 3}, 633 {"scientific", 123456, "1,23456E5", 123456L, 123456.0, new BigDecimal("123456"), "123456", 5}, 634 635 {"", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0}, 636 {"compact-long", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0}, 637 {"compact-short", 123, "123", 123L, 123.0, new BigDecimal("123"), "123", 0}, 638 {"scientific", 123, "1,23E2", 123L, 123.0, new BigDecimal("123"), "123", 2}, 639 640 {"", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0}, 641 {"compact-long", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0}, 642 {"compact-short", 1.2, "1,2", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0}, 643 {"scientific", 1.2, "1,2E0", 1L, 1.2, new BigDecimal("1.2"), "1.2", 0}, 644 645 {"", 0.12, "0,12", 0L, 0.12, new BigDecimal("0.12"), "0.12", 0}, 646 {"compact-long", 0.12, "0,12", 0L, 0.12, new BigDecimal("0.12"), "0.12", 0}, 647 {"compact-short", 0.12, "0,12", 0L, 0.12, new BigDecimal("0.12"), "0.12", 0}, 648 {"scientific", 0.12, "1,2E-1", 0L, 0.12, new BigDecimal("0.12"), "0.12", -1}, 649 650 {"", 0.012, "0,012", 0L, 0.012, new BigDecimal("0.012"), "0.012", 0}, 651 {"compact-long", 0.012, "0,012", 0L, 0.012, new BigDecimal("0.012"), "0.012", 0}, 652 {"compact-short", 0.012, "0,012", 0L, 0.012, new BigDecimal("0.012"), "0.012", 0}, 653 {"scientific", 0.012, "1,2E-2", 0L, 0.012, new BigDecimal("0.012"), "0.012", -2}, 654 655 {"", 999.9, "999,9", 999L, 999.9, new BigDecimal("999.9"), "999.9", 0}, 656 {"compact-long", 999.9, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3}, 657 {"compact-short", 999.9, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3}, 658 {"scientific", 999.9, "9,999E2", 999L, 999.9, new BigDecimal("999.9"), "999.9", 2}, 659 660 {"", 1000.0, "1 000", 1000L, 1000.0, new BigDecimal("1000"), "1000", 0}, 661 {"compact-long", 1000.0, "1 millier", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3}, 662 {"compact-short", 1000.0, "1 k", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3}, 663 {"scientific", 1000.0, "1E3", 1000L, 1000.0, new BigDecimal("1000"), "1000", 3}, 664 }; 665 666 for (Object[] caseDatum : casesData) { 667 // test the helper methods used to compute plural operand values 668 669 String skeleton = (String) caseDatum[0]; 670 LocalizedNumberFormatter formatter = 671 NumberFormatter.forSkeleton(skeleton) 672 .locale(locale); 673 double input = ((Number) caseDatum[1]).doubleValue(); 674 String expectedString = (String) caseDatum[2]; 675 long expectedLong = (long) caseDatum[3]; 676 double expectedDouble = (double) caseDatum[4]; 677 BigDecimal expectedBigDecimal = (BigDecimal) caseDatum[5]; 678 String expectedPlainString = (String) caseDatum[6]; 679 int expectedSuppressedExponent = (int) caseDatum[7]; 680 681 FormattedNumber fn = formatter.format(input); 682 DecimalQuantity_DualStorageBCD dq = (DecimalQuantity_DualStorageBCD) 683 fn.getFixedDecimal(); 684 String actualString = fn.toString(); 685 long actualLong = dq.toLong(true); 686 double actualDouble = dq.toDouble(); 687 BigDecimal actualBigDecimal = dq.toBigDecimal(); 688 String actualPlainString = dq.toPlainString(); 689 int actualSuppressedExponent = dq.getExponent(); 690 691 assertEquals( 692 String.format("formatted number %s toString: %f", skeleton, input), 693 expectedString, 694 actualString); 695 assertEquals( 696 String.format("compact decimal %s toLong: %f", skeleton, input), 697 expectedLong, 698 actualLong); 699 assertDoubleEquals( 700 String.format("compact decimal %s toDouble: %f", skeleton, input), 701 expectedDouble, 702 actualDouble); 703 assertBigDecimalEquals( 704 String.format("compact decimal %s toBigDecimal: %f", skeleton, input), 705 expectedBigDecimal, 706 actualBigDecimal); 707 assertEquals( 708 String.format("formatted number %s toPlainString: %f", skeleton, input), 709 expectedPlainString, 710 actualPlainString); 711 assertEquals( 712 String.format("compact decimal %s suppressed exponent: %f", skeleton, input), 713 expectedSuppressedExponent, 714 actualSuppressedExponent); 715 716 // test the actual computed values of the plural operands 717 718 double expectedNOperand = expectedDouble; 719 double expectedIOperand = expectedLong; 720 double expectedEOperand = expectedSuppressedExponent; 721 double actualNOperand = dq.getPluralOperand(Operand.n); 722 double actualIOperand = dq.getPluralOperand(Operand.i); 723 double actualEOperand = dq.getPluralOperand(Operand.e); 724 725 assertEquals( 726 String.format("formatted number %s toString: %s", skeleton, input), 727 expectedString, 728 actualString); 729 assertDoubleEquals( 730 String.format("compact decimal %s n operand: %f", skeleton, input), 731 expectedNOperand, 732 actualNOperand); 733 assertDoubleEquals( 734 String.format("compact decimal %s i operand: %f", skeleton, input), 735 expectedIOperand, 736 actualIOperand); 737 assertDoubleEquals( 738 String.format("compact decimal %s e operand: %f", skeleton, input), 739 expectedEOperand, 740 actualEOperand); 741 } 742 } 743 744 745 @Test testCompactNotationFractionPluralOperands()746 public void testCompactNotationFractionPluralOperands() { 747 ULocale locale = new ULocale("fr-FR"); 748 LocalizedNumberFormatter formatter = 749 NumberFormatter.withLocale(locale) 750 .notation(Notation.compactLong()) 751 .precision(Precision.fixedFraction(5)) 752 .scale(Scale.powerOfTen(-1)); 753 double formatterInput = 12345; 754 double inputVal = 1234.5; 755 FormattedNumber fn = formatter.format(formatterInput); 756 DecimalQuantity_DualStorageBCD dq = (DecimalQuantity_DualStorageBCD) 757 fn.getFixedDecimal(); 758 759 double expectedNOperand = 1234.5; 760 double expectedIOperand = 1234; 761 double expectedFOperand = 50; 762 double expectedTOperand = 5; 763 double expectedVOperand = 2; 764 double expectedWOperand = 1; 765 double expectedEOperand = 3; 766 String expectedString = "1,23450 millier"; 767 double actualNOperand = dq.getPluralOperand(Operand.n); 768 double actualIOperand = dq.getPluralOperand(Operand.i); 769 double actualFOperand = dq.getPluralOperand(Operand.f); 770 double actualTOperand = dq.getPluralOperand(Operand.t); 771 double actualVOperand = dq.getPluralOperand(Operand.v); 772 double actualWOperand = dq.getPluralOperand(Operand.w); 773 double actualEOperand = dq.getPluralOperand(Operand.e); 774 String actualString = fn.toString(); 775 776 assertDoubleEquals( 777 String.format("compact decimal fraction n operand: %f", inputVal), 778 expectedNOperand, 779 actualNOperand); 780 assertDoubleEquals( 781 String.format("compact decimal fraction i operand: %f", inputVal), 782 expectedIOperand, 783 actualIOperand); 784 assertDoubleEquals( 785 String.format("compact decimal fraction f operand: %f", inputVal), 786 expectedFOperand, 787 actualFOperand); 788 assertDoubleEquals( 789 String.format("compact decimal fraction t operand: %f", inputVal), 790 expectedTOperand, 791 actualTOperand); 792 assertDoubleEquals( 793 String.format("compact decimal fraction v operand: %f", inputVal), 794 expectedVOperand, 795 actualVOperand); 796 assertDoubleEquals( 797 String.format("compact decimal fraction w operand: %f", inputVal), 798 expectedWOperand, 799 actualWOperand); 800 assertDoubleEquals( 801 String.format("compact decimal fraction e operand: %f", inputVal), 802 expectedEOperand, 803 actualEOperand); 804 assertEquals( 805 String.format("compact decimal fraction toString: %f", inputVal), 806 expectedString, 807 actualString); 808 } 809 810 @Test testSuppressedExponentUnchangedByInitialScaling()811 public void testSuppressedExponentUnchangedByInitialScaling() { 812 ULocale locale = new ULocale("fr-FR"); 813 LocalizedNumberFormatter withLocale = NumberFormatter.withLocale(locale); 814 LocalizedNumberFormatter compactLong = 815 withLocale.notation(Notation.compactLong()); 816 LocalizedNumberFormatter compactScaled = 817 compactLong.scale(Scale.powerOfTen(3)); 818 819 Object[][] casesData = { 820 // input, compact long string output, 821 // compact n operand, compact i operand, compact e operand 822 {123456789, "123 millions", 123000000.0, 123000000.0, 6.0}, 823 {1234567, "1,2 million", 1200000.0, 1200000.0, 6.0}, 824 {123456, "123 mille", 123000.0, 123000.0, 3.0}, 825 {123, "123", 123.0, 123.0, 0.0}, 826 }; 827 828 for (Object[] caseDatum : casesData) { 829 int input = (int) caseDatum[0]; 830 String expectedString = (String) caseDatum[1]; 831 double expectedNOperand = (double) caseDatum[2]; 832 double expectedIOperand = (double) caseDatum[3]; 833 double expectedEOperand = (double) caseDatum[4]; 834 835 FormattedNumber fnCompactScaled = compactScaled.format(input); 836 DecimalQuantity_DualStorageBCD dqCompactScaled = 837 (DecimalQuantity_DualStorageBCD) fnCompactScaled.getFixedDecimal(); 838 double compactScaledEOperand = dqCompactScaled.getPluralOperand(Operand.e); 839 840 FormattedNumber fnCompact = compactLong.format(input); 841 DecimalQuantity_DualStorageBCD dqCompact = 842 (DecimalQuantity_DualStorageBCD) fnCompact.getFixedDecimal(); 843 String actualString = fnCompact.toString(); 844 double compactNOperand = dqCompact.getPluralOperand(Operand.n); 845 double compactIOperand = dqCompact.getPluralOperand(Operand.i); 846 double compactEOperand = dqCompact.getPluralOperand(Operand.e); 847 assertEquals( 848 String.format("formatted number compactLong toString: %s", input), 849 expectedString, 850 actualString); 851 assertDoubleEquals( 852 String.format("compact decimal %d, n operand vs. expected", input), 853 expectedNOperand, 854 compactNOperand); 855 assertDoubleEquals( 856 String.format("compact decimal %d, i operand vs. expected", input), 857 expectedIOperand, 858 compactIOperand); 859 assertDoubleEquals( 860 String.format("compact decimal %d, e operand vs. expected", input), 861 expectedEOperand, 862 compactEOperand); 863 864 // By scaling by 10^3 in a locale that has words / compact notation 865 // based on powers of 10^3, we guarantee that the suppressed 866 // exponent will differ by 3. 867 assertDoubleEquals( 868 String.format("decimal %d, e operand for compact vs. compact scaled", input), 869 compactEOperand + 3, 870 compactScaledEOperand); 871 } 872 } 873 doubleEquals(double d1, double d2)874 static boolean doubleEquals(double d1, double d2) { 875 return (Math.abs(d1 - d2) < 1e-6) || (Math.abs((d1 - d2) / d1) < 1e-6); 876 } 877 assertDoubleEquals(String message, double d1, double d2)878 static void assertDoubleEquals(String message, double d1, double d2) { 879 boolean equal = doubleEquals(d1, d2); 880 handleAssert(equal, message, d1, d2, null, false); 881 } 882 assertBigDecimalEquals(String message, String d1, BigDecimal d2)883 static void assertBigDecimalEquals(String message, String d1, BigDecimal d2) { 884 assertBigDecimalEquals(message, new BigDecimal(d1), d2); 885 } 886 assertBigDecimalEquals(String message, BigDecimal d1, BigDecimal d2)887 static void assertBigDecimalEquals(String message, BigDecimal d1, BigDecimal d2) { 888 boolean equal = d1.compareTo(d2) == 0; 889 handleAssert(equal, message, d1, d2, null, false); 890 } 891 assertToStringAndHealth(DecimalQuantity_DualStorageBCD fq, String expected)892 static void assertToStringAndHealth(DecimalQuantity_DualStorageBCD fq, String expected) { 893 String actual = fq.toString(); 894 assertEquals("DecimalQuantity toString", expected, actual); 895 String health = fq.checkHealth(); 896 assertNull("DecimalQuantity health", health); 897 } 898 } 899