]>
Commit | Line | Data |
---|---|---|
0f5d89e8 A |
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 <cstdlib> | |
9 | #include <cmath> | |
10 | #include <limits> | |
11 | #include <stdlib.h> | |
12 | ||
13 | #include "unicode/plurrule.h" | |
14 | #include "cmemory.h" | |
15 | #include "number_decnum.h" | |
16 | #include "putilimp.h" | |
17 | #include "number_decimalquantity.h" | |
18 | #include "number_roundingutils.h" | |
19 | #include "double-conversion.h" | |
20 | #include "charstr.h" | |
21 | #include "number_utils.h" | |
22 | #include "uassert.h" | |
23 | ||
24 | using namespace icu; | |
25 | using namespace icu::number; | |
26 | using namespace icu::number::impl; | |
27 | ||
28 | using icu::double_conversion::DoubleToStringConverter; | |
29 | using icu::double_conversion::StringToDoubleConverter; | |
30 | ||
31 | namespace { | |
32 | ||
33 | int8_t NEGATIVE_FLAG = 1; | |
34 | int8_t INFINITY_FLAG = 2; | |
35 | int8_t NAN_FLAG = 4; | |
36 | ||
37 | /** Helper function for safe subtraction (no overflow). */ | |
38 | inline int32_t safeSubtract(int32_t a, int32_t b) { | |
39 | // Note: In C++, signed integer subtraction is undefined behavior. | |
40 | int32_t diff = static_cast<int32_t>(static_cast<uint32_t>(a) - static_cast<uint32_t>(b)); | |
41 | if (b < 0 && diff < a) { return INT32_MAX; } | |
42 | if (b > 0 && diff > a) { return INT32_MIN; } | |
43 | return diff; | |
44 | } | |
45 | ||
46 | static double DOUBLE_MULTIPLIERS[] = { | |
47 | 1e0, | |
48 | 1e1, | |
49 | 1e2, | |
50 | 1e3, | |
51 | 1e4, | |
52 | 1e5, | |
53 | 1e6, | |
54 | 1e7, | |
55 | 1e8, | |
56 | 1e9, | |
57 | 1e10, | |
58 | 1e11, | |
59 | 1e12, | |
60 | 1e13, | |
61 | 1e14, | |
62 | 1e15, | |
63 | 1e16, | |
64 | 1e17, | |
65 | 1e18, | |
66 | 1e19, | |
67 | 1e20, | |
68 | 1e21}; | |
69 | ||
70 | } // namespace | |
71 | ||
72 | icu::IFixedDecimal::~IFixedDecimal() = default; | |
73 | ||
74 | DecimalQuantity::DecimalQuantity() { | |
75 | setBcdToZero(); | |
76 | flags = 0; | |
77 | } | |
78 | ||
79 | DecimalQuantity::~DecimalQuantity() { | |
80 | if (usingBytes) { | |
81 | uprv_free(fBCD.bcdBytes.ptr); | |
82 | fBCD.bcdBytes.ptr = nullptr; | |
83 | usingBytes = false; | |
84 | } | |
85 | } | |
86 | ||
87 | DecimalQuantity::DecimalQuantity(const DecimalQuantity &other) { | |
88 | *this = other; | |
89 | } | |
90 | ||
91 | DecimalQuantity::DecimalQuantity(DecimalQuantity&& src) U_NOEXCEPT { | |
92 | *this = std::move(src); | |
93 | } | |
94 | ||
95 | DecimalQuantity &DecimalQuantity::operator=(const DecimalQuantity &other) { | |
96 | if (this == &other) { | |
97 | return *this; | |
98 | } | |
99 | copyBcdFrom(other); | |
100 | copyFieldsFrom(other); | |
101 | return *this; | |
102 | } | |
103 | ||
104 | DecimalQuantity& DecimalQuantity::operator=(DecimalQuantity&& src) U_NOEXCEPT { | |
105 | if (this == &src) { | |
106 | return *this; | |
107 | } | |
108 | moveBcdFrom(src); | |
109 | copyFieldsFrom(src); | |
110 | return *this; | |
111 | } | |
112 | ||
113 | void DecimalQuantity::copyFieldsFrom(const DecimalQuantity& other) { | |
114 | bogus = other.bogus; | |
0f5d89e8 A |
115 | lReqPos = other.lReqPos; |
116 | rReqPos = other.rReqPos; | |
0f5d89e8 A |
117 | scale = other.scale; |
118 | precision = other.precision; | |
119 | flags = other.flags; | |
120 | origDouble = other.origDouble; | |
121 | origDelta = other.origDelta; | |
122 | isApproximate = other.isApproximate; | |
123 | } | |
124 | ||
125 | void DecimalQuantity::clear() { | |
0f5d89e8 A |
126 | lReqPos = 0; |
127 | rReqPos = 0; | |
0f5d89e8 A |
128 | flags = 0; |
129 | setBcdToZero(); // sets scale, precision, hasDouble, origDouble, origDelta, and BCD data | |
130 | } | |
131 | ||
3d1f044b | 132 | void DecimalQuantity::setMinInteger(int32_t minInt) { |
0f5d89e8 A |
133 | // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. |
134 | U_ASSERT(minInt >= 0); | |
0f5d89e8 A |
135 | |
136 | // Special behavior: do not set minInt to be less than what is already set. | |
137 | // This is so significant digits rounding can set the integer length. | |
138 | if (minInt < lReqPos) { | |
139 | minInt = lReqPos; | |
140 | } | |
141 | ||
142 | // Save values into internal state | |
0f5d89e8 A |
143 | lReqPos = minInt; |
144 | } | |
145 | ||
3d1f044b | 146 | void DecimalQuantity::setMinFraction(int32_t minFrac) { |
0f5d89e8 A |
147 | // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. |
148 | U_ASSERT(minFrac >= 0); | |
0f5d89e8 A |
149 | |
150 | // Save values into internal state | |
151 | // Negation is safe for minFrac/maxFrac because -Integer.MAX_VALUE > Integer.MIN_VALUE | |
152 | rReqPos = -minFrac; | |
3d1f044b A |
153 | } |
154 | ||
155 | void DecimalQuantity::applyMaxInteger(int32_t maxInt) { | |
156 | // Validation should happen outside of DecimalQuantity, e.g., in the Precision class. | |
157 | U_ASSERT(maxInt >= 0); | |
158 | ||
159 | if (precision == 0) { | |
160 | return; | |
161 | } | |
162 | ||
163 | if (maxInt <= scale) { | |
164 | setBcdToZero(); | |
165 | return; | |
166 | } | |
167 | ||
168 | int32_t magnitude = getMagnitude(); | |
169 | if (maxInt <= magnitude) { | |
170 | popFromLeft(magnitude - maxInt + 1); | |
171 | compact(); | |
172 | } | |
0f5d89e8 A |
173 | } |
174 | ||
175 | uint64_t DecimalQuantity::getPositionFingerprint() const { | |
176 | uint64_t fingerprint = 0; | |
0f5d89e8 A |
177 | fingerprint ^= (lReqPos << 16); |
178 | fingerprint ^= (static_cast<uint64_t>(rReqPos) << 32); | |
0f5d89e8 A |
179 | return fingerprint; |
180 | } | |
181 | ||
182 | void DecimalQuantity::roundToIncrement(double roundingIncrement, RoundingMode roundingMode, | |
3d1f044b A |
183 | UErrorCode& status) { |
184 | // Do not call this method with an increment having only a 1 or a 5 digit! | |
185 | // Use a more efficient call to either roundToMagnitude() or roundToNickel(). | |
186 | // Check a few popular rounding increments; a more thorough check is in Java. | |
187 | U_ASSERT(roundingIncrement != 0.01); | |
188 | U_ASSERT(roundingIncrement != 0.05); | |
189 | U_ASSERT(roundingIncrement != 0.1); | |
190 | U_ASSERT(roundingIncrement != 0.5); | |
191 | U_ASSERT(roundingIncrement != 1); | |
192 | U_ASSERT(roundingIncrement != 5); | |
193 | ||
194 | DecNum incrementDN; | |
195 | incrementDN.setTo(roundingIncrement, status); | |
196 | if (U_FAILURE(status)) { return; } | |
197 | ||
198 | // Divide this DecimalQuantity by the increment, round, then multiply back. | |
199 | divideBy(incrementDN, status); | |
200 | if (U_FAILURE(status)) { return; } | |
201 | roundToMagnitude(0, roundingMode, status); | |
202 | if (U_FAILURE(status)) { return; } | |
203 | multiplyBy(incrementDN, status); | |
204 | if (U_FAILURE(status)) { return; } | |
0f5d89e8 A |
205 | } |
206 | ||
207 | void DecimalQuantity::multiplyBy(const DecNum& multiplicand, UErrorCode& status) { | |
340931cb | 208 | if (isZeroish()) { |
0f5d89e8 A |
209 | return; |
210 | } | |
211 | // Convert to DecNum, multiply, and convert back. | |
212 | DecNum decnum; | |
213 | toDecNum(decnum, status); | |
214 | if (U_FAILURE(status)) { return; } | |
215 | decnum.multiplyBy(multiplicand, status); | |
216 | if (U_FAILURE(status)) { return; } | |
217 | setToDecNum(decnum, status); | |
218 | } | |
219 | ||
220 | void DecimalQuantity::divideBy(const DecNum& divisor, UErrorCode& status) { | |
340931cb | 221 | if (isZeroish()) { |
0f5d89e8 A |
222 | return; |
223 | } | |
224 | // Convert to DecNum, multiply, and convert back. | |
225 | DecNum decnum; | |
226 | toDecNum(decnum, status); | |
227 | if (U_FAILURE(status)) { return; } | |
228 | decnum.divideBy(divisor, status); | |
229 | if (U_FAILURE(status)) { return; } | |
230 | setToDecNum(decnum, status); | |
231 | } | |
232 | ||
233 | void DecimalQuantity::negate() { | |
234 | flags ^= NEGATIVE_FLAG; | |
235 | } | |
236 | ||
237 | int32_t DecimalQuantity::getMagnitude() const { | |
238 | U_ASSERT(precision != 0); | |
239 | return scale + precision - 1; | |
240 | } | |
241 | ||
242 | bool DecimalQuantity::adjustMagnitude(int32_t delta) { | |
243 | if (precision != 0) { | |
244 | // i.e., scale += delta; origDelta += delta | |
245 | bool overflow = uprv_add32_overflow(scale, delta, &scale); | |
246 | overflow = uprv_add32_overflow(origDelta, delta, &origDelta) || overflow; | |
247 | // Make sure that precision + scale won't overflow, either | |
248 | int32_t dummy; | |
249 | overflow = overflow || uprv_add32_overflow(scale, precision, &dummy); | |
250 | return overflow; | |
251 | } | |
252 | return false; | |
253 | } | |
254 | ||
255 | double DecimalQuantity::getPluralOperand(PluralOperand operand) const { | |
256 | // If this assertion fails, you need to call roundToInfinity() or some other rounding method. | |
257 | // See the comment at the top of this file explaining the "isApproximate" field. | |
258 | U_ASSERT(!isApproximate); | |
259 | ||
260 | switch (operand) { | |
261 | case PLURAL_OPERAND_I: | |
262 | // Invert the negative sign if necessary | |
263 | return static_cast<double>(isNegative() ? -toLong(true) : toLong(true)); | |
264 | case PLURAL_OPERAND_F: | |
265 | return static_cast<double>(toFractionLong(true)); | |
266 | case PLURAL_OPERAND_T: | |
267 | return static_cast<double>(toFractionLong(false)); | |
268 | case PLURAL_OPERAND_V: | |
269 | return fractionCount(); | |
270 | case PLURAL_OPERAND_W: | |
271 | return fractionCountWithoutTrailingZeros(); | |
272 | default: | |
273 | return std::abs(toDouble()); | |
274 | } | |
275 | } | |
276 | ||
277 | bool DecimalQuantity::hasIntegerValue() const { | |
278 | return scale >= 0; | |
279 | } | |
280 | ||
281 | int32_t DecimalQuantity::getUpperDisplayMagnitude() const { | |
282 | // If this assertion fails, you need to call roundToInfinity() or some other rounding method. | |
283 | // See the comment in the header file explaining the "isApproximate" field. | |
284 | U_ASSERT(!isApproximate); | |
285 | ||
286 | int32_t magnitude = scale + precision; | |
3d1f044b | 287 | int32_t result = (lReqPos > magnitude) ? lReqPos : magnitude; |
0f5d89e8 A |
288 | return result - 1; |
289 | } | |
290 | ||
291 | int32_t DecimalQuantity::getLowerDisplayMagnitude() const { | |
292 | // If this assertion fails, you need to call roundToInfinity() or some other rounding method. | |
293 | // See the comment in the header file explaining the "isApproximate" field. | |
294 | U_ASSERT(!isApproximate); | |
295 | ||
296 | int32_t magnitude = scale; | |
3d1f044b | 297 | int32_t result = (rReqPos < magnitude) ? rReqPos : magnitude; |
0f5d89e8 A |
298 | return result; |
299 | } | |
300 | ||
301 | int8_t DecimalQuantity::getDigit(int32_t magnitude) const { | |
302 | // If this assertion fails, you need to call roundToInfinity() or some other rounding method. | |
303 | // See the comment at the top of this file explaining the "isApproximate" field. | |
304 | U_ASSERT(!isApproximate); | |
305 | ||
306 | return getDigitPos(magnitude - scale); | |
307 | } | |
308 | ||
309 | int32_t DecimalQuantity::fractionCount() const { | |
310 | return -getLowerDisplayMagnitude(); | |
311 | } | |
312 | ||
313 | int32_t DecimalQuantity::fractionCountWithoutTrailingZeros() const { | |
314 | return -scale > 0 ? -scale : 0; // max(-scale, 0) | |
315 | } | |
316 | ||
317 | bool DecimalQuantity::isNegative() const { | |
318 | return (flags & NEGATIVE_FLAG) != 0; | |
319 | } | |
320 | ||
340931cb A |
321 | Signum DecimalQuantity::signum() const { |
322 | if (isNegative()) { | |
323 | return SIGNUM_NEG; | |
324 | } else if (isZeroish() && !isInfinite()) { | |
325 | return SIGNUM_ZERO; | |
326 | } else { | |
327 | return SIGNUM_POS; | |
328 | } | |
0f5d89e8 A |
329 | } |
330 | ||
331 | bool DecimalQuantity::isInfinite() const { | |
332 | return (flags & INFINITY_FLAG) != 0; | |
333 | } | |
334 | ||
335 | bool DecimalQuantity::isNaN() const { | |
336 | return (flags & NAN_FLAG) != 0; | |
337 | } | |
338 | ||
340931cb | 339 | bool DecimalQuantity::isZeroish() const { |
0f5d89e8 A |
340 | return precision == 0; |
341 | } | |
342 | ||
343 | DecimalQuantity &DecimalQuantity::setToInt(int32_t n) { | |
344 | setBcdToZero(); | |
345 | flags = 0; | |
346 | if (n == INT32_MIN) { | |
347 | flags |= NEGATIVE_FLAG; | |
348 | // leave as INT32_MIN; handled below in _setToInt() | |
349 | } else if (n < 0) { | |
350 | flags |= NEGATIVE_FLAG; | |
351 | n = -n; | |
352 | } | |
353 | if (n != 0) { | |
354 | _setToInt(n); | |
355 | compact(); | |
356 | } | |
357 | return *this; | |
358 | } | |
359 | ||
360 | void DecimalQuantity::_setToInt(int32_t n) { | |
361 | if (n == INT32_MIN) { | |
362 | readLongToBcd(-static_cast<int64_t>(n)); | |
363 | } else { | |
364 | readIntToBcd(n); | |
365 | } | |
366 | } | |
367 | ||
368 | DecimalQuantity &DecimalQuantity::setToLong(int64_t n) { | |
369 | setBcdToZero(); | |
370 | flags = 0; | |
371 | if (n < 0 && n > INT64_MIN) { | |
372 | flags |= NEGATIVE_FLAG; | |
373 | n = -n; | |
374 | } | |
375 | if (n != 0) { | |
376 | _setToLong(n); | |
377 | compact(); | |
378 | } | |
379 | return *this; | |
380 | } | |
381 | ||
382 | void DecimalQuantity::_setToLong(int64_t n) { | |
383 | if (n == INT64_MIN) { | |
384 | DecNum decnum; | |
385 | UErrorCode localStatus = U_ZERO_ERROR; | |
386 | decnum.setTo("9.223372036854775808E+18", localStatus); | |
387 | if (U_FAILURE(localStatus)) { return; } // unexpected | |
388 | flags |= NEGATIVE_FLAG; | |
389 | readDecNumberToBcd(decnum); | |
390 | } else if (n <= INT32_MAX) { | |
391 | readIntToBcd(static_cast<int32_t>(n)); | |
392 | } else { | |
393 | readLongToBcd(n); | |
394 | } | |
395 | } | |
396 | ||
397 | DecimalQuantity &DecimalQuantity::setToDouble(double n) { | |
398 | setBcdToZero(); | |
399 | flags = 0; | |
400 | // signbit() from <math.h> handles +0.0 vs -0.0 | |
401 | if (std::signbit(n)) { | |
402 | flags |= NEGATIVE_FLAG; | |
403 | n = -n; | |
404 | } | |
405 | if (std::isnan(n) != 0) { | |
406 | flags |= NAN_FLAG; | |
407 | } else if (std::isfinite(n) == 0) { | |
408 | flags |= INFINITY_FLAG; | |
409 | } else if (n != 0) { | |
410 | _setToDoubleFast(n); | |
411 | compact(); | |
412 | } | |
413 | return *this; | |
414 | } | |
415 | ||
416 | void DecimalQuantity::_setToDoubleFast(double n) { | |
417 | isApproximate = true; | |
418 | origDouble = n; | |
419 | origDelta = 0; | |
420 | ||
421 | // Make sure the double is an IEEE 754 double. If not, fall back to the slow path right now. | |
422 | // TODO: Make a fast path for other types of doubles. | |
423 | if (!std::numeric_limits<double>::is_iec559) { | |
424 | convertToAccurateDouble(); | |
425 | // Turn off the approximate double flag, since the value is now exact. | |
426 | isApproximate = false; | |
427 | origDouble = 0.0; | |
428 | return; | |
429 | } | |
430 | ||
431 | // To get the bits from the double, use memcpy, which takes care of endianness. | |
432 | uint64_t ieeeBits; | |
433 | uprv_memcpy(&ieeeBits, &n, sizeof(n)); | |
434 | int32_t exponent = static_cast<int32_t>((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff; | |
435 | ||
436 | // Not all integers can be represented exactly for exponent > 52 | |
437 | if (exponent <= 52 && static_cast<int64_t>(n) == n) { | |
438 | _setToLong(static_cast<int64_t>(n)); | |
439 | return; | |
440 | } | |
441 | ||
442 | // 3.3219... is log2(10) | |
443 | auto fracLength = static_cast<int32_t> ((52 - exponent) / 3.32192809489); | |
444 | if (fracLength >= 0) { | |
445 | int32_t i = fracLength; | |
446 | // 1e22 is the largest exact double. | |
447 | for (; i >= 22; i -= 22) n *= 1e22; | |
448 | n *= DOUBLE_MULTIPLIERS[i]; | |
449 | } else { | |
450 | int32_t i = fracLength; | |
451 | // 1e22 is the largest exact double. | |
452 | for (; i <= -22; i += 22) n /= 1e22; | |
453 | n /= DOUBLE_MULTIPLIERS[-i]; | |
454 | } | |
455 | auto result = static_cast<int64_t>(std::round(n)); | |
456 | if (result != 0) { | |
457 | _setToLong(result); | |
458 | scale -= fracLength; | |
459 | } | |
460 | } | |
461 | ||
462 | void DecimalQuantity::convertToAccurateDouble() { | |
463 | U_ASSERT(origDouble != 0); | |
464 | int32_t delta = origDelta; | |
465 | ||
466 | // Call the slow oracle function (Double.toString in Java, DoubleToAscii in C++). | |
467 | char buffer[DoubleToStringConverter::kBase10MaximalLength + 1]; | |
468 | bool sign; // unused; always positive | |
469 | int32_t length; | |
470 | int32_t point; | |
471 | DoubleToStringConverter::DoubleToAscii( | |
472 | origDouble, | |
473 | DoubleToStringConverter::DtoaMode::SHORTEST, | |
474 | 0, | |
475 | buffer, | |
476 | sizeof(buffer), | |
477 | &sign, | |
478 | &length, | |
479 | &point | |
480 | ); | |
481 | ||
482 | setBcdToZero(); | |
483 | readDoubleConversionToBcd(buffer, length, point); | |
484 | scale += delta; | |
485 | explicitExactDouble = true; | |
486 | } | |
487 | ||
488 | DecimalQuantity &DecimalQuantity::setToDecNumber(StringPiece n, UErrorCode& status) { | |
489 | setBcdToZero(); | |
490 | flags = 0; | |
491 | ||
492 | // Compute the decNumber representation | |
493 | DecNum decnum; | |
494 | decnum.setTo(n, status); | |
495 | ||
496 | _setToDecNum(decnum, status); | |
497 | return *this; | |
498 | } | |
499 | ||
500 | DecimalQuantity& DecimalQuantity::setToDecNum(const DecNum& decnum, UErrorCode& status) { | |
501 | setBcdToZero(); | |
502 | flags = 0; | |
503 | ||
504 | _setToDecNum(decnum, status); | |
505 | return *this; | |
506 | } | |
507 | ||
508 | void DecimalQuantity::_setToDecNum(const DecNum& decnum, UErrorCode& status) { | |
509 | if (U_FAILURE(status)) { return; } | |
510 | if (decnum.isNegative()) { | |
511 | flags |= NEGATIVE_FLAG; | |
512 | } | |
513 | if (!decnum.isZero()) { | |
514 | readDecNumberToBcd(decnum); | |
515 | compact(); | |
516 | } | |
517 | } | |
518 | ||
519 | int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const { | |
520 | // NOTE: Call sites should be guarded by fitsInLong(), like this: | |
521 | // if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ } | |
522 | // Fallback behavior upon truncateIfOverflow is to truncate at 17 digits. | |
523 | uint64_t result = 0L; | |
3d1f044b | 524 | int32_t upperMagnitude = scale + precision - 1; |
0f5d89e8 A |
525 | if (truncateIfOverflow) { |
526 | upperMagnitude = std::min(upperMagnitude, 17); | |
527 | } | |
528 | for (int32_t magnitude = upperMagnitude; magnitude >= 0; magnitude--) { | |
529 | result = result * 10 + getDigitPos(magnitude - scale); | |
530 | } | |
531 | if (isNegative()) { | |
532 | return static_cast<int64_t>(0LL - result); // i.e., -result | |
533 | } | |
534 | return static_cast<int64_t>(result); | |
535 | } | |
536 | ||
537 | uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const { | |
538 | uint64_t result = 0L; | |
539 | int32_t magnitude = -1; | |
3d1f044b | 540 | int32_t lowerMagnitude = scale; |
0f5d89e8 A |
541 | if (includeTrailingZeros) { |
542 | lowerMagnitude = std::min(lowerMagnitude, rReqPos); | |
543 | } | |
544 | for (; magnitude >= lowerMagnitude && result <= 1e18L; magnitude--) { | |
545 | result = result * 10 + getDigitPos(magnitude - scale); | |
546 | } | |
547 | // Remove trailing zeros; this can happen during integer overflow cases. | |
548 | if (!includeTrailingZeros) { | |
549 | while (result > 0 && (result % 10) == 0) { | |
550 | result /= 10; | |
551 | } | |
552 | } | |
553 | return result; | |
554 | } | |
555 | ||
556 | bool DecimalQuantity::fitsInLong(bool ignoreFraction) const { | |
340931cb A |
557 | if (isInfinite() || isNaN()) { |
558 | return false; | |
559 | } | |
560 | if (isZeroish()) { | |
0f5d89e8 A |
561 | return true; |
562 | } | |
563 | if (scale < 0 && !ignoreFraction) { | |
564 | return false; | |
565 | } | |
566 | int magnitude = getMagnitude(); | |
567 | if (magnitude < 18) { | |
568 | return true; | |
569 | } | |
570 | if (magnitude > 18) { | |
571 | return false; | |
572 | } | |
573 | // Hard case: the magnitude is 10^18. | |
574 | // The largest int64 is: 9,223,372,036,854,775,807 | |
575 | for (int p = 0; p < precision; p++) { | |
576 | int8_t digit = getDigit(18 - p); | |
577 | static int8_t INT64_BCD[] = { 9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8 }; | |
578 | if (digit < INT64_BCD[p]) { | |
579 | return true; | |
580 | } else if (digit > INT64_BCD[p]) { | |
581 | return false; | |
582 | } | |
583 | } | |
584 | // Exactly equal to max long plus one. | |
585 | return isNegative(); | |
586 | } | |
587 | ||
588 | double DecimalQuantity::toDouble() const { | |
589 | // If this assertion fails, you need to call roundToInfinity() or some other rounding method. | |
590 | // See the comment in the header file explaining the "isApproximate" field. | |
591 | U_ASSERT(!isApproximate); | |
592 | ||
593 | if (isNaN()) { | |
594 | return NAN; | |
595 | } else if (isInfinite()) { | |
596 | return isNegative() ? -INFINITY : INFINITY; | |
597 | } | |
598 | ||
599 | // We are processing well-formed input, so we don't need any special options to StringToDoubleConverter. | |
600 | StringToDoubleConverter converter(0, 0, 0, "", ""); | |
601 | UnicodeString numberString = this->toScientificString(); | |
602 | int32_t count; | |
603 | return converter.StringToDouble( | |
604 | reinterpret_cast<const uint16_t*>(numberString.getBuffer()), | |
605 | numberString.length(), | |
606 | &count); | |
607 | } | |
608 | ||
609 | void DecimalQuantity::toDecNum(DecNum& output, UErrorCode& status) const { | |
610 | // Special handling for zero | |
611 | if (precision == 0) { | |
612 | output.setTo("0", status); | |
613 | } | |
614 | ||
615 | // Use the BCD constructor. We need to do a little bit of work to convert, though. | |
616 | // The decNumber constructor expects most-significant first, but we store least-significant first. | |
617 | MaybeStackArray<uint8_t, 20> ubcd(precision); | |
618 | for (int32_t m = 0; m < precision; m++) { | |
619 | ubcd[precision - m - 1] = static_cast<uint8_t>(getDigitPos(m)); | |
620 | } | |
621 | output.setTo(ubcd.getAlias(), precision, scale, isNegative(), status); | |
622 | } | |
623 | ||
624 | void DecimalQuantity::truncate() { | |
625 | if (scale < 0) { | |
626 | shiftRight(-scale); | |
627 | scale = 0; | |
628 | compact(); | |
629 | } | |
630 | } | |
631 | ||
3d1f044b A |
632 | void DecimalQuantity::roundToNickel(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) { |
633 | roundToMagnitude(magnitude, roundingMode, true, status); | |
634 | } | |
635 | ||
0f5d89e8 | 636 | void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) { |
3d1f044b A |
637 | roundToMagnitude(magnitude, roundingMode, false, status); |
638 | } | |
639 | ||
640 | void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, bool nickel, UErrorCode& status) { | |
0f5d89e8 A |
641 | // The position in the BCD at which rounding will be performed; digits to the right of position |
642 | // will be rounded away. | |
0f5d89e8 A |
643 | int position = safeSubtract(magnitude, scale); |
644 | ||
3d1f044b A |
645 | // "trailing" = least significant digit to the left of rounding |
646 | int8_t trailingDigit = getDigitPos(position); | |
647 | ||
648 | if (position <= 0 && !isApproximate && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { | |
0f5d89e8 A |
649 | // All digits are to the left of the rounding magnitude. |
650 | } else if (precision == 0) { | |
651 | // No rounding for zero. | |
652 | } else { | |
653 | // Perform rounding logic. | |
654 | // "leading" = most significant digit to the right of rounding | |
0f5d89e8 | 655 | int8_t leadingDigit = getDigitPos(safeSubtract(position, 1)); |
0f5d89e8 A |
656 | |
657 | // Compute which section of the number we are in. | |
658 | // EDGE means we are at the bottom or top edge, like 1.000 or 1.999 (used by doubles) | |
659 | // LOWER means we are between the bottom edge and the midpoint, like 1.391 | |
660 | // MIDPOINT means we are exactly in the middle, like 1.500 | |
661 | // UPPER means we are between the midpoint and the top edge, like 1.916 | |
3d1f044b | 662 | roundingutils::Section section; |
0f5d89e8 | 663 | if (!isApproximate) { |
3d1f044b A |
664 | if (nickel && trailingDigit != 2 && trailingDigit != 7) { |
665 | // Nickel rounding, and not at .02x or .07x | |
666 | if (trailingDigit < 2) { | |
667 | // .00, .01 => down to .00 | |
668 | section = roundingutils::SECTION_LOWER; | |
669 | } else if (trailingDigit < 5) { | |
670 | // .03, .04 => up to .05 | |
671 | section = roundingutils::SECTION_UPPER; | |
672 | } else if (trailingDigit < 7) { | |
673 | // .05, .06 => down to .05 | |
674 | section = roundingutils::SECTION_LOWER; | |
675 | } else { | |
676 | // .08, .09 => up to .10 | |
677 | section = roundingutils::SECTION_UPPER; | |
678 | } | |
679 | } else if (leadingDigit < 5) { | |
680 | // Includes nickel rounding .020-.024 and .070-.074 | |
0f5d89e8 A |
681 | section = roundingutils::SECTION_LOWER; |
682 | } else if (leadingDigit > 5) { | |
3d1f044b | 683 | // Includes nickel rounding .026-.029 and .076-.079 |
0f5d89e8 A |
684 | section = roundingutils::SECTION_UPPER; |
685 | } else { | |
3d1f044b A |
686 | // Includes nickel rounding .025 and .075 |
687 | section = roundingutils::SECTION_MIDPOINT; | |
0f5d89e8 A |
688 | for (int p = safeSubtract(position, 2); p >= 0; p--) { |
689 | if (getDigitPos(p) != 0) { | |
690 | section = roundingutils::SECTION_UPPER; | |
691 | break; | |
692 | } | |
693 | } | |
694 | } | |
695 | } else { | |
696 | int32_t p = safeSubtract(position, 2); | |
697 | int32_t minP = uprv_max(0, precision - 14); | |
3d1f044b | 698 | if (leadingDigit == 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { |
0f5d89e8 A |
699 | section = roundingutils::SECTION_LOWER_EDGE; |
700 | for (; p >= minP; p--) { | |
701 | if (getDigitPos(p) != 0) { | |
702 | section = roundingutils::SECTION_LOWER; | |
703 | break; | |
704 | } | |
705 | } | |
3d1f044b A |
706 | } else if (leadingDigit == 4 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { |
707 | section = roundingutils::SECTION_MIDPOINT; | |
0f5d89e8 A |
708 | for (; p >= minP; p--) { |
709 | if (getDigitPos(p) != 9) { | |
710 | section = roundingutils::SECTION_LOWER; | |
711 | break; | |
712 | } | |
713 | } | |
3d1f044b A |
714 | } else if (leadingDigit == 5 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { |
715 | section = roundingutils::SECTION_MIDPOINT; | |
0f5d89e8 A |
716 | for (; p >= minP; p--) { |
717 | if (getDigitPos(p) != 0) { | |
718 | section = roundingutils::SECTION_UPPER; | |
719 | break; | |
720 | } | |
721 | } | |
3d1f044b | 722 | } else if (leadingDigit == 9 && (!nickel || trailingDigit == 4 || trailingDigit == 9)) { |
0f5d89e8 A |
723 | section = roundingutils::SECTION_UPPER_EDGE; |
724 | for (; p >= minP; p--) { | |
725 | if (getDigitPos(p) != 9) { | |
726 | section = roundingutils::SECTION_UPPER; | |
727 | break; | |
728 | } | |
729 | } | |
3d1f044b A |
730 | } else if (nickel && trailingDigit != 2 && trailingDigit != 7) { |
731 | // Nickel rounding, and not at .02x or .07x | |
732 | if (trailingDigit < 2) { | |
733 | // .00, .01 => down to .00 | |
734 | section = roundingutils::SECTION_LOWER; | |
735 | } else if (trailingDigit < 5) { | |
736 | // .03, .04 => up to .05 | |
737 | section = roundingutils::SECTION_UPPER; | |
738 | } else if (trailingDigit < 7) { | |
739 | // .05, .06 => down to .05 | |
740 | section = roundingutils::SECTION_LOWER; | |
741 | } else { | |
742 | // .08, .09 => up to .10 | |
743 | section = roundingutils::SECTION_UPPER; | |
744 | } | |
0f5d89e8 | 745 | } else if (leadingDigit < 5) { |
3d1f044b | 746 | // Includes nickel rounding .020-.024 and .070-.074 |
0f5d89e8 A |
747 | section = roundingutils::SECTION_LOWER; |
748 | } else { | |
3d1f044b | 749 | // Includes nickel rounding .026-.029 and .076-.079 |
0f5d89e8 A |
750 | section = roundingutils::SECTION_UPPER; |
751 | } | |
752 | ||
753 | bool roundsAtMidpoint = roundingutils::roundsAtMidpoint(roundingMode); | |
754 | if (safeSubtract(position, 1) < precision - 14 || | |
755 | (roundsAtMidpoint && section == roundingutils::SECTION_MIDPOINT) || | |
756 | (!roundsAtMidpoint && section < 0 /* i.e. at upper or lower edge */)) { | |
3d1f044b A |
757 | // Oops! This means that we have to get the exact representation of the double, |
758 | // because the zone of uncertainty is along the rounding boundary. | |
0f5d89e8 | 759 | convertToAccurateDouble(); |
3d1f044b | 760 | roundToMagnitude(magnitude, roundingMode, nickel, status); // start over |
0f5d89e8 A |
761 | return; |
762 | } | |
763 | ||
764 | // Turn off the approximate double flag, since the value is now confirmed to be exact. | |
765 | isApproximate = false; | |
766 | origDouble = 0.0; | |
767 | origDelta = 0; | |
768 | ||
3d1f044b | 769 | if (position <= 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { |
0f5d89e8 A |
770 | // All digits are to the left of the rounding magnitude. |
771 | return; | |
772 | } | |
773 | ||
774 | // Good to continue rounding. | |
775 | if (section == -1) { section = roundingutils::SECTION_LOWER; } | |
776 | if (section == -2) { section = roundingutils::SECTION_UPPER; } | |
777 | } | |
778 | ||
3d1f044b A |
779 | // Nickel rounding "half even" goes to the nearest whole (away from the 5). |
780 | bool isEven = nickel | |
781 | ? (trailingDigit < 2 || trailingDigit > 7 | |
782 | || (trailingDigit == 2 && section != roundingutils::SECTION_UPPER) | |
783 | || (trailingDigit == 7 && section == roundingutils::SECTION_UPPER)) | |
784 | : (trailingDigit % 2) == 0; | |
785 | ||
786 | bool roundDown = roundingutils::getRoundingDirection(isEven, | |
0f5d89e8 A |
787 | isNegative(), |
788 | section, | |
789 | roundingMode, | |
790 | status); | |
791 | if (U_FAILURE(status)) { | |
792 | return; | |
793 | } | |
794 | ||
795 | // Perform truncation | |
796 | if (position >= precision) { | |
797 | setBcdToZero(); | |
798 | scale = magnitude; | |
799 | } else { | |
800 | shiftRight(position); | |
801 | } | |
802 | ||
3d1f044b A |
803 | if (nickel) { |
804 | if (trailingDigit < 5 && roundDown) { | |
805 | setDigitPos(0, 0); | |
806 | compact(); | |
807 | return; | |
808 | } else if (trailingDigit >= 5 && !roundDown) { | |
809 | setDigitPos(0, 9); | |
810 | trailingDigit = 9; | |
811 | // do not return: use the bubbling logic below | |
812 | } else { | |
813 | setDigitPos(0, 5); | |
814 | // compact not necessary: digit at position 0 is nonzero | |
815 | return; | |
816 | } | |
817 | } | |
818 | ||
0f5d89e8 A |
819 | // Bubble the result to the higher digits |
820 | if (!roundDown) { | |
821 | if (trailingDigit == 9) { | |
822 | int bubblePos = 0; | |
3d1f044b A |
823 | // Note: in the long implementation, the most digits BCD can have at this point is |
824 | // 15, so bubblePos <= 15 and getDigitPos(bubblePos) is safe. | |
0f5d89e8 A |
825 | for (; getDigitPos(bubblePos) == 9; bubblePos++) {} |
826 | shiftRight(bubblePos); // shift off the trailing 9s | |
827 | } | |
828 | int8_t digit0 = getDigitPos(0); | |
829 | U_ASSERT(digit0 != 9); | |
830 | setDigitPos(0, static_cast<int8_t>(digit0 + 1)); | |
831 | precision += 1; // in case an extra digit got added | |
832 | } | |
833 | ||
834 | compact(); | |
835 | } | |
836 | } | |
837 | ||
838 | void DecimalQuantity::roundToInfinity() { | |
839 | if (isApproximate) { | |
840 | convertToAccurateDouble(); | |
841 | } | |
842 | } | |
843 | ||
844 | void DecimalQuantity::appendDigit(int8_t value, int32_t leadingZeros, bool appendAsInteger) { | |
845 | U_ASSERT(leadingZeros >= 0); | |
846 | ||
847 | // Zero requires special handling to maintain the invariant that the least-significant digit | |
848 | // in the BCD is nonzero. | |
849 | if (value == 0) { | |
850 | if (appendAsInteger && precision != 0) { | |
851 | scale += leadingZeros + 1; | |
852 | } | |
853 | return; | |
854 | } | |
855 | ||
856 | // Deal with trailing zeros | |
857 | if (scale > 0) { | |
858 | leadingZeros += scale; | |
859 | if (appendAsInteger) { | |
860 | scale = 0; | |
861 | } | |
862 | } | |
863 | ||
864 | // Append digit | |
865 | shiftLeft(leadingZeros + 1); | |
866 | setDigitPos(0, value); | |
867 | ||
868 | // Fix scale if in integer mode | |
869 | if (appendAsInteger) { | |
870 | scale += leadingZeros + 1; | |
871 | } | |
872 | } | |
873 | ||
874 | UnicodeString DecimalQuantity::toPlainString() const { | |
875 | U_ASSERT(!isApproximate); | |
876 | UnicodeString sb; | |
877 | if (isNegative()) { | |
878 | sb.append(u'-'); | |
879 | } | |
880 | if (precision == 0 || getMagnitude() < 0) { | |
881 | sb.append(u'0'); | |
882 | } | |
883 | for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { | |
884 | if (m == -1) { sb.append(u'.'); } | |
885 | sb.append(getDigit(m) + u'0'); | |
886 | } | |
887 | return sb; | |
888 | } | |
889 | ||
890 | UnicodeString DecimalQuantity::toScientificString() const { | |
891 | U_ASSERT(!isApproximate); | |
892 | UnicodeString result; | |
893 | if (isNegative()) { | |
894 | result.append(u'-'); | |
895 | } | |
896 | if (precision == 0) { | |
897 | result.append(u"0E+0", -1); | |
898 | return result; | |
899 | } | |
3d1f044b A |
900 | int32_t upperPos = precision - 1; |
901 | int32_t lowerPos = 0; | |
0f5d89e8 A |
902 | int32_t p = upperPos; |
903 | result.append(u'0' + getDigitPos(p)); | |
904 | if ((--p) >= lowerPos) { | |
905 | result.append(u'.'); | |
906 | for (; p >= lowerPos; p--) { | |
907 | result.append(u'0' + getDigitPos(p)); | |
908 | } | |
909 | } | |
910 | result.append(u'E'); | |
911 | int32_t _scale = upperPos + scale; | |
3d1f044b A |
912 | if (_scale == INT32_MIN) { |
913 | result.append({u"-2147483648", -1}); | |
914 | return result; | |
915 | } else if (_scale < 0) { | |
0f5d89e8 A |
916 | _scale *= -1; |
917 | result.append(u'-'); | |
918 | } else { | |
919 | result.append(u'+'); | |
920 | } | |
921 | if (_scale == 0) { | |
922 | result.append(u'0'); | |
923 | } | |
924 | int32_t insertIndex = result.length(); | |
925 | while (_scale > 0) { | |
926 | std::div_t res = std::div(_scale, 10); | |
927 | result.insert(insertIndex, u'0' + res.rem); | |
928 | _scale = res.quot; | |
929 | } | |
930 | return result; | |
931 | } | |
932 | ||
933 | //////////////////////////////////////////////////// | |
934 | /// End of DecimalQuantity_AbstractBCD.java /// | |
935 | /// Start of DecimalQuantity_DualStorageBCD.java /// | |
936 | //////////////////////////////////////////////////// | |
937 | ||
938 | int8_t DecimalQuantity::getDigitPos(int32_t position) const { | |
939 | if (usingBytes) { | |
940 | if (position < 0 || position >= precision) { return 0; } | |
941 | return fBCD.bcdBytes.ptr[position]; | |
942 | } else { | |
943 | if (position < 0 || position >= 16) { return 0; } | |
944 | return (int8_t) ((fBCD.bcdLong >> (position * 4)) & 0xf); | |
945 | } | |
946 | } | |
947 | ||
948 | void DecimalQuantity::setDigitPos(int32_t position, int8_t value) { | |
949 | U_ASSERT(position >= 0); | |
950 | if (usingBytes) { | |
951 | ensureCapacity(position + 1); | |
952 | fBCD.bcdBytes.ptr[position] = value; | |
953 | } else if (position >= 16) { | |
954 | switchStorage(); | |
955 | ensureCapacity(position + 1); | |
956 | fBCD.bcdBytes.ptr[position] = value; | |
957 | } else { | |
958 | int shift = position * 4; | |
959 | fBCD.bcdLong = (fBCD.bcdLong & ~(0xfL << shift)) | ((long) value << shift); | |
960 | } | |
961 | } | |
962 | ||
963 | void DecimalQuantity::shiftLeft(int32_t numDigits) { | |
964 | if (!usingBytes && precision + numDigits > 16) { | |
965 | switchStorage(); | |
966 | } | |
967 | if (usingBytes) { | |
968 | ensureCapacity(precision + numDigits); | |
969 | int i = precision + numDigits - 1; | |
970 | for (; i >= numDigits; i--) { | |
971 | fBCD.bcdBytes.ptr[i] = fBCD.bcdBytes.ptr[i - numDigits]; | |
972 | } | |
973 | for (; i >= 0; i--) { | |
974 | fBCD.bcdBytes.ptr[i] = 0; | |
975 | } | |
976 | } else { | |
977 | fBCD.bcdLong <<= (numDigits * 4); | |
978 | } | |
979 | scale -= numDigits; | |
980 | precision += numDigits; | |
981 | } | |
982 | ||
983 | void DecimalQuantity::shiftRight(int32_t numDigits) { | |
984 | if (usingBytes) { | |
985 | int i = 0; | |
986 | for (; i < precision - numDigits; i++) { | |
987 | fBCD.bcdBytes.ptr[i] = fBCD.bcdBytes.ptr[i + numDigits]; | |
988 | } | |
989 | for (; i < precision; i++) { | |
990 | fBCD.bcdBytes.ptr[i] = 0; | |
991 | } | |
992 | } else { | |
993 | fBCD.bcdLong >>= (numDigits * 4); | |
994 | } | |
995 | scale += numDigits; | |
996 | precision -= numDigits; | |
997 | } | |
998 | ||
3d1f044b A |
999 | void DecimalQuantity::popFromLeft(int32_t numDigits) { |
1000 | U_ASSERT(numDigits <= precision); | |
1001 | if (usingBytes) { | |
1002 | int i = precision - 1; | |
1003 | for (; i >= precision - numDigits; i--) { | |
1004 | fBCD.bcdBytes.ptr[i] = 0; | |
1005 | } | |
1006 | } else { | |
1007 | fBCD.bcdLong &= (static_cast<uint64_t>(1) << ((precision - numDigits) * 4)) - 1; | |
1008 | } | |
1009 | precision -= numDigits; | |
1010 | } | |
1011 | ||
0f5d89e8 A |
1012 | void DecimalQuantity::setBcdToZero() { |
1013 | if (usingBytes) { | |
1014 | uprv_free(fBCD.bcdBytes.ptr); | |
1015 | fBCD.bcdBytes.ptr = nullptr; | |
1016 | usingBytes = false; | |
1017 | } | |
1018 | fBCD.bcdLong = 0L; | |
1019 | scale = 0; | |
1020 | precision = 0; | |
1021 | isApproximate = false; | |
1022 | origDouble = 0; | |
1023 | origDelta = 0; | |
1024 | } | |
1025 | ||
1026 | void DecimalQuantity::readIntToBcd(int32_t n) { | |
1027 | U_ASSERT(n != 0); | |
1028 | // ints always fit inside the long implementation. | |
1029 | uint64_t result = 0L; | |
1030 | int i = 16; | |
1031 | for (; n != 0; n /= 10, i--) { | |
1032 | result = (result >> 4) + ((static_cast<uint64_t>(n) % 10) << 60); | |
1033 | } | |
1034 | U_ASSERT(!usingBytes); | |
1035 | fBCD.bcdLong = result >> (i * 4); | |
1036 | scale = 0; | |
1037 | precision = 16 - i; | |
1038 | } | |
1039 | ||
1040 | void DecimalQuantity::readLongToBcd(int64_t n) { | |
1041 | U_ASSERT(n != 0); | |
1042 | if (n >= 10000000000000000L) { | |
1043 | ensureCapacity(); | |
1044 | int i = 0; | |
1045 | for (; n != 0L; n /= 10L, i++) { | |
1046 | fBCD.bcdBytes.ptr[i] = static_cast<int8_t>(n % 10); | |
1047 | } | |
1048 | U_ASSERT(usingBytes); | |
1049 | scale = 0; | |
1050 | precision = i; | |
1051 | } else { | |
1052 | uint64_t result = 0L; | |
1053 | int i = 16; | |
1054 | for (; n != 0L; n /= 10L, i--) { | |
1055 | result = (result >> 4) + ((n % 10) << 60); | |
1056 | } | |
1057 | U_ASSERT(i >= 0); | |
1058 | U_ASSERT(!usingBytes); | |
1059 | fBCD.bcdLong = result >> (i * 4); | |
1060 | scale = 0; | |
1061 | precision = 16 - i; | |
1062 | } | |
1063 | } | |
1064 | ||
1065 | void DecimalQuantity::readDecNumberToBcd(const DecNum& decnum) { | |
1066 | const decNumber* dn = decnum.getRawDecNumber(); | |
1067 | if (dn->digits > 16) { | |
1068 | ensureCapacity(dn->digits); | |
1069 | for (int32_t i = 0; i < dn->digits; i++) { | |
1070 | fBCD.bcdBytes.ptr[i] = dn->lsu[i]; | |
1071 | } | |
1072 | } else { | |
1073 | uint64_t result = 0L; | |
1074 | for (int32_t i = 0; i < dn->digits; i++) { | |
1075 | result |= static_cast<uint64_t>(dn->lsu[i]) << (4 * i); | |
1076 | } | |
1077 | fBCD.bcdLong = result; | |
1078 | } | |
1079 | scale = dn->exponent; | |
1080 | precision = dn->digits; | |
1081 | } | |
1082 | ||
1083 | void DecimalQuantity::readDoubleConversionToBcd( | |
1084 | const char* buffer, int32_t length, int32_t point) { | |
1085 | // NOTE: Despite the fact that double-conversion's API is called | |
1086 | // "DoubleToAscii", they actually use '0' (as opposed to u8'0'). | |
1087 | if (length > 16) { | |
1088 | ensureCapacity(length); | |
1089 | for (int32_t i = 0; i < length; i++) { | |
1090 | fBCD.bcdBytes.ptr[i] = buffer[length-i-1] - '0'; | |
1091 | } | |
1092 | } else { | |
1093 | uint64_t result = 0L; | |
1094 | for (int32_t i = 0; i < length; i++) { | |
1095 | result |= static_cast<uint64_t>(buffer[length-i-1] - '0') << (4 * i); | |
1096 | } | |
1097 | fBCD.bcdLong = result; | |
1098 | } | |
1099 | scale = point - length; | |
1100 | precision = length; | |
1101 | } | |
1102 | ||
1103 | void DecimalQuantity::compact() { | |
1104 | if (usingBytes) { | |
1105 | int32_t delta = 0; | |
1106 | for (; delta < precision && fBCD.bcdBytes.ptr[delta] == 0; delta++); | |
1107 | if (delta == precision) { | |
1108 | // Number is zero | |
1109 | setBcdToZero(); | |
1110 | return; | |
1111 | } else { | |
1112 | // Remove trailing zeros | |
1113 | shiftRight(delta); | |
1114 | } | |
1115 | ||
1116 | // Compute precision | |
1117 | int32_t leading = precision - 1; | |
1118 | for (; leading >= 0 && fBCD.bcdBytes.ptr[leading] == 0; leading--); | |
1119 | precision = leading + 1; | |
1120 | ||
1121 | // Switch storage mechanism if possible | |
1122 | if (precision <= 16) { | |
1123 | switchStorage(); | |
1124 | } | |
1125 | ||
1126 | } else { | |
1127 | if (fBCD.bcdLong == 0L) { | |
1128 | // Number is zero | |
1129 | setBcdToZero(); | |
1130 | return; | |
1131 | } | |
1132 | ||
1133 | // Compact the number (remove trailing zeros) | |
1134 | // TODO: Use a more efficient algorithm here and below. There is a logarithmic one. | |
1135 | int32_t delta = 0; | |
1136 | for (; delta < precision && getDigitPos(delta) == 0; delta++); | |
1137 | fBCD.bcdLong >>= delta * 4; | |
1138 | scale += delta; | |
1139 | ||
1140 | // Compute precision | |
1141 | int32_t leading = precision - 1; | |
1142 | for (; leading >= 0 && getDigitPos(leading) == 0; leading--); | |
1143 | precision = leading + 1; | |
1144 | } | |
1145 | } | |
1146 | ||
1147 | void DecimalQuantity::ensureCapacity() { | |
1148 | ensureCapacity(40); | |
1149 | } | |
1150 | ||
1151 | void DecimalQuantity::ensureCapacity(int32_t capacity) { | |
1152 | if (capacity == 0) { return; } | |
1153 | int32_t oldCapacity = usingBytes ? fBCD.bcdBytes.len : 0; | |
1154 | if (!usingBytes) { | |
1155 | // TODO: There is nothing being done to check for memory allocation failures. | |
1156 | // TODO: Consider indexing by nybbles instead of bytes in C++, so that we can | |
1157 | // make these arrays half the size. | |
1158 | fBCD.bcdBytes.ptr = static_cast<int8_t*>(uprv_malloc(capacity * sizeof(int8_t))); | |
1159 | fBCD.bcdBytes.len = capacity; | |
1160 | // Initialize the byte array to zeros (this is done automatically in Java) | |
1161 | uprv_memset(fBCD.bcdBytes.ptr, 0, capacity * sizeof(int8_t)); | |
1162 | } else if (oldCapacity < capacity) { | |
1163 | auto bcd1 = static_cast<int8_t*>(uprv_malloc(capacity * 2 * sizeof(int8_t))); | |
1164 | uprv_memcpy(bcd1, fBCD.bcdBytes.ptr, oldCapacity * sizeof(int8_t)); | |
1165 | // Initialize the rest of the byte array to zeros (this is done automatically in Java) | |
1166 | uprv_memset(bcd1 + oldCapacity, 0, (capacity - oldCapacity) * sizeof(int8_t)); | |
1167 | uprv_free(fBCD.bcdBytes.ptr); | |
1168 | fBCD.bcdBytes.ptr = bcd1; | |
1169 | fBCD.bcdBytes.len = capacity * 2; | |
1170 | } | |
1171 | usingBytes = true; | |
1172 | } | |
1173 | ||
1174 | void DecimalQuantity::switchStorage() { | |
1175 | if (usingBytes) { | |
1176 | // Change from bytes to long | |
1177 | uint64_t bcdLong = 0L; | |
1178 | for (int i = precision - 1; i >= 0; i--) { | |
1179 | bcdLong <<= 4; | |
1180 | bcdLong |= fBCD.bcdBytes.ptr[i]; | |
1181 | } | |
1182 | uprv_free(fBCD.bcdBytes.ptr); | |
1183 | fBCD.bcdBytes.ptr = nullptr; | |
1184 | fBCD.bcdLong = bcdLong; | |
1185 | usingBytes = false; | |
1186 | } else { | |
1187 | // Change from long to bytes | |
1188 | // Copy the long into a local variable since it will get munged when we allocate the bytes | |
1189 | uint64_t bcdLong = fBCD.bcdLong; | |
1190 | ensureCapacity(); | |
1191 | for (int i = 0; i < precision; i++) { | |
1192 | fBCD.bcdBytes.ptr[i] = static_cast<int8_t>(bcdLong & 0xf); | |
1193 | bcdLong >>= 4; | |
1194 | } | |
1195 | U_ASSERT(usingBytes); | |
1196 | } | |
1197 | } | |
1198 | ||
1199 | void DecimalQuantity::copyBcdFrom(const DecimalQuantity &other) { | |
1200 | setBcdToZero(); | |
1201 | if (other.usingBytes) { | |
1202 | ensureCapacity(other.precision); | |
1203 | uprv_memcpy(fBCD.bcdBytes.ptr, other.fBCD.bcdBytes.ptr, other.precision * sizeof(int8_t)); | |
1204 | } else { | |
1205 | fBCD.bcdLong = other.fBCD.bcdLong; | |
1206 | } | |
1207 | } | |
1208 | ||
1209 | void DecimalQuantity::moveBcdFrom(DecimalQuantity &other) { | |
1210 | setBcdToZero(); | |
1211 | if (other.usingBytes) { | |
1212 | usingBytes = true; | |
1213 | fBCD.bcdBytes.ptr = other.fBCD.bcdBytes.ptr; | |
1214 | fBCD.bcdBytes.len = other.fBCD.bcdBytes.len; | |
1215 | // Take ownership away from the old instance: | |
1216 | other.fBCD.bcdBytes.ptr = nullptr; | |
1217 | other.usingBytes = false; | |
1218 | } else { | |
1219 | fBCD.bcdLong = other.fBCD.bcdLong; | |
1220 | } | |
1221 | } | |
1222 | ||
1223 | const char16_t* DecimalQuantity::checkHealth() const { | |
1224 | if (usingBytes) { | |
1225 | if (precision == 0) { return u"Zero precision but we are in byte mode"; } | |
1226 | int32_t capacity = fBCD.bcdBytes.len; | |
1227 | if (precision > capacity) { return u"Precision exceeds length of byte array"; } | |
1228 | if (getDigitPos(precision - 1) == 0) { return u"Most significant digit is zero in byte mode"; } | |
1229 | if (getDigitPos(0) == 0) { return u"Least significant digit is zero in long mode"; } | |
1230 | for (int i = 0; i < precision; i++) { | |
1231 | if (getDigitPos(i) >= 10) { return u"Digit exceeding 10 in byte array"; } | |
1232 | if (getDigitPos(i) < 0) { return u"Digit below 0 in byte array"; } | |
1233 | } | |
1234 | for (int i = precision; i < capacity; i++) { | |
1235 | if (getDigitPos(i) != 0) { return u"Nonzero digits outside of range in byte array"; } | |
1236 | } | |
1237 | } else { | |
1238 | if (precision == 0 && fBCD.bcdLong != 0) { | |
1239 | return u"Value in bcdLong even though precision is zero"; | |
1240 | } | |
1241 | if (precision > 16) { return u"Precision exceeds length of long"; } | |
1242 | if (precision != 0 && getDigitPos(precision - 1) == 0) { | |
1243 | return u"Most significant digit is zero in long mode"; | |
1244 | } | |
1245 | if (precision != 0 && getDigitPos(0) == 0) { | |
1246 | return u"Least significant digit is zero in long mode"; | |
1247 | } | |
1248 | for (int i = 0; i < precision; i++) { | |
1249 | if (getDigitPos(i) >= 10) { return u"Digit exceeding 10 in long"; } | |
1250 | if (getDigitPos(i) < 0) { return u"Digit below 0 in long (?!)"; } | |
1251 | } | |
1252 | for (int i = precision; i < 16; i++) { | |
1253 | if (getDigitPos(i) != 0) { return u"Nonzero digits outside of range in long"; } | |
1254 | } | |
1255 | } | |
1256 | ||
1257 | // No error | |
1258 | return nullptr; | |
1259 | } | |
1260 | ||
1261 | bool DecimalQuantity::operator==(const DecimalQuantity& other) const { | |
3d1f044b A |
1262 | bool basicEquals = |
1263 | scale == other.scale | |
1264 | && precision == other.precision | |
1265 | && flags == other.flags | |
1266 | && lReqPos == other.lReqPos | |
1267 | && rReqPos == other.rReqPos | |
1268 | && isApproximate == other.isApproximate; | |
1269 | if (!basicEquals) { | |
1270 | return false; | |
1271 | } | |
1272 | ||
1273 | if (precision == 0) { | |
1274 | return true; | |
1275 | } else if (isApproximate) { | |
1276 | return origDouble == other.origDouble && origDelta == other.origDelta; | |
1277 | } else { | |
1278 | for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { | |
1279 | if (getDigit(m) != other.getDigit(m)) { | |
1280 | return false; | |
1281 | } | |
1282 | } | |
1283 | return true; | |
1284 | } | |
0f5d89e8 A |
1285 | } |
1286 | ||
1287 | UnicodeString DecimalQuantity::toString() const { | |
1288 | MaybeStackArray<char, 30> digits(precision + 1); | |
1289 | for (int32_t i = 0; i < precision; i++) { | |
1290 | digits[i] = getDigitPos(precision - i - 1) + '0'; | |
1291 | } | |
1292 | digits[precision] = 0; // terminate buffer | |
1293 | char buffer8[100]; | |
1294 | snprintf( | |
1295 | buffer8, | |
1296 | sizeof(buffer8), | |
3d1f044b | 1297 | "<DecimalQuantity %d:%d %s %s%s%s%d>", |
0f5d89e8 A |
1298 | lReqPos, |
1299 | rReqPos, | |
0f5d89e8 A |
1300 | (usingBytes ? "bytes" : "long"), |
1301 | (isNegative() ? "-" : ""), | |
1302 | (precision == 0 ? "0" : digits.getAlias()), | |
1303 | "E", | |
1304 | scale); | |
1305 | return UnicodeString(buffer8, -1, US_INV); | |
1306 | } | |
1307 | ||
1308 | #endif /* #if !UCONFIG_NO_FORMATTING */ |