]>
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) { | |
208 | if (isInfinite() || isZero() || isNaN()) { | |
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) { | |
221 | if (isInfinite() || isZero() || isNaN()) { | |
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 | ||
321 | int8_t DecimalQuantity::signum() const { | |
322 | return isNegative() ? -1 : isZero() ? 0 : 1; | |
323 | } | |
324 | ||
325 | bool DecimalQuantity::isInfinite() const { | |
326 | return (flags & INFINITY_FLAG) != 0; | |
327 | } | |
328 | ||
329 | bool DecimalQuantity::isNaN() const { | |
330 | return (flags & NAN_FLAG) != 0; | |
331 | } | |
332 | ||
333 | bool DecimalQuantity::isZero() const { | |
334 | return precision == 0; | |
335 | } | |
336 | ||
337 | DecimalQuantity &DecimalQuantity::setToInt(int32_t n) { | |
338 | setBcdToZero(); | |
339 | flags = 0; | |
340 | if (n == INT32_MIN) { | |
341 | flags |= NEGATIVE_FLAG; | |
342 | // leave as INT32_MIN; handled below in _setToInt() | |
343 | } else if (n < 0) { | |
344 | flags |= NEGATIVE_FLAG; | |
345 | n = -n; | |
346 | } | |
347 | if (n != 0) { | |
348 | _setToInt(n); | |
349 | compact(); | |
350 | } | |
351 | return *this; | |
352 | } | |
353 | ||
354 | void DecimalQuantity::_setToInt(int32_t n) { | |
355 | if (n == INT32_MIN) { | |
356 | readLongToBcd(-static_cast<int64_t>(n)); | |
357 | } else { | |
358 | readIntToBcd(n); | |
359 | } | |
360 | } | |
361 | ||
362 | DecimalQuantity &DecimalQuantity::setToLong(int64_t n) { | |
363 | setBcdToZero(); | |
364 | flags = 0; | |
365 | if (n < 0 && n > INT64_MIN) { | |
366 | flags |= NEGATIVE_FLAG; | |
367 | n = -n; | |
368 | } | |
369 | if (n != 0) { | |
370 | _setToLong(n); | |
371 | compact(); | |
372 | } | |
373 | return *this; | |
374 | } | |
375 | ||
376 | void DecimalQuantity::_setToLong(int64_t n) { | |
377 | if (n == INT64_MIN) { | |
378 | DecNum decnum; | |
379 | UErrorCode localStatus = U_ZERO_ERROR; | |
380 | decnum.setTo("9.223372036854775808E+18", localStatus); | |
381 | if (U_FAILURE(localStatus)) { return; } // unexpected | |
382 | flags |= NEGATIVE_FLAG; | |
383 | readDecNumberToBcd(decnum); | |
384 | } else if (n <= INT32_MAX) { | |
385 | readIntToBcd(static_cast<int32_t>(n)); | |
386 | } else { | |
387 | readLongToBcd(n); | |
388 | } | |
389 | } | |
390 | ||
391 | DecimalQuantity &DecimalQuantity::setToDouble(double n) { | |
392 | setBcdToZero(); | |
393 | flags = 0; | |
394 | // signbit() from <math.h> handles +0.0 vs -0.0 | |
395 | if (std::signbit(n)) { | |
396 | flags |= NEGATIVE_FLAG; | |
397 | n = -n; | |
398 | } | |
399 | if (std::isnan(n) != 0) { | |
400 | flags |= NAN_FLAG; | |
401 | } else if (std::isfinite(n) == 0) { | |
402 | flags |= INFINITY_FLAG; | |
403 | } else if (n != 0) { | |
404 | _setToDoubleFast(n); | |
405 | compact(); | |
406 | } | |
407 | return *this; | |
408 | } | |
409 | ||
410 | void DecimalQuantity::_setToDoubleFast(double n) { | |
411 | isApproximate = true; | |
412 | origDouble = n; | |
413 | origDelta = 0; | |
414 | ||
415 | // Make sure the double is an IEEE 754 double. If not, fall back to the slow path right now. | |
416 | // TODO: Make a fast path for other types of doubles. | |
417 | if (!std::numeric_limits<double>::is_iec559) { | |
418 | convertToAccurateDouble(); | |
419 | // Turn off the approximate double flag, since the value is now exact. | |
420 | isApproximate = false; | |
421 | origDouble = 0.0; | |
422 | return; | |
423 | } | |
424 | ||
425 | // To get the bits from the double, use memcpy, which takes care of endianness. | |
426 | uint64_t ieeeBits; | |
427 | uprv_memcpy(&ieeeBits, &n, sizeof(n)); | |
428 | int32_t exponent = static_cast<int32_t>((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff; | |
429 | ||
430 | // Not all integers can be represented exactly for exponent > 52 | |
431 | if (exponent <= 52 && static_cast<int64_t>(n) == n) { | |
432 | _setToLong(static_cast<int64_t>(n)); | |
433 | return; | |
434 | } | |
435 | ||
436 | // 3.3219... is log2(10) | |
437 | auto fracLength = static_cast<int32_t> ((52 - exponent) / 3.32192809489); | |
438 | if (fracLength >= 0) { | |
439 | int32_t i = fracLength; | |
440 | // 1e22 is the largest exact double. | |
441 | for (; i >= 22; i -= 22) n *= 1e22; | |
442 | n *= DOUBLE_MULTIPLIERS[i]; | |
443 | } else { | |
444 | int32_t i = fracLength; | |
445 | // 1e22 is the largest exact double. | |
446 | for (; i <= -22; i += 22) n /= 1e22; | |
447 | n /= DOUBLE_MULTIPLIERS[-i]; | |
448 | } | |
449 | auto result = static_cast<int64_t>(std::round(n)); | |
450 | if (result != 0) { | |
451 | _setToLong(result); | |
452 | scale -= fracLength; | |
453 | } | |
454 | } | |
455 | ||
456 | void DecimalQuantity::convertToAccurateDouble() { | |
457 | U_ASSERT(origDouble != 0); | |
458 | int32_t delta = origDelta; | |
459 | ||
460 | // Call the slow oracle function (Double.toString in Java, DoubleToAscii in C++). | |
461 | char buffer[DoubleToStringConverter::kBase10MaximalLength + 1]; | |
462 | bool sign; // unused; always positive | |
463 | int32_t length; | |
464 | int32_t point; | |
465 | DoubleToStringConverter::DoubleToAscii( | |
466 | origDouble, | |
467 | DoubleToStringConverter::DtoaMode::SHORTEST, | |
468 | 0, | |
469 | buffer, | |
470 | sizeof(buffer), | |
471 | &sign, | |
472 | &length, | |
473 | &point | |
474 | ); | |
475 | ||
476 | setBcdToZero(); | |
477 | readDoubleConversionToBcd(buffer, length, point); | |
478 | scale += delta; | |
479 | explicitExactDouble = true; | |
480 | } | |
481 | ||
482 | DecimalQuantity &DecimalQuantity::setToDecNumber(StringPiece n, UErrorCode& status) { | |
483 | setBcdToZero(); | |
484 | flags = 0; | |
485 | ||
486 | // Compute the decNumber representation | |
487 | DecNum decnum; | |
488 | decnum.setTo(n, status); | |
489 | ||
490 | _setToDecNum(decnum, status); | |
491 | return *this; | |
492 | } | |
493 | ||
494 | DecimalQuantity& DecimalQuantity::setToDecNum(const DecNum& decnum, UErrorCode& status) { | |
495 | setBcdToZero(); | |
496 | flags = 0; | |
497 | ||
498 | _setToDecNum(decnum, status); | |
499 | return *this; | |
500 | } | |
501 | ||
502 | void DecimalQuantity::_setToDecNum(const DecNum& decnum, UErrorCode& status) { | |
503 | if (U_FAILURE(status)) { return; } | |
504 | if (decnum.isNegative()) { | |
505 | flags |= NEGATIVE_FLAG; | |
506 | } | |
507 | if (!decnum.isZero()) { | |
508 | readDecNumberToBcd(decnum); | |
509 | compact(); | |
510 | } | |
511 | } | |
512 | ||
513 | int64_t DecimalQuantity::toLong(bool truncateIfOverflow) const { | |
514 | // NOTE: Call sites should be guarded by fitsInLong(), like this: | |
515 | // if (dq.fitsInLong()) { /* use dq.toLong() */ } else { /* use some fallback */ } | |
516 | // Fallback behavior upon truncateIfOverflow is to truncate at 17 digits. | |
517 | uint64_t result = 0L; | |
3d1f044b | 518 | int32_t upperMagnitude = scale + precision - 1; |
0f5d89e8 A |
519 | if (truncateIfOverflow) { |
520 | upperMagnitude = std::min(upperMagnitude, 17); | |
521 | } | |
522 | for (int32_t magnitude = upperMagnitude; magnitude >= 0; magnitude--) { | |
523 | result = result * 10 + getDigitPos(magnitude - scale); | |
524 | } | |
525 | if (isNegative()) { | |
526 | return static_cast<int64_t>(0LL - result); // i.e., -result | |
527 | } | |
528 | return static_cast<int64_t>(result); | |
529 | } | |
530 | ||
531 | uint64_t DecimalQuantity::toFractionLong(bool includeTrailingZeros) const { | |
532 | uint64_t result = 0L; | |
533 | int32_t magnitude = -1; | |
3d1f044b | 534 | int32_t lowerMagnitude = scale; |
0f5d89e8 A |
535 | if (includeTrailingZeros) { |
536 | lowerMagnitude = std::min(lowerMagnitude, rReqPos); | |
537 | } | |
538 | for (; magnitude >= lowerMagnitude && result <= 1e18L; magnitude--) { | |
539 | result = result * 10 + getDigitPos(magnitude - scale); | |
540 | } | |
541 | // Remove trailing zeros; this can happen during integer overflow cases. | |
542 | if (!includeTrailingZeros) { | |
543 | while (result > 0 && (result % 10) == 0) { | |
544 | result /= 10; | |
545 | } | |
546 | } | |
547 | return result; | |
548 | } | |
549 | ||
550 | bool DecimalQuantity::fitsInLong(bool ignoreFraction) const { | |
551 | if (isZero()) { | |
552 | return true; | |
553 | } | |
554 | if (scale < 0 && !ignoreFraction) { | |
555 | return false; | |
556 | } | |
557 | int magnitude = getMagnitude(); | |
558 | if (magnitude < 18) { | |
559 | return true; | |
560 | } | |
561 | if (magnitude > 18) { | |
562 | return false; | |
563 | } | |
564 | // Hard case: the magnitude is 10^18. | |
565 | // The largest int64 is: 9,223,372,036,854,775,807 | |
566 | for (int p = 0; p < precision; p++) { | |
567 | int8_t digit = getDigit(18 - p); | |
568 | static int8_t INT64_BCD[] = { 9, 2, 2, 3, 3, 7, 2, 0, 3, 6, 8, 5, 4, 7, 7, 5, 8, 0, 8 }; | |
569 | if (digit < INT64_BCD[p]) { | |
570 | return true; | |
571 | } else if (digit > INT64_BCD[p]) { | |
572 | return false; | |
573 | } | |
574 | } | |
575 | // Exactly equal to max long plus one. | |
576 | return isNegative(); | |
577 | } | |
578 | ||
579 | double DecimalQuantity::toDouble() const { | |
580 | // If this assertion fails, you need to call roundToInfinity() or some other rounding method. | |
581 | // See the comment in the header file explaining the "isApproximate" field. | |
582 | U_ASSERT(!isApproximate); | |
583 | ||
584 | if (isNaN()) { | |
585 | return NAN; | |
586 | } else if (isInfinite()) { | |
587 | return isNegative() ? -INFINITY : INFINITY; | |
588 | } | |
589 | ||
590 | // We are processing well-formed input, so we don't need any special options to StringToDoubleConverter. | |
591 | StringToDoubleConverter converter(0, 0, 0, "", ""); | |
592 | UnicodeString numberString = this->toScientificString(); | |
593 | int32_t count; | |
594 | return converter.StringToDouble( | |
595 | reinterpret_cast<const uint16_t*>(numberString.getBuffer()), | |
596 | numberString.length(), | |
597 | &count); | |
598 | } | |
599 | ||
600 | void DecimalQuantity::toDecNum(DecNum& output, UErrorCode& status) const { | |
601 | // Special handling for zero | |
602 | if (precision == 0) { | |
603 | output.setTo("0", status); | |
604 | } | |
605 | ||
606 | // Use the BCD constructor. We need to do a little bit of work to convert, though. | |
607 | // The decNumber constructor expects most-significant first, but we store least-significant first. | |
608 | MaybeStackArray<uint8_t, 20> ubcd(precision); | |
609 | for (int32_t m = 0; m < precision; m++) { | |
610 | ubcd[precision - m - 1] = static_cast<uint8_t>(getDigitPos(m)); | |
611 | } | |
612 | output.setTo(ubcd.getAlias(), precision, scale, isNegative(), status); | |
613 | } | |
614 | ||
615 | void DecimalQuantity::truncate() { | |
616 | if (scale < 0) { | |
617 | shiftRight(-scale); | |
618 | scale = 0; | |
619 | compact(); | |
620 | } | |
621 | } | |
622 | ||
3d1f044b A |
623 | void DecimalQuantity::roundToNickel(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) { |
624 | roundToMagnitude(magnitude, roundingMode, true, status); | |
625 | } | |
626 | ||
0f5d89e8 | 627 | void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, UErrorCode& status) { |
3d1f044b A |
628 | roundToMagnitude(magnitude, roundingMode, false, status); |
629 | } | |
630 | ||
631 | void DecimalQuantity::roundToMagnitude(int32_t magnitude, RoundingMode roundingMode, bool nickel, UErrorCode& status) { | |
0f5d89e8 A |
632 | // The position in the BCD at which rounding will be performed; digits to the right of position |
633 | // will be rounded away. | |
0f5d89e8 A |
634 | int position = safeSubtract(magnitude, scale); |
635 | ||
3d1f044b A |
636 | // "trailing" = least significant digit to the left of rounding |
637 | int8_t trailingDigit = getDigitPos(position); | |
638 | ||
639 | if (position <= 0 && !isApproximate && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { | |
0f5d89e8 A |
640 | // All digits are to the left of the rounding magnitude. |
641 | } else if (precision == 0) { | |
642 | // No rounding for zero. | |
643 | } else { | |
644 | // Perform rounding logic. | |
645 | // "leading" = most significant digit to the right of rounding | |
0f5d89e8 | 646 | int8_t leadingDigit = getDigitPos(safeSubtract(position, 1)); |
0f5d89e8 A |
647 | |
648 | // Compute which section of the number we are in. | |
649 | // EDGE means we are at the bottom or top edge, like 1.000 or 1.999 (used by doubles) | |
650 | // LOWER means we are between the bottom edge and the midpoint, like 1.391 | |
651 | // MIDPOINT means we are exactly in the middle, like 1.500 | |
652 | // UPPER means we are between the midpoint and the top edge, like 1.916 | |
3d1f044b | 653 | roundingutils::Section section; |
0f5d89e8 | 654 | if (!isApproximate) { |
3d1f044b A |
655 | if (nickel && trailingDigit != 2 && trailingDigit != 7) { |
656 | // Nickel rounding, and not at .02x or .07x | |
657 | if (trailingDigit < 2) { | |
658 | // .00, .01 => down to .00 | |
659 | section = roundingutils::SECTION_LOWER; | |
660 | } else if (trailingDigit < 5) { | |
661 | // .03, .04 => up to .05 | |
662 | section = roundingutils::SECTION_UPPER; | |
663 | } else if (trailingDigit < 7) { | |
664 | // .05, .06 => down to .05 | |
665 | section = roundingutils::SECTION_LOWER; | |
666 | } else { | |
667 | // .08, .09 => up to .10 | |
668 | section = roundingutils::SECTION_UPPER; | |
669 | } | |
670 | } else if (leadingDigit < 5) { | |
671 | // Includes nickel rounding .020-.024 and .070-.074 | |
0f5d89e8 A |
672 | section = roundingutils::SECTION_LOWER; |
673 | } else if (leadingDigit > 5) { | |
3d1f044b | 674 | // Includes nickel rounding .026-.029 and .076-.079 |
0f5d89e8 A |
675 | section = roundingutils::SECTION_UPPER; |
676 | } else { | |
3d1f044b A |
677 | // Includes nickel rounding .025 and .075 |
678 | section = roundingutils::SECTION_MIDPOINT; | |
0f5d89e8 A |
679 | for (int p = safeSubtract(position, 2); p >= 0; p--) { |
680 | if (getDigitPos(p) != 0) { | |
681 | section = roundingutils::SECTION_UPPER; | |
682 | break; | |
683 | } | |
684 | } | |
685 | } | |
686 | } else { | |
687 | int32_t p = safeSubtract(position, 2); | |
688 | int32_t minP = uprv_max(0, precision - 14); | |
3d1f044b | 689 | if (leadingDigit == 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { |
0f5d89e8 A |
690 | section = roundingutils::SECTION_LOWER_EDGE; |
691 | for (; p >= minP; p--) { | |
692 | if (getDigitPos(p) != 0) { | |
693 | section = roundingutils::SECTION_LOWER; | |
694 | break; | |
695 | } | |
696 | } | |
3d1f044b A |
697 | } else if (leadingDigit == 4 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { |
698 | section = roundingutils::SECTION_MIDPOINT; | |
0f5d89e8 A |
699 | for (; p >= minP; p--) { |
700 | if (getDigitPos(p) != 9) { | |
701 | section = roundingutils::SECTION_LOWER; | |
702 | break; | |
703 | } | |
704 | } | |
3d1f044b A |
705 | } else if (leadingDigit == 5 && (!nickel || trailingDigit == 2 || trailingDigit == 7)) { |
706 | section = roundingutils::SECTION_MIDPOINT; | |
0f5d89e8 A |
707 | for (; p >= minP; p--) { |
708 | if (getDigitPos(p) != 0) { | |
709 | section = roundingutils::SECTION_UPPER; | |
710 | break; | |
711 | } | |
712 | } | |
3d1f044b | 713 | } else if (leadingDigit == 9 && (!nickel || trailingDigit == 4 || trailingDigit == 9)) { |
0f5d89e8 A |
714 | section = roundingutils::SECTION_UPPER_EDGE; |
715 | for (; p >= minP; p--) { | |
716 | if (getDigitPos(p) != 9) { | |
717 | section = roundingutils::SECTION_UPPER; | |
718 | break; | |
719 | } | |
720 | } | |
3d1f044b A |
721 | } else if (nickel && trailingDigit != 2 && trailingDigit != 7) { |
722 | // Nickel rounding, and not at .02x or .07x | |
723 | if (trailingDigit < 2) { | |
724 | // .00, .01 => down to .00 | |
725 | section = roundingutils::SECTION_LOWER; | |
726 | } else if (trailingDigit < 5) { | |
727 | // .03, .04 => up to .05 | |
728 | section = roundingutils::SECTION_UPPER; | |
729 | } else if (trailingDigit < 7) { | |
730 | // .05, .06 => down to .05 | |
731 | section = roundingutils::SECTION_LOWER; | |
732 | } else { | |
733 | // .08, .09 => up to .10 | |
734 | section = roundingutils::SECTION_UPPER; | |
735 | } | |
0f5d89e8 | 736 | } else if (leadingDigit < 5) { |
3d1f044b | 737 | // Includes nickel rounding .020-.024 and .070-.074 |
0f5d89e8 A |
738 | section = roundingutils::SECTION_LOWER; |
739 | } else { | |
3d1f044b | 740 | // Includes nickel rounding .026-.029 and .076-.079 |
0f5d89e8 A |
741 | section = roundingutils::SECTION_UPPER; |
742 | } | |
743 | ||
744 | bool roundsAtMidpoint = roundingutils::roundsAtMidpoint(roundingMode); | |
745 | if (safeSubtract(position, 1) < precision - 14 || | |
746 | (roundsAtMidpoint && section == roundingutils::SECTION_MIDPOINT) || | |
747 | (!roundsAtMidpoint && section < 0 /* i.e. at upper or lower edge */)) { | |
3d1f044b A |
748 | // Oops! This means that we have to get the exact representation of the double, |
749 | // because the zone of uncertainty is along the rounding boundary. | |
0f5d89e8 | 750 | convertToAccurateDouble(); |
3d1f044b | 751 | roundToMagnitude(magnitude, roundingMode, nickel, status); // start over |
0f5d89e8 A |
752 | return; |
753 | } | |
754 | ||
755 | // Turn off the approximate double flag, since the value is now confirmed to be exact. | |
756 | isApproximate = false; | |
757 | origDouble = 0.0; | |
758 | origDelta = 0; | |
759 | ||
3d1f044b | 760 | if (position <= 0 && (!nickel || trailingDigit == 0 || trailingDigit == 5)) { |
0f5d89e8 A |
761 | // All digits are to the left of the rounding magnitude. |
762 | return; | |
763 | } | |
764 | ||
765 | // Good to continue rounding. | |
766 | if (section == -1) { section = roundingutils::SECTION_LOWER; } | |
767 | if (section == -2) { section = roundingutils::SECTION_UPPER; } | |
768 | } | |
769 | ||
3d1f044b A |
770 | // Nickel rounding "half even" goes to the nearest whole (away from the 5). |
771 | bool isEven = nickel | |
772 | ? (trailingDigit < 2 || trailingDigit > 7 | |
773 | || (trailingDigit == 2 && section != roundingutils::SECTION_UPPER) | |
774 | || (trailingDigit == 7 && section == roundingutils::SECTION_UPPER)) | |
775 | : (trailingDigit % 2) == 0; | |
776 | ||
777 | bool roundDown = roundingutils::getRoundingDirection(isEven, | |
0f5d89e8 A |
778 | isNegative(), |
779 | section, | |
780 | roundingMode, | |
781 | status); | |
782 | if (U_FAILURE(status)) { | |
783 | return; | |
784 | } | |
785 | ||
786 | // Perform truncation | |
787 | if (position >= precision) { | |
788 | setBcdToZero(); | |
789 | scale = magnitude; | |
790 | } else { | |
791 | shiftRight(position); | |
792 | } | |
793 | ||
3d1f044b A |
794 | if (nickel) { |
795 | if (trailingDigit < 5 && roundDown) { | |
796 | setDigitPos(0, 0); | |
797 | compact(); | |
798 | return; | |
799 | } else if (trailingDigit >= 5 && !roundDown) { | |
800 | setDigitPos(0, 9); | |
801 | trailingDigit = 9; | |
802 | // do not return: use the bubbling logic below | |
803 | } else { | |
804 | setDigitPos(0, 5); | |
805 | // compact not necessary: digit at position 0 is nonzero | |
806 | return; | |
807 | } | |
808 | } | |
809 | ||
0f5d89e8 A |
810 | // Bubble the result to the higher digits |
811 | if (!roundDown) { | |
812 | if (trailingDigit == 9) { | |
813 | int bubblePos = 0; | |
3d1f044b A |
814 | // Note: in the long implementation, the most digits BCD can have at this point is |
815 | // 15, so bubblePos <= 15 and getDigitPos(bubblePos) is safe. | |
0f5d89e8 A |
816 | for (; getDigitPos(bubblePos) == 9; bubblePos++) {} |
817 | shiftRight(bubblePos); // shift off the trailing 9s | |
818 | } | |
819 | int8_t digit0 = getDigitPos(0); | |
820 | U_ASSERT(digit0 != 9); | |
821 | setDigitPos(0, static_cast<int8_t>(digit0 + 1)); | |
822 | precision += 1; // in case an extra digit got added | |
823 | } | |
824 | ||
825 | compact(); | |
826 | } | |
827 | } | |
828 | ||
829 | void DecimalQuantity::roundToInfinity() { | |
830 | if (isApproximate) { | |
831 | convertToAccurateDouble(); | |
832 | } | |
833 | } | |
834 | ||
835 | void DecimalQuantity::appendDigit(int8_t value, int32_t leadingZeros, bool appendAsInteger) { | |
836 | U_ASSERT(leadingZeros >= 0); | |
837 | ||
838 | // Zero requires special handling to maintain the invariant that the least-significant digit | |
839 | // in the BCD is nonzero. | |
840 | if (value == 0) { | |
841 | if (appendAsInteger && precision != 0) { | |
842 | scale += leadingZeros + 1; | |
843 | } | |
844 | return; | |
845 | } | |
846 | ||
847 | // Deal with trailing zeros | |
848 | if (scale > 0) { | |
849 | leadingZeros += scale; | |
850 | if (appendAsInteger) { | |
851 | scale = 0; | |
852 | } | |
853 | } | |
854 | ||
855 | // Append digit | |
856 | shiftLeft(leadingZeros + 1); | |
857 | setDigitPos(0, value); | |
858 | ||
859 | // Fix scale if in integer mode | |
860 | if (appendAsInteger) { | |
861 | scale += leadingZeros + 1; | |
862 | } | |
863 | } | |
864 | ||
865 | UnicodeString DecimalQuantity::toPlainString() const { | |
866 | U_ASSERT(!isApproximate); | |
867 | UnicodeString sb; | |
868 | if (isNegative()) { | |
869 | sb.append(u'-'); | |
870 | } | |
871 | if (precision == 0 || getMagnitude() < 0) { | |
872 | sb.append(u'0'); | |
873 | } | |
874 | for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { | |
875 | if (m == -1) { sb.append(u'.'); } | |
876 | sb.append(getDigit(m) + u'0'); | |
877 | } | |
878 | return sb; | |
879 | } | |
880 | ||
881 | UnicodeString DecimalQuantity::toScientificString() const { | |
882 | U_ASSERT(!isApproximate); | |
883 | UnicodeString result; | |
884 | if (isNegative()) { | |
885 | result.append(u'-'); | |
886 | } | |
887 | if (precision == 0) { | |
888 | result.append(u"0E+0", -1); | |
889 | return result; | |
890 | } | |
3d1f044b A |
891 | int32_t upperPos = precision - 1; |
892 | int32_t lowerPos = 0; | |
0f5d89e8 A |
893 | int32_t p = upperPos; |
894 | result.append(u'0' + getDigitPos(p)); | |
895 | if ((--p) >= lowerPos) { | |
896 | result.append(u'.'); | |
897 | for (; p >= lowerPos; p--) { | |
898 | result.append(u'0' + getDigitPos(p)); | |
899 | } | |
900 | } | |
901 | result.append(u'E'); | |
902 | int32_t _scale = upperPos + scale; | |
3d1f044b A |
903 | if (_scale == INT32_MIN) { |
904 | result.append({u"-2147483648", -1}); | |
905 | return result; | |
906 | } else if (_scale < 0) { | |
0f5d89e8 A |
907 | _scale *= -1; |
908 | result.append(u'-'); | |
909 | } else { | |
910 | result.append(u'+'); | |
911 | } | |
912 | if (_scale == 0) { | |
913 | result.append(u'0'); | |
914 | } | |
915 | int32_t insertIndex = result.length(); | |
916 | while (_scale > 0) { | |
917 | std::div_t res = std::div(_scale, 10); | |
918 | result.insert(insertIndex, u'0' + res.rem); | |
919 | _scale = res.quot; | |
920 | } | |
921 | return result; | |
922 | } | |
923 | ||
924 | //////////////////////////////////////////////////// | |
925 | /// End of DecimalQuantity_AbstractBCD.java /// | |
926 | /// Start of DecimalQuantity_DualStorageBCD.java /// | |
927 | //////////////////////////////////////////////////// | |
928 | ||
929 | int8_t DecimalQuantity::getDigitPos(int32_t position) const { | |
930 | if (usingBytes) { | |
931 | if (position < 0 || position >= precision) { return 0; } | |
932 | return fBCD.bcdBytes.ptr[position]; | |
933 | } else { | |
934 | if (position < 0 || position >= 16) { return 0; } | |
935 | return (int8_t) ((fBCD.bcdLong >> (position * 4)) & 0xf); | |
936 | } | |
937 | } | |
938 | ||
939 | void DecimalQuantity::setDigitPos(int32_t position, int8_t value) { | |
940 | U_ASSERT(position >= 0); | |
941 | if (usingBytes) { | |
942 | ensureCapacity(position + 1); | |
943 | fBCD.bcdBytes.ptr[position] = value; | |
944 | } else if (position >= 16) { | |
945 | switchStorage(); | |
946 | ensureCapacity(position + 1); | |
947 | fBCD.bcdBytes.ptr[position] = value; | |
948 | } else { | |
949 | int shift = position * 4; | |
950 | fBCD.bcdLong = (fBCD.bcdLong & ~(0xfL << shift)) | ((long) value << shift); | |
951 | } | |
952 | } | |
953 | ||
954 | void DecimalQuantity::shiftLeft(int32_t numDigits) { | |
955 | if (!usingBytes && precision + numDigits > 16) { | |
956 | switchStorage(); | |
957 | } | |
958 | if (usingBytes) { | |
959 | ensureCapacity(precision + numDigits); | |
960 | int i = precision + numDigits - 1; | |
961 | for (; i >= numDigits; i--) { | |
962 | fBCD.bcdBytes.ptr[i] = fBCD.bcdBytes.ptr[i - numDigits]; | |
963 | } | |
964 | for (; i >= 0; i--) { | |
965 | fBCD.bcdBytes.ptr[i] = 0; | |
966 | } | |
967 | } else { | |
968 | fBCD.bcdLong <<= (numDigits * 4); | |
969 | } | |
970 | scale -= numDigits; | |
971 | precision += numDigits; | |
972 | } | |
973 | ||
974 | void DecimalQuantity::shiftRight(int32_t numDigits) { | |
975 | if (usingBytes) { | |
976 | int i = 0; | |
977 | for (; i < precision - numDigits; i++) { | |
978 | fBCD.bcdBytes.ptr[i] = fBCD.bcdBytes.ptr[i + numDigits]; | |
979 | } | |
980 | for (; i < precision; i++) { | |
981 | fBCD.bcdBytes.ptr[i] = 0; | |
982 | } | |
983 | } else { | |
984 | fBCD.bcdLong >>= (numDigits * 4); | |
985 | } | |
986 | scale += numDigits; | |
987 | precision -= numDigits; | |
988 | } | |
989 | ||
3d1f044b A |
990 | void DecimalQuantity::popFromLeft(int32_t numDigits) { |
991 | U_ASSERT(numDigits <= precision); | |
992 | if (usingBytes) { | |
993 | int i = precision - 1; | |
994 | for (; i >= precision - numDigits; i--) { | |
995 | fBCD.bcdBytes.ptr[i] = 0; | |
996 | } | |
997 | } else { | |
998 | fBCD.bcdLong &= (static_cast<uint64_t>(1) << ((precision - numDigits) * 4)) - 1; | |
999 | } | |
1000 | precision -= numDigits; | |
1001 | } | |
1002 | ||
0f5d89e8 A |
1003 | void DecimalQuantity::setBcdToZero() { |
1004 | if (usingBytes) { | |
1005 | uprv_free(fBCD.bcdBytes.ptr); | |
1006 | fBCD.bcdBytes.ptr = nullptr; | |
1007 | usingBytes = false; | |
1008 | } | |
1009 | fBCD.bcdLong = 0L; | |
1010 | scale = 0; | |
1011 | precision = 0; | |
1012 | isApproximate = false; | |
1013 | origDouble = 0; | |
1014 | origDelta = 0; | |
1015 | } | |
1016 | ||
1017 | void DecimalQuantity::readIntToBcd(int32_t n) { | |
1018 | U_ASSERT(n != 0); | |
1019 | // ints always fit inside the long implementation. | |
1020 | uint64_t result = 0L; | |
1021 | int i = 16; | |
1022 | for (; n != 0; n /= 10, i--) { | |
1023 | result = (result >> 4) + ((static_cast<uint64_t>(n) % 10) << 60); | |
1024 | } | |
1025 | U_ASSERT(!usingBytes); | |
1026 | fBCD.bcdLong = result >> (i * 4); | |
1027 | scale = 0; | |
1028 | precision = 16 - i; | |
1029 | } | |
1030 | ||
1031 | void DecimalQuantity::readLongToBcd(int64_t n) { | |
1032 | U_ASSERT(n != 0); | |
1033 | if (n >= 10000000000000000L) { | |
1034 | ensureCapacity(); | |
1035 | int i = 0; | |
1036 | for (; n != 0L; n /= 10L, i++) { | |
1037 | fBCD.bcdBytes.ptr[i] = static_cast<int8_t>(n % 10); | |
1038 | } | |
1039 | U_ASSERT(usingBytes); | |
1040 | scale = 0; | |
1041 | precision = i; | |
1042 | } else { | |
1043 | uint64_t result = 0L; | |
1044 | int i = 16; | |
1045 | for (; n != 0L; n /= 10L, i--) { | |
1046 | result = (result >> 4) + ((n % 10) << 60); | |
1047 | } | |
1048 | U_ASSERT(i >= 0); | |
1049 | U_ASSERT(!usingBytes); | |
1050 | fBCD.bcdLong = result >> (i * 4); | |
1051 | scale = 0; | |
1052 | precision = 16 - i; | |
1053 | } | |
1054 | } | |
1055 | ||
1056 | void DecimalQuantity::readDecNumberToBcd(const DecNum& decnum) { | |
1057 | const decNumber* dn = decnum.getRawDecNumber(); | |
1058 | if (dn->digits > 16) { | |
1059 | ensureCapacity(dn->digits); | |
1060 | for (int32_t i = 0; i < dn->digits; i++) { | |
1061 | fBCD.bcdBytes.ptr[i] = dn->lsu[i]; | |
1062 | } | |
1063 | } else { | |
1064 | uint64_t result = 0L; | |
1065 | for (int32_t i = 0; i < dn->digits; i++) { | |
1066 | result |= static_cast<uint64_t>(dn->lsu[i]) << (4 * i); | |
1067 | } | |
1068 | fBCD.bcdLong = result; | |
1069 | } | |
1070 | scale = dn->exponent; | |
1071 | precision = dn->digits; | |
1072 | } | |
1073 | ||
1074 | void DecimalQuantity::readDoubleConversionToBcd( | |
1075 | const char* buffer, int32_t length, int32_t point) { | |
1076 | // NOTE: Despite the fact that double-conversion's API is called | |
1077 | // "DoubleToAscii", they actually use '0' (as opposed to u8'0'). | |
1078 | if (length > 16) { | |
1079 | ensureCapacity(length); | |
1080 | for (int32_t i = 0; i < length; i++) { | |
1081 | fBCD.bcdBytes.ptr[i] = buffer[length-i-1] - '0'; | |
1082 | } | |
1083 | } else { | |
1084 | uint64_t result = 0L; | |
1085 | for (int32_t i = 0; i < length; i++) { | |
1086 | result |= static_cast<uint64_t>(buffer[length-i-1] - '0') << (4 * i); | |
1087 | } | |
1088 | fBCD.bcdLong = result; | |
1089 | } | |
1090 | scale = point - length; | |
1091 | precision = length; | |
1092 | } | |
1093 | ||
1094 | void DecimalQuantity::compact() { | |
1095 | if (usingBytes) { | |
1096 | int32_t delta = 0; | |
1097 | for (; delta < precision && fBCD.bcdBytes.ptr[delta] == 0; delta++); | |
1098 | if (delta == precision) { | |
1099 | // Number is zero | |
1100 | setBcdToZero(); | |
1101 | return; | |
1102 | } else { | |
1103 | // Remove trailing zeros | |
1104 | shiftRight(delta); | |
1105 | } | |
1106 | ||
1107 | // Compute precision | |
1108 | int32_t leading = precision - 1; | |
1109 | for (; leading >= 0 && fBCD.bcdBytes.ptr[leading] == 0; leading--); | |
1110 | precision = leading + 1; | |
1111 | ||
1112 | // Switch storage mechanism if possible | |
1113 | if (precision <= 16) { | |
1114 | switchStorage(); | |
1115 | } | |
1116 | ||
1117 | } else { | |
1118 | if (fBCD.bcdLong == 0L) { | |
1119 | // Number is zero | |
1120 | setBcdToZero(); | |
1121 | return; | |
1122 | } | |
1123 | ||
1124 | // Compact the number (remove trailing zeros) | |
1125 | // TODO: Use a more efficient algorithm here and below. There is a logarithmic one. | |
1126 | int32_t delta = 0; | |
1127 | for (; delta < precision && getDigitPos(delta) == 0; delta++); | |
1128 | fBCD.bcdLong >>= delta * 4; | |
1129 | scale += delta; | |
1130 | ||
1131 | // Compute precision | |
1132 | int32_t leading = precision - 1; | |
1133 | for (; leading >= 0 && getDigitPos(leading) == 0; leading--); | |
1134 | precision = leading + 1; | |
1135 | } | |
1136 | } | |
1137 | ||
1138 | void DecimalQuantity::ensureCapacity() { | |
1139 | ensureCapacity(40); | |
1140 | } | |
1141 | ||
1142 | void DecimalQuantity::ensureCapacity(int32_t capacity) { | |
1143 | if (capacity == 0) { return; } | |
1144 | int32_t oldCapacity = usingBytes ? fBCD.bcdBytes.len : 0; | |
1145 | if (!usingBytes) { | |
1146 | // TODO: There is nothing being done to check for memory allocation failures. | |
1147 | // TODO: Consider indexing by nybbles instead of bytes in C++, so that we can | |
1148 | // make these arrays half the size. | |
1149 | fBCD.bcdBytes.ptr = static_cast<int8_t*>(uprv_malloc(capacity * sizeof(int8_t))); | |
1150 | fBCD.bcdBytes.len = capacity; | |
1151 | // Initialize the byte array to zeros (this is done automatically in Java) | |
1152 | uprv_memset(fBCD.bcdBytes.ptr, 0, capacity * sizeof(int8_t)); | |
1153 | } else if (oldCapacity < capacity) { | |
1154 | auto bcd1 = static_cast<int8_t*>(uprv_malloc(capacity * 2 * sizeof(int8_t))); | |
1155 | uprv_memcpy(bcd1, fBCD.bcdBytes.ptr, oldCapacity * sizeof(int8_t)); | |
1156 | // Initialize the rest of the byte array to zeros (this is done automatically in Java) | |
1157 | uprv_memset(bcd1 + oldCapacity, 0, (capacity - oldCapacity) * sizeof(int8_t)); | |
1158 | uprv_free(fBCD.bcdBytes.ptr); | |
1159 | fBCD.bcdBytes.ptr = bcd1; | |
1160 | fBCD.bcdBytes.len = capacity * 2; | |
1161 | } | |
1162 | usingBytes = true; | |
1163 | } | |
1164 | ||
1165 | void DecimalQuantity::switchStorage() { | |
1166 | if (usingBytes) { | |
1167 | // Change from bytes to long | |
1168 | uint64_t bcdLong = 0L; | |
1169 | for (int i = precision - 1; i >= 0; i--) { | |
1170 | bcdLong <<= 4; | |
1171 | bcdLong |= fBCD.bcdBytes.ptr[i]; | |
1172 | } | |
1173 | uprv_free(fBCD.bcdBytes.ptr); | |
1174 | fBCD.bcdBytes.ptr = nullptr; | |
1175 | fBCD.bcdLong = bcdLong; | |
1176 | usingBytes = false; | |
1177 | } else { | |
1178 | // Change from long to bytes | |
1179 | // Copy the long into a local variable since it will get munged when we allocate the bytes | |
1180 | uint64_t bcdLong = fBCD.bcdLong; | |
1181 | ensureCapacity(); | |
1182 | for (int i = 0; i < precision; i++) { | |
1183 | fBCD.bcdBytes.ptr[i] = static_cast<int8_t>(bcdLong & 0xf); | |
1184 | bcdLong >>= 4; | |
1185 | } | |
1186 | U_ASSERT(usingBytes); | |
1187 | } | |
1188 | } | |
1189 | ||
1190 | void DecimalQuantity::copyBcdFrom(const DecimalQuantity &other) { | |
1191 | setBcdToZero(); | |
1192 | if (other.usingBytes) { | |
1193 | ensureCapacity(other.precision); | |
1194 | uprv_memcpy(fBCD.bcdBytes.ptr, other.fBCD.bcdBytes.ptr, other.precision * sizeof(int8_t)); | |
1195 | } else { | |
1196 | fBCD.bcdLong = other.fBCD.bcdLong; | |
1197 | } | |
1198 | } | |
1199 | ||
1200 | void DecimalQuantity::moveBcdFrom(DecimalQuantity &other) { | |
1201 | setBcdToZero(); | |
1202 | if (other.usingBytes) { | |
1203 | usingBytes = true; | |
1204 | fBCD.bcdBytes.ptr = other.fBCD.bcdBytes.ptr; | |
1205 | fBCD.bcdBytes.len = other.fBCD.bcdBytes.len; | |
1206 | // Take ownership away from the old instance: | |
1207 | other.fBCD.bcdBytes.ptr = nullptr; | |
1208 | other.usingBytes = false; | |
1209 | } else { | |
1210 | fBCD.bcdLong = other.fBCD.bcdLong; | |
1211 | } | |
1212 | } | |
1213 | ||
1214 | const char16_t* DecimalQuantity::checkHealth() const { | |
1215 | if (usingBytes) { | |
1216 | if (precision == 0) { return u"Zero precision but we are in byte mode"; } | |
1217 | int32_t capacity = fBCD.bcdBytes.len; | |
1218 | if (precision > capacity) { return u"Precision exceeds length of byte array"; } | |
1219 | if (getDigitPos(precision - 1) == 0) { return u"Most significant digit is zero in byte mode"; } | |
1220 | if (getDigitPos(0) == 0) { return u"Least significant digit is zero in long mode"; } | |
1221 | for (int i = 0; i < precision; i++) { | |
1222 | if (getDigitPos(i) >= 10) { return u"Digit exceeding 10 in byte array"; } | |
1223 | if (getDigitPos(i) < 0) { return u"Digit below 0 in byte array"; } | |
1224 | } | |
1225 | for (int i = precision; i < capacity; i++) { | |
1226 | if (getDigitPos(i) != 0) { return u"Nonzero digits outside of range in byte array"; } | |
1227 | } | |
1228 | } else { | |
1229 | if (precision == 0 && fBCD.bcdLong != 0) { | |
1230 | return u"Value in bcdLong even though precision is zero"; | |
1231 | } | |
1232 | if (precision > 16) { return u"Precision exceeds length of long"; } | |
1233 | if (precision != 0 && getDigitPos(precision - 1) == 0) { | |
1234 | return u"Most significant digit is zero in long mode"; | |
1235 | } | |
1236 | if (precision != 0 && getDigitPos(0) == 0) { | |
1237 | return u"Least significant digit is zero in long mode"; | |
1238 | } | |
1239 | for (int i = 0; i < precision; i++) { | |
1240 | if (getDigitPos(i) >= 10) { return u"Digit exceeding 10 in long"; } | |
1241 | if (getDigitPos(i) < 0) { return u"Digit below 0 in long (?!)"; } | |
1242 | } | |
1243 | for (int i = precision; i < 16; i++) { | |
1244 | if (getDigitPos(i) != 0) { return u"Nonzero digits outside of range in long"; } | |
1245 | } | |
1246 | } | |
1247 | ||
1248 | // No error | |
1249 | return nullptr; | |
1250 | } | |
1251 | ||
1252 | bool DecimalQuantity::operator==(const DecimalQuantity& other) const { | |
3d1f044b A |
1253 | bool basicEquals = |
1254 | scale == other.scale | |
1255 | && precision == other.precision | |
1256 | && flags == other.flags | |
1257 | && lReqPos == other.lReqPos | |
1258 | && rReqPos == other.rReqPos | |
1259 | && isApproximate == other.isApproximate; | |
1260 | if (!basicEquals) { | |
1261 | return false; | |
1262 | } | |
1263 | ||
1264 | if (precision == 0) { | |
1265 | return true; | |
1266 | } else if (isApproximate) { | |
1267 | return origDouble == other.origDouble && origDelta == other.origDelta; | |
1268 | } else { | |
1269 | for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) { | |
1270 | if (getDigit(m) != other.getDigit(m)) { | |
1271 | return false; | |
1272 | } | |
1273 | } | |
1274 | return true; | |
1275 | } | |
0f5d89e8 A |
1276 | } |
1277 | ||
1278 | UnicodeString DecimalQuantity::toString() const { | |
1279 | MaybeStackArray<char, 30> digits(precision + 1); | |
1280 | for (int32_t i = 0; i < precision; i++) { | |
1281 | digits[i] = getDigitPos(precision - i - 1) + '0'; | |
1282 | } | |
1283 | digits[precision] = 0; // terminate buffer | |
1284 | char buffer8[100]; | |
1285 | snprintf( | |
1286 | buffer8, | |
1287 | sizeof(buffer8), | |
3d1f044b | 1288 | "<DecimalQuantity %d:%d %s %s%s%s%d>", |
0f5d89e8 A |
1289 | lReqPos, |
1290 | rReqPos, | |
0f5d89e8 A |
1291 | (usingBytes ? "bytes" : "long"), |
1292 | (isNegative() ? "-" : ""), | |
1293 | (precision == 0 ? "0" : digits.getAlias()), | |
1294 | "E", | |
1295 | scale); | |
1296 | return UnicodeString(buffer8, -1, US_INV); | |
1297 | } | |
1298 | ||
1299 | #endif /* #if !UCONFIG_NO_FORMATTING */ |