]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | /* |
2 | ********************************************************************** | |
b331163b | 3 | * Copyright (C) 1997-2015, International Business Machines |
b75a7d8f A |
4 | * Corporation and others. All Rights Reserved. |
5 | ********************************************************************** | |
6 | * | |
7 | * File DIGITLST.CPP | |
8 | * | |
9 | * Modification History: | |
10 | * | |
11 | * Date Name Description | |
12 | * 03/21/97 clhuang Converted from java. | |
13 | * 03/21/97 clhuang Implemented with new APIs. | |
14 | * 03/27/97 helena Updated to pass the simple test after code review. | |
15 | * 03/31/97 aliu Moved isLONG_MIN to here, and fixed it. | |
16 | * 04/15/97 aliu Changed MAX_COUNT to DBL_DIG. Changed Digit to char. | |
17 | * Reworked representation by replacing fDecimalAt | |
18 | * with fExponent. | |
19 | * 04/16/97 aliu Rewrote set() and getDouble() to use sprintf/atof | |
20 | * to do digit conversion. | |
21 | * 09/09/97 aliu Modified for exponential notation support. | |
22 | * 08/02/98 stephen Added nearest/even rounding | |
23 | * Fixed bug in fitsIntoLong | |
24 | ****************************************************************************** | |
25 | */ | |
26 | ||
b75a7d8f | 27 | #include "digitlst.h" |
46f4442e A |
28 | |
29 | #if !UCONFIG_NO_FORMATTING | |
30 | #include "unicode/putil.h" | |
729e4ab9 A |
31 | #include "charstr.h" |
32 | #include "cmemory.h" | |
374ca955 | 33 | #include "cstring.h" |
4388f060 | 34 | #include "mutex.h" |
374ca955 | 35 | #include "putilimp.h" |
46f4442e | 36 | #include "uassert.h" |
2ca993e8 | 37 | #include "digitinterval.h" |
b75a7d8f A |
38 | #include <stdlib.h> |
39 | #include <limits.h> | |
40 | #include <string.h> | |
41 | #include <stdio.h> | |
729e4ab9 A |
42 | #include <limits> |
43 | ||
b75a7d8f A |
44 | // *************************************************************************** |
45 | // class DigitList | |
729e4ab9 A |
46 | // A wrapper onto decNumber. |
47 | // Used to be standalone. | |
b75a7d8f A |
48 | // *************************************************************************** |
49 | ||
50 | /** | |
729e4ab9 A |
51 | * This is the zero digit. The base for the digits returned by getDigit() |
52 | * Note that it is the platform invariant digit, and is not Unicode. | |
b75a7d8f A |
53 | */ |
54 | #define kZero '0' | |
55 | ||
b75a7d8f A |
56 | |
57 | /* Only for 32 bit numbers. Ignore the negative sign. */ | |
51004dcb A |
58 | //static const char LONG_MIN_REP[] = "2147483648"; |
59 | //static const char I64_MIN_REP[] = "9223372036854775808"; | |
374ca955 | 60 | |
b75a7d8f A |
61 | |
62 | U_NAMESPACE_BEGIN | |
63 | ||
b75a7d8f A |
64 | // ------------------------------------- |
65 | // default constructor | |
66 | ||
67 | DigitList::DigitList() | |
68 | { | |
729e4ab9 A |
69 | uprv_decContextDefault(&fContext, DEC_INIT_BASE); |
70 | fContext.traps = 0; | |
71 | uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN); | |
72 | fContext.digits = fStorage.getCapacity(); | |
73 | ||
74 | fDecNumber = fStorage.getAlias(); | |
75 | uprv_decNumberZero(fDecNumber); | |
76 | ||
51004dcb | 77 | internalSetDouble(0.0); |
b75a7d8f A |
78 | } |
79 | ||
80 | // ------------------------------------- | |
81 | ||
82 | DigitList::~DigitList() | |
83 | { | |
84 | } | |
85 | ||
86 | // ------------------------------------- | |
87 | // copy constructor | |
88 | ||
89 | DigitList::DigitList(const DigitList &other) | |
90 | { | |
729e4ab9 | 91 | fDecNumber = fStorage.getAlias(); |
b75a7d8f A |
92 | *this = other; |
93 | } | |
94 | ||
729e4ab9 | 95 | |
b75a7d8f A |
96 | // ------------------------------------- |
97 | // assignment operator | |
98 | ||
99 | DigitList& | |
100 | DigitList::operator=(const DigitList& other) | |
101 | { | |
102 | if (this != &other) | |
103 | { | |
729e4ab9 A |
104 | uprv_memcpy(&fContext, &other.fContext, sizeof(decContext)); |
105 | ||
106 | if (other.fStorage.getCapacity() > fStorage.getCapacity()) { | |
107 | fDecNumber = fStorage.resize(other.fStorage.getCapacity()); | |
108 | } | |
109 | // Always reset the fContext.digits, even if fDecNumber was not reallocated, | |
110 | // because above we copied fContext from other.fContext. | |
111 | fContext.digits = fStorage.getCapacity(); | |
112 | uprv_decNumberCopy(fDecNumber, other.fDecNumber); | |
113 | ||
4388f060 A |
114 | { |
115 | // fDouble is lazily created and cached. | |
116 | // Avoid potential races with that happening with other.fDouble | |
117 | // while we are doing the assignment. | |
118 | Mutex mutex; | |
51004dcb A |
119 | |
120 | if(other.fHave==kDouble) { | |
2ca993e8 | 121 | fUnion.fDouble = other.fUnion.fDouble; |
51004dcb A |
122 | } |
123 | fHave = other.fHave; | |
4388f060 | 124 | } |
b75a7d8f A |
125 | } |
126 | return *this; | |
127 | } | |
128 | ||
129 | // ------------------------------------- | |
729e4ab9 | 130 | // operator == (does not exactly match the old DigitList function) |
b75a7d8f A |
131 | |
132 | UBool | |
133 | DigitList::operator==(const DigitList& that) const | |
134 | { | |
729e4ab9 A |
135 | if (this == &that) { |
136 | return TRUE; | |
137 | } | |
138 | decNumber n; // Has space for only a none digit value. | |
139 | decContext c; | |
140 | uprv_decContextDefault(&c, DEC_INIT_BASE); | |
141 | c.digits = 1; | |
142 | c.traps = 0; | |
143 | ||
144 | uprv_decNumberCompare(&n, this->fDecNumber, that.fDecNumber, &c); | |
145 | UBool result = decNumberIsZero(&n); | |
146 | return result; | |
b75a7d8f A |
147 | } |
148 | ||
149 | // ------------------------------------- | |
729e4ab9 A |
150 | // comparison function. Returns |
151 | // Not Comparable : -2 | |
152 | // < : -1 | |
153 | // == : 0 | |
154 | // > : +1 | |
155 | int32_t DigitList::compare(const DigitList &other) { | |
156 | decNumber result; | |
157 | int32_t savedDigits = fContext.digits; | |
158 | fContext.digits = 1; | |
159 | uprv_decNumberCompare(&result, this->fDecNumber, other.fDecNumber, &fContext); | |
160 | fContext.digits = savedDigits; | |
161 | if (decNumberIsZero(&result)) { | |
162 | return 0; | |
163 | } else if (decNumberIsSpecial(&result)) { | |
164 | return -2; | |
165 | } else if (result.bits & DECNEG) { | |
166 | return -1; | |
167 | } else { | |
168 | return 1; | |
169 | } | |
170 | } | |
b75a7d8f | 171 | |
b75a7d8f | 172 | |
729e4ab9 A |
173 | // ------------------------------------- |
174 | // Reduce - remove trailing zero digits. | |
175 | void | |
176 | DigitList::reduce() { | |
177 | uprv_decNumberReduce(fDecNumber, fDecNumber, &fContext); | |
b75a7d8f A |
178 | } |
179 | ||
180 | ||
729e4ab9 A |
181 | // ------------------------------------- |
182 | // trim - remove trailing fraction zero digits. | |
183 | void | |
184 | DigitList::trim() { | |
185 | uprv_decNumberTrim(fDecNumber); | |
186 | } | |
b75a7d8f A |
187 | |
188 | // ------------------------------------- | |
729e4ab9 A |
189 | // Resets the digit list; sets all the digits to zero. |
190 | ||
191 | void | |
192 | DigitList::clear() | |
193 | { | |
194 | uprv_decNumberZero(fDecNumber); | |
195 | uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN); | |
51004dcb | 196 | internalSetDouble(0.0); |
729e4ab9 A |
197 | } |
198 | ||
b75a7d8f A |
199 | |
200 | /** | |
729e4ab9 | 201 | * Formats a int64_t number into a base 10 string representation, and NULL terminates it. |
b75a7d8f | 202 | * @param number The number to format |
729e4ab9 A |
203 | * @param outputStr The string to output to. Must be at least MAX_DIGITS+2 in length (21), |
204 | * to hold the longest int64_t value. | |
b75a7d8f A |
205 | * @return the number of digits written, not including the sign. |
206 | */ | |
207 | static int32_t | |
729e4ab9 A |
208 | formatBase10(int64_t number, char *outputStr) { |
209 | // The number is output backwards, starting with the LSD. | |
210 | // Fill the buffer from the far end. After the number is complete, | |
211 | // slide the string contents to the front. | |
212 | ||
213 | const int32_t MAX_IDX = MAX_DIGITS+2; | |
214 | int32_t destIdx = MAX_IDX; | |
215 | outputStr[--destIdx] = 0; | |
b75a7d8f | 216 | |
729e4ab9 A |
217 | int64_t n = number; |
218 | if (number < 0) { // Negative numbers are slightly larger than a postive | |
219 | outputStr[--destIdx] = (char)(-(n % 10) + kZero); | |
220 | n /= -10; | |
b75a7d8f | 221 | } |
729e4ab9 A |
222 | do { |
223 | outputStr[--destIdx] = (char)(n % 10 + kZero); | |
224 | n /= 10; | |
225 | } while (n > 0); | |
226 | ||
227 | if (number < 0) { | |
228 | outputStr[--destIdx] = '-'; | |
b75a7d8f A |
229 | } |
230 | ||
729e4ab9 A |
231 | // Slide the number to the start of the output str |
232 | U_ASSERT(destIdx >= 0); | |
233 | int32_t length = MAX_IDX - destIdx; | |
234 | uprv_memmove(outputStr, outputStr+MAX_IDX-length, length); | |
b75a7d8f | 235 | |
729e4ab9 A |
236 | return length; |
237 | } | |
238 | ||
239 | ||
240 | // ------------------------------------- | |
4388f060 A |
241 | // |
242 | // setRoundingMode() | |
243 | // For most modes, the meaning and names are the same between the decNumber library | |
244 | // (which DigitList follows) and the ICU Formatting Rounding Mode values. | |
245 | // The flag constants are different, however. | |
246 | // | |
247 | // Note that ICU's kRoundingUnnecessary is not implemented directly by DigitList. | |
248 | // This mode, inherited from Java, means that numbers that would not format exactly | |
249 | // will return an error when formatting is attempted. | |
729e4ab9 A |
250 | |
251 | void | |
252 | DigitList::setRoundingMode(DecimalFormat::ERoundingMode m) { | |
253 | enum rounding r; | |
254 | ||
255 | switch (m) { | |
256 | case DecimalFormat::kRoundCeiling: r = DEC_ROUND_CEILING; break; | |
257 | case DecimalFormat::kRoundFloor: r = DEC_ROUND_FLOOR; break; | |
258 | case DecimalFormat::kRoundDown: r = DEC_ROUND_DOWN; break; | |
259 | case DecimalFormat::kRoundUp: r = DEC_ROUND_UP; break; | |
260 | case DecimalFormat::kRoundHalfEven: r = DEC_ROUND_HALF_EVEN; break; | |
261 | case DecimalFormat::kRoundHalfDown: r = DEC_ROUND_HALF_DOWN; break; | |
262 | case DecimalFormat::kRoundHalfUp: r = DEC_ROUND_HALF_UP; break; | |
4388f060 | 263 | case DecimalFormat::kRoundUnnecessary: r = DEC_ROUND_HALF_EVEN; break; |
729e4ab9 A |
264 | default: |
265 | // TODO: how to report the problem? | |
266 | // Leave existing mode unchanged. | |
267 | r = uprv_decContextGetRounding(&fContext); | |
b75a7d8f | 268 | } |
729e4ab9 A |
269 | uprv_decContextSetRounding(&fContext, r); |
270 | ||
271 | } | |
272 | ||
273 | ||
274 | // ------------------------------------- | |
275 | ||
276 | void | |
277 | DigitList::setPositive(UBool s) { | |
278 | if (s) { | |
279 | fDecNumber->bits &= ~DECNEG; | |
280 | } else { | |
281 | fDecNumber->bits |= DECNEG; | |
b75a7d8f | 282 | } |
51004dcb | 283 | internalClear(); |
729e4ab9 A |
284 | } |
285 | // ------------------------------------- | |
286 | ||
287 | void | |
288 | DigitList::setDecimalAt(int32_t d) { | |
289 | U_ASSERT((fDecNumber->bits & DECSPECIAL) == 0); // Not Infinity or NaN | |
290 | U_ASSERT(d-1>-999999999); | |
291 | U_ASSERT(d-1< 999999999); | |
292 | int32_t adjustedDigits = fDecNumber->digits; | |
293 | if (decNumberIsZero(fDecNumber)) { | |
294 | // Account for difference in how zero is represented between DigitList & decNumber. | |
295 | adjustedDigits = 0; | |
b75a7d8f | 296 | } |
729e4ab9 | 297 | fDecNumber->exponent = d - adjustedDigits; |
51004dcb | 298 | internalClear(); |
729e4ab9 | 299 | } |
b75a7d8f | 300 | |
729e4ab9 A |
301 | int32_t |
302 | DigitList::getDecimalAt() { | |
303 | U_ASSERT((fDecNumber->bits & DECSPECIAL) == 0); // Not Infinity or NaN | |
304 | if (decNumberIsZero(fDecNumber) || ((fDecNumber->bits & DECSPECIAL) != 0)) { | |
305 | return fDecNumber->exponent; // Exponent should be zero for these cases. | |
306 | } | |
307 | return fDecNumber->exponent + fDecNumber->digits; | |
308 | } | |
b75a7d8f | 309 | |
729e4ab9 A |
310 | void |
311 | DigitList::setCount(int32_t c) { | |
312 | U_ASSERT(c <= fContext.digits); | |
313 | if (c == 0) { | |
314 | // For a value of zero, DigitList sets all fields to zero, while | |
315 | // decNumber keeps one digit (with that digit being a zero) | |
316 | c = 1; | |
317 | fDecNumber->lsu[0] = 0; | |
b75a7d8f | 318 | } |
729e4ab9 | 319 | fDecNumber->digits = c; |
51004dcb | 320 | internalClear(); |
b75a7d8f A |
321 | } |
322 | ||
729e4ab9 A |
323 | int32_t |
324 | DigitList::getCount() const { | |
325 | if (decNumberIsZero(fDecNumber) && fDecNumber->exponent==0) { | |
326 | // The extra test for exponent==0 is needed because parsing sometimes appends | |
327 | // zero digits. It's bogus, decimalFormatter parsing needs to be cleaned up. | |
328 | return 0; | |
329 | } else { | |
330 | return fDecNumber->digits; | |
331 | } | |
332 | } | |
333 | ||
334 | void | |
335 | DigitList::setDigit(int32_t i, char v) { | |
336 | int32_t count = fDecNumber->digits; | |
337 | U_ASSERT(i<count); | |
338 | U_ASSERT(v>='0' && v<='9'); | |
339 | v &= 0x0f; | |
340 | fDecNumber->lsu[count-i-1] = v; | |
51004dcb | 341 | internalClear(); |
729e4ab9 A |
342 | } |
343 | ||
344 | char | |
345 | DigitList::getDigit(int32_t i) { | |
346 | int32_t count = fDecNumber->digits; | |
347 | U_ASSERT(i<count); | |
348 | return fDecNumber->lsu[count-i-1] + '0'; | |
349 | } | |
350 | ||
351 | // copied from DigitList::getDigit() | |
352 | uint8_t | |
353 | DigitList::getDigitValue(int32_t i) { | |
354 | int32_t count = fDecNumber->digits; | |
355 | U_ASSERT(i<count); | |
356 | return fDecNumber->lsu[count-i-1]; | |
357 | } | |
358 | ||
359 | // ------------------------------------- | |
360 | // Appends the digit to the digit list if it's not out of scope. | |
361 | // Ignores the digit, otherwise. | |
362 | // | |
363 | // This function is horribly inefficient to implement with decNumber because | |
364 | // the digits are stored least significant first, which requires moving all | |
365 | // existing digits down one to make space for the new one to be appended. | |
366 | // | |
367 | void | |
368 | DigitList::append(char digit) | |
369 | { | |
370 | U_ASSERT(digit>='0' && digit<='9'); | |
371 | // Ignore digits which exceed the precision we can represent | |
372 | // And don't fix for larger precision. Fix callers instead. | |
373 | if (decNumberIsZero(fDecNumber)) { | |
374 | // Zero needs to be special cased because of the difference in the way | |
375 | // that the old DigitList and decNumber represent it. | |
376 | // digit cout was zero for digitList, is one for decNumber | |
377 | fDecNumber->lsu[0] = digit & 0x0f; | |
378 | fDecNumber->digits = 1; | |
379 | fDecNumber->exponent--; // To match the old digit list implementation. | |
380 | } else { | |
381 | int32_t nDigits = fDecNumber->digits; | |
382 | if (nDigits < fContext.digits) { | |
383 | int i; | |
384 | for (i=nDigits; i>0; i--) { | |
385 | fDecNumber->lsu[i] = fDecNumber->lsu[i-1]; | |
386 | } | |
387 | fDecNumber->lsu[0] = digit & 0x0f; | |
388 | fDecNumber->digits++; | |
389 | // DigitList emulation - appending doesn't change the magnitude of existing | |
390 | // digits. With decNumber's decimal being after the | |
391 | // least signficant digit, we need to adjust the exponent. | |
392 | fDecNumber->exponent--; | |
393 | } | |
394 | } | |
51004dcb | 395 | internalClear(); |
729e4ab9 A |
396 | } |
397 | ||
2ca993e8 A |
398 | char DigitList::getStrtodDecimalSeparator() { |
399 | // TODO: maybe use andy's pthread once. | |
400 | static char gDecimal = 0; | |
401 | char result; | |
402 | { | |
403 | Mutex mutex; | |
404 | result = gDecimal;; | |
405 | if (result == 0) { | |
406 | // We need to know the decimal separator character that will be used with strtod(). | |
407 | // Depends on the C runtime global locale. | |
408 | // Most commonly is '.' | |
409 | // TODO: caching could fail if the global locale is changed on the fly. | |
410 | char rep[MAX_DIGITS]; | |
411 | sprintf(rep, "%+1.1f", 1.0); | |
412 | result = rep[2]; | |
413 | gDecimal = result;; | |
414 | } | |
415 | } | |
416 | return result; | |
417 | } | |
418 | ||
729e4ab9 A |
419 | // ------------------------------------- |
420 | ||
b75a7d8f | 421 | /** |
4388f060 | 422 | * Currently, getDouble() depends on strtod() to do its conversion. |
b75a7d8f A |
423 | * |
424 | * WARNING!! | |
425 | * This is an extremely costly function. ~1/2 of the conversion time | |
426 | * can be linked to this function. | |
427 | */ | |
428 | double | |
729e4ab9 | 429 | DigitList::getDouble() const |
b75a7d8f | 430 | { |
4388f060 A |
431 | static char gDecimal = 0; |
432 | char decimalSeparator; | |
433 | { | |
434 | Mutex mutex; | |
51004dcb A |
435 | if (fHave == kDouble) { |
436 | return fUnion.fDouble; | |
4388f060 A |
437 | } |
438 | decimalSeparator = gDecimal; | |
729e4ab9 | 439 | } |
729e4ab9 | 440 | |
4388f060 A |
441 | if (decimalSeparator == 0) { |
442 | // We need to know the decimal separator character that will be used with strtod(). | |
443 | // Depends on the C runtime global locale. | |
444 | // Most commonly is '.' | |
445 | // TODO: caching could fail if the global locale is changed on the fly. | |
729e4ab9 | 446 | char rep[MAX_DIGITS]; |
729e4ab9 | 447 | sprintf(rep, "%+1.1f", 1.0); |
4388f060 | 448 | decimalSeparator = rep[2]; |
b75a7d8f A |
449 | } |
450 | ||
4388f060 | 451 | double tDouble = 0.0; |
729e4ab9 | 452 | if (isZero()) { |
4388f060 | 453 | tDouble = 0.0; |
729e4ab9 | 454 | if (decNumberIsNegative(fDecNumber)) { |
4388f060 | 455 | tDouble /= -1; |
729e4ab9 A |
456 | } |
457 | } else if (isInfinite()) { | |
458 | if (std::numeric_limits<double>::has_infinity) { | |
4388f060 | 459 | tDouble = std::numeric_limits<double>::infinity(); |
729e4ab9 | 460 | } else { |
4388f060 | 461 | tDouble = std::numeric_limits<double>::max(); |
729e4ab9 A |
462 | } |
463 | if (!isPositive()) { | |
51004dcb | 464 | tDouble = -tDouble; //this was incorrectly "-fDouble" originally. |
729e4ab9 A |
465 | } |
466 | } else { | |
467 | MaybeStackArray<char, MAX_DBL_DIGITS+18> s; | |
468 | // Note: 14 is a magic constant from the decNumber library documentation, | |
469 | // the max number of extra characters beyond the number of digits | |
470 | // needed to represent the number in string form. Add a few more | |
471 | // for the additional digits we retain. | |
472 | ||
473 | // Round down to appx. double precision, if the number is longer than that. | |
474 | // Copy the number first, so that we don't modify the original. | |
475 | if (getCount() > MAX_DBL_DIGITS + 3) { | |
476 | DigitList numToConvert(*this); | |
477 | numToConvert.reduce(); // Removes any trailing zeros, so that digit count is good. | |
478 | numToConvert.round(MAX_DBL_DIGITS+3); | |
51004dcb | 479 | uprv_decNumberToString(numToConvert.fDecNumber, s.getAlias()); |
729e4ab9 A |
480 | // TODO: how many extra digits should be included for an accurate conversion? |
481 | } else { | |
51004dcb | 482 | uprv_decNumberToString(this->fDecNumber, s.getAlias()); |
729e4ab9 A |
483 | } |
484 | U_ASSERT(uprv_strlen(&s[0]) < MAX_DBL_DIGITS+18); | |
485 | ||
4388f060 | 486 | if (decimalSeparator != '.') { |
51004dcb | 487 | char *decimalPt = strchr(s.getAlias(), '.'); |
729e4ab9 | 488 | if (decimalPt != NULL) { |
4388f060 | 489 | *decimalPt = decimalSeparator; |
729e4ab9 A |
490 | } |
491 | } | |
492 | char *end = NULL; | |
51004dcb | 493 | tDouble = uprv_strtod(s.getAlias(), &end); |
4388f060 A |
494 | } |
495 | { | |
496 | Mutex mutex; | |
497 | DigitList *nonConstThis = const_cast<DigitList *>(this); | |
51004dcb | 498 | nonConstThis->internalSetDouble(tDouble); |
4388f060 | 499 | gDecimal = decimalSeparator; |
729e4ab9 | 500 | } |
4388f060 | 501 | return tDouble; |
b75a7d8f A |
502 | } |
503 | ||
504 | // ------------------------------------- | |
505 | ||
506 | /** | |
729e4ab9 A |
507 | * convert this number to an int32_t. Round if there is a fractional part. |
508 | * Return zero if the number cannot be represented. | |
b75a7d8f | 509 | */ |
374ca955 | 510 | int32_t DigitList::getLong() /*const*/ |
b75a7d8f | 511 | { |
729e4ab9 | 512 | int32_t result = 0; |
2ca993e8 | 513 | if (getUpperExponent() > 10) { |
729e4ab9 A |
514 | // Overflow, absolute value too big. |
515 | return result; | |
b75a7d8f | 516 | } |
729e4ab9 A |
517 | if (fDecNumber->exponent != 0) { |
518 | // Force to an integer, with zero exponent, rounding if necessary. | |
519 | // (decNumberToInt32 will only work if the exponent is exactly zero.) | |
520 | DigitList copy(*this); | |
521 | DigitList zero; | |
522 | uprv_decNumberQuantize(copy.fDecNumber, copy.fDecNumber, zero.fDecNumber, &fContext); | |
523 | result = uprv_decNumberToInt32(copy.fDecNumber, &fContext); | |
524 | } else { | |
525 | result = uprv_decNumberToInt32(fDecNumber, &fContext); | |
b75a7d8f | 526 | } |
729e4ab9 | 527 | return result; |
b75a7d8f A |
528 | } |
529 | ||
374ca955 A |
530 | |
531 | /** | |
729e4ab9 A |
532 | * convert this number to an int64_t. Truncate if there is a fractional part. |
533 | * Return zero if the number cannot be represented. | |
374ca955 | 534 | */ |
729e4ab9 | 535 | int64_t DigitList::getInt64() /*const*/ { |
2ca993e8 A |
536 | // TODO: fast conversion if fHave == fDouble |
537 | ||
729e4ab9 A |
538 | // Truncate if non-integer. |
539 | // Return 0 if out of range. | |
540 | // Range of in64_t is -9223372036854775808 to 9223372036854775807 (19 digits) | |
541 | // | |
2ca993e8 | 542 | if (getUpperExponent() > 19) { |
729e4ab9 A |
543 | // Overflow, absolute value too big. |
544 | return 0; | |
545 | } | |
374ca955 | 546 | |
729e4ab9 A |
547 | // The number of integer digits may differ from the number of digits stored |
548 | // in the decimal number. | |
549 | // for 12.345 numIntDigits = 2, number->digits = 5 | |
550 | // for 12E4 numIntDigits = 6, number->digits = 2 | |
551 | // The conversion ignores the fraction digits in the first case, | |
552 | // and fakes up extra zero digits in the second. | |
553 | // TODO: It would be faster to store a table of powers of ten to multiply by | |
554 | // instead of looping over zero digits, multiplying each time. | |
555 | ||
2ca993e8 | 556 | int32_t numIntDigits = getUpperExponent(); |
729e4ab9 A |
557 | uint64_t value = 0; |
558 | for (int32_t i = 0; i < numIntDigits; i++) { | |
559 | // Loop is iterating over digits starting with the most significant. | |
560 | // Numbers are stored with the least significant digit at index zero. | |
561 | int32_t digitIndex = fDecNumber->digits - i - 1; | |
562 | int32_t v = (digitIndex >= 0) ? fDecNumber->lsu[digitIndex] : 0; | |
563 | value = value * (uint64_t)10 + (uint64_t)v; | |
564 | } | |
374ca955 | 565 | |
729e4ab9 A |
566 | if (decNumberIsNegative(fDecNumber)) { |
567 | value = ~value; | |
568 | value += 1; | |
569 | } | |
570 | int64_t svalue = (int64_t)value; | |
571 | ||
572 | // Check overflow. It's convenient that the MSD is 9 only on overflow, the amount of | |
573 | // overflow can't wrap too far. The test will also fail -0, but | |
574 | // that does no harm; the right answer is 0. | |
575 | if (numIntDigits == 19) { | |
576 | if (( decNumberIsNegative(fDecNumber) && svalue>0) || | |
577 | (!decNumberIsNegative(fDecNumber) && svalue<0)) { | |
578 | svalue = 0; | |
374ca955 | 579 | } |
374ca955 | 580 | } |
729e4ab9 A |
581 | |
582 | return svalue; | |
583 | } | |
584 | ||
585 | ||
586 | /** | |
587 | * Return a string form of this number. | |
588 | * Format is as defined by the decNumber library, for interchange of | |
589 | * decimal numbers. | |
590 | */ | |
591 | void DigitList::getDecimal(CharString &str, UErrorCode &status) { | |
592 | if (U_FAILURE(status)) { | |
593 | return; | |
594 | } | |
374ca955 | 595 | |
729e4ab9 A |
596 | // A decimal number in string form can, worst case, be 14 characters longer |
597 | // than the number of digits. So says the decNumber library doc. | |
598 | int32_t maxLength = fDecNumber->digits + 14; | |
599 | int32_t capacity = 0; | |
600 | char *buffer = str.clear().getAppendBuffer(maxLength, 0, capacity, status); | |
601 | if (U_FAILURE(status)) { | |
602 | return; // Memory allocation error on growing the string. | |
374ca955 | 603 | } |
729e4ab9 A |
604 | U_ASSERT(capacity >= maxLength); |
605 | uprv_decNumberToString(this->fDecNumber, buffer); | |
606 | U_ASSERT((int32_t)uprv_strlen(buffer) <= maxLength); | |
607 | str.append(buffer, -1, status); | |
374ca955 A |
608 | } |
609 | ||
b75a7d8f | 610 | /** |
729e4ab9 A |
611 | * Return true if this is an integer value that can be held |
612 | * by an int32_t type. | |
b75a7d8f A |
613 | */ |
614 | UBool | |
374ca955 | 615 | DigitList::fitsIntoLong(UBool ignoreNegativeZero) /*const*/ |
b75a7d8f | 616 | { |
729e4ab9 A |
617 | if (decNumberIsSpecial(this->fDecNumber)) { |
618 | // NaN or Infinity. Does not fit in int32. | |
619 | return FALSE; | |
b75a7d8f | 620 | } |
729e4ab9 A |
621 | uprv_decNumberTrim(this->fDecNumber); |
622 | if (fDecNumber->exponent < 0) { | |
623 | // Number contains fraction digits. | |
b75a7d8f | 624 | return FALSE; |
729e4ab9 A |
625 | } |
626 | if (decNumberIsZero(this->fDecNumber) && !ignoreNegativeZero && | |
627 | (fDecNumber->bits & DECNEG) != 0) { | |
628 | // Negative Zero, not ingored. Cannot represent as a long. | |
629 | return FALSE; | |
630 | } | |
2ca993e8 | 631 | if (getUpperExponent() < 10) { |
729e4ab9 A |
632 | // The number is 9 or fewer digits. |
633 | // The max and min int32 are 10 digts, so this number fits. | |
634 | // This is the common case. | |
b75a7d8f | 635 | return TRUE; |
729e4ab9 | 636 | } |
b75a7d8f | 637 | |
729e4ab9 A |
638 | // TODO: Should cache these constants; construction is relatively costly. |
639 | // But not of huge consequence; they're only needed for 10 digit ints. | |
640 | UErrorCode status = U_ZERO_ERROR; | |
641 | DigitList min32; min32.set("-2147483648", status); | |
642 | if (this->compare(min32) < 0) { | |
643 | return FALSE; | |
644 | } | |
645 | DigitList max32; max32.set("2147483647", status); | |
646 | if (this->compare(max32) > 0) { | |
647 | return FALSE; | |
648 | } | |
649 | if (U_FAILURE(status)) { | |
650 | return FALSE; | |
b75a7d8f | 651 | } |
729e4ab9 A |
652 | return true; |
653 | } | |
b75a7d8f | 654 | |
b75a7d8f | 655 | |
b75a7d8f | 656 | |
374ca955 A |
657 | /** |
658 | * Return true if the number represented by this object can fit into | |
659 | * a long. | |
660 | */ | |
661 | UBool | |
662 | DigitList::fitsIntoInt64(UBool ignoreNegativeZero) /*const*/ | |
663 | { | |
729e4ab9 A |
664 | if (decNumberIsSpecial(this->fDecNumber)) { |
665 | // NaN or Infinity. Does not fit in int32. | |
666 | return FALSE; | |
374ca955 | 667 | } |
729e4ab9 A |
668 | uprv_decNumberTrim(this->fDecNumber); |
669 | if (fDecNumber->exponent < 0) { | |
670 | // Number contains fraction digits. | |
374ca955 | 671 | return FALSE; |
374ca955 | 672 | } |
729e4ab9 A |
673 | if (decNumberIsZero(this->fDecNumber) && !ignoreNegativeZero && |
674 | (fDecNumber->bits & DECNEG) != 0) { | |
675 | // Negative Zero, not ingored. Cannot represent as a long. | |
676 | return FALSE; | |
677 | } | |
2ca993e8 | 678 | if (getUpperExponent() < 19) { |
729e4ab9 A |
679 | // The number is 18 or fewer digits. |
680 | // The max and min int64 are 19 digts, so this number fits. | |
681 | // This is the common case. | |
374ca955 | 682 | return TRUE; |
729e4ab9 | 683 | } |
374ca955 | 684 | |
729e4ab9 A |
685 | // TODO: Should cache these constants; construction is relatively costly. |
686 | // But not of huge consequence; they're only needed for 19 digit ints. | |
687 | UErrorCode status = U_ZERO_ERROR; | |
688 | DigitList min64; min64.set("-9223372036854775808", status); | |
689 | if (this->compare(min64) < 0) { | |
690 | return FALSE; | |
691 | } | |
692 | DigitList max64; max64.set("9223372036854775807", status); | |
693 | if (this->compare(max64) > 0) { | |
694 | return FALSE; | |
695 | } | |
696 | if (U_FAILURE(status)) { | |
697 | return FALSE; | |
698 | } | |
699 | return true; | |
374ca955 A |
700 | } |
701 | ||
702 | ||
b75a7d8f A |
703 | // ------------------------------------- |
704 | ||
374ca955 | 705 | void |
729e4ab9 | 706 | DigitList::set(int32_t source) |
374ca955 | 707 | { |
729e4ab9 | 708 | set((int64_t)source); |
51004dcb | 709 | internalSetDouble(source); |
374ca955 A |
710 | } |
711 | ||
712 | // ------------------------------------- | |
b75a7d8f | 713 | /** |
51004dcb | 714 | * Set an int64, via decnumber |
b75a7d8f A |
715 | */ |
716 | void | |
729e4ab9 | 717 | DigitList::set(int64_t source) |
b75a7d8f | 718 | { |
729e4ab9 A |
719 | char str[MAX_DIGITS+2]; // Leave room for sign and trailing nul. |
720 | formatBase10(source, str); | |
721 | U_ASSERT(uprv_strlen(str) < sizeof(str)); | |
b75a7d8f | 722 | |
729e4ab9 | 723 | uprv_decNumberFromString(fDecNumber, str, &fContext); |
b331163b | 724 | internalSetDouble(static_cast<double>(source)); |
51004dcb A |
725 | } |
726 | ||
729e4ab9 A |
727 | // ------------------------------------- |
728 | /** | |
729 | * Set the DigitList from a decimal number string. | |
730 | * | |
731 | * The incoming string _must_ be nul terminated, even though it is arriving | |
732 | * as a StringPiece because that is what the decNumber library wants. | |
733 | * We can get away with this for an internal function; it would not | |
734 | * be acceptable for a public API. | |
735 | */ | |
736 | void | |
51004dcb | 737 | DigitList::set(const StringPiece &source, UErrorCode &status, uint32_t /*fastpathBits*/) { |
729e4ab9 A |
738 | if (U_FAILURE(status)) { |
739 | return; | |
740 | } | |
741 | ||
51004dcb A |
742 | #if 0 |
743 | if(fastpathBits==(kFastpathOk|kNoDecimal)) { | |
744 | int32_t size = source.size(); | |
745 | const char *data = source.data(); | |
746 | int64_t r = 0; | |
747 | int64_t m = 1; | |
748 | // fast parse | |
749 | while(size>0) { | |
750 | char ch = data[--size]; | |
751 | if(ch=='+') { | |
752 | break; | |
753 | } else if(ch=='-') { | |
754 | r = -r; | |
755 | break; | |
756 | } else { | |
757 | int64_t d = ch-'0'; | |
758 | //printf("CH[%d]=%c, %d, *=%d\n", size,ch, (int)d, (int)m); | |
759 | r+=(d)*m; | |
760 | m *= 10; | |
761 | } | |
762 | } | |
763 | //printf("R=%d\n", r); | |
764 | set(r); | |
765 | } else | |
766 | #endif | |
767 | { | |
768 | // Figure out a max number of digits to use during the conversion, and | |
769 | // resize the number up if necessary. | |
770 | int32_t numDigits = source.length(); | |
771 | if (numDigits > fContext.digits) { | |
729e4ab9 A |
772 | // fContext.digits == fStorage.getCapacity() |
773 | decNumber *t = fStorage.resize(numDigits, fStorage.getCapacity()); | |
774 | if (t == NULL) { | |
51004dcb A |
775 | status = U_MEMORY_ALLOCATION_ERROR; |
776 | return; | |
729e4ab9 A |
777 | } |
778 | fDecNumber = t; | |
779 | fContext.digits = numDigits; | |
51004dcb | 780 | } |
729e4ab9 | 781 | |
51004dcb A |
782 | fContext.status = 0; |
783 | uprv_decNumberFromString(fDecNumber, source.data(), &fContext); | |
784 | if ((fContext.status & DEC_Conversion_syntax) != 0) { | |
729e4ab9 | 785 | status = U_DECIMAL_NUMBER_SYNTAX_ERROR; |
51004dcb | 786 | } |
729e4ab9 | 787 | } |
51004dcb | 788 | internalClear(); |
729e4ab9 | 789 | } |
b75a7d8f A |
790 | |
791 | /** | |
792 | * Set the digit list to a representation of the given double value. | |
793 | * This method supports both fixed-point and exponential notation. | |
729e4ab9 | 794 | * @param source Value to be converted. |
b75a7d8f A |
795 | */ |
796 | void | |
729e4ab9 | 797 | DigitList::set(double source) |
b75a7d8f A |
798 | { |
799 | // for now, simple implementation; later, do proper IEEE stuff | |
800 | char rep[MAX_DIGITS + 8]; // Extra space for '+', '.', e+NNN, and '\0' (actually +8 is enough) | |
b75a7d8f | 801 | |
4388f060 A |
802 | // Generate a representation of the form /[+-][0-9].[0-9]+e[+-][0-9]+/ |
803 | // Can also generate /[+-]nan/ or /[+-]inf/ | |
804 | // TODO: Use something other than sprintf() here, since it's behavior is somewhat platform specific. | |
805 | // That is why infinity is special cased here. | |
806 | if (uprv_isInfinite(source)) { | |
807 | if (uprv_isNegativeInfinity(source)) { | |
808 | uprv_strcpy(rep,"-inf"); // Handle negative infinity | |
809 | } else { | |
810 | uprv_strcpy(rep,"inf"); | |
811 | } | |
812 | } else { | |
813 | sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source); | |
814 | } | |
729e4ab9 A |
815 | U_ASSERT(uprv_strlen(rep) < sizeof(rep)); |
816 | ||
4388f060 A |
817 | // uprv_decNumberFromString() will parse the string expecting '.' as a |
818 | // decimal separator, however sprintf() can use ',' in certain locales. | |
819 | // Overwrite a ',' with '.' here before proceeding. | |
820 | char *decimalSeparator = strchr(rep, ','); | |
821 | if (decimalSeparator != NULL) { | |
822 | *decimalSeparator = '.'; | |
823 | } | |
824 | ||
729e4ab9 A |
825 | // Create a decNumber from the string. |
826 | uprv_decNumberFromString(fDecNumber, rep, &fContext); | |
827 | uprv_decNumberTrim(fDecNumber); | |
51004dcb | 828 | internalSetDouble(source); |
729e4ab9 | 829 | } |
b75a7d8f | 830 | |
729e4ab9 | 831 | // ------------------------------------- |
b75a7d8f | 832 | |
729e4ab9 A |
833 | /* |
834 | * Multiply | |
835 | * The number will be expanded if need be to retain full precision. | |
836 | * In practice, for formatting, multiply is by 10, 100 or 1000, so more digits | |
837 | * will not be required for this use. | |
838 | */ | |
839 | void | |
840 | DigitList::mult(const DigitList &other, UErrorCode &status) { | |
2ca993e8 A |
841 | if (U_FAILURE(status)) { |
842 | return; | |
843 | } | |
729e4ab9 A |
844 | fContext.status = 0; |
845 | int32_t requiredDigits = this->digits() + other.digits(); | |
846 | if (requiredDigits > fContext.digits) { | |
847 | reduce(); // Remove any trailing zeros | |
848 | int32_t requiredDigits = this->digits() + other.digits(); | |
849 | ensureCapacity(requiredDigits, status); | |
b75a7d8f | 850 | } |
729e4ab9 | 851 | uprv_decNumberMultiply(fDecNumber, fDecNumber, other.fDecNumber, &fContext); |
51004dcb | 852 | internalClear(); |
729e4ab9 | 853 | } |
b75a7d8f | 854 | |
729e4ab9 | 855 | // ------------------------------------- |
b75a7d8f | 856 | |
729e4ab9 A |
857 | /* |
858 | * Divide | |
859 | * The number will _not_ be expanded for inexact results. | |
860 | * TODO: probably should expand some, for rounding increments that | |
861 | * could add a few digits, e.g. .25, but not expand arbitrarily. | |
862 | */ | |
863 | void | |
864 | DigitList::div(const DigitList &other, UErrorCode &status) { | |
865 | if (U_FAILURE(status)) { | |
b75a7d8f A |
866 | return; |
867 | } | |
729e4ab9 | 868 | uprv_decNumberDivide(fDecNumber, fDecNumber, other.fDecNumber, &fContext); |
51004dcb | 869 | internalClear(); |
729e4ab9 | 870 | } |
b75a7d8f | 871 | |
729e4ab9 | 872 | // ------------------------------------- |
b75a7d8f | 873 | |
729e4ab9 A |
874 | /* |
875 | * ensureCapacity. Grow the digit storage for the number if it's less than the requested | |
876 | * amount. Never reduce it. Available size is kept in fContext.digits. | |
877 | */ | |
878 | void | |
879 | DigitList::ensureCapacity(int32_t requestedCapacity, UErrorCode &status) { | |
880 | if (U_FAILURE(status)) { | |
881 | return; | |
b75a7d8f | 882 | } |
729e4ab9 A |
883 | if (requestedCapacity <= 0) { |
884 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
885 | return; | |
886 | } | |
887 | if (requestedCapacity > DEC_MAX_DIGITS) { | |
888 | // Don't report an error for requesting too much. | |
889 | // Arithemetic Results will be rounded to what can be supported. | |
890 | // At 999,999,999 max digits, exceeding the limit is not too likely! | |
891 | requestedCapacity = DEC_MAX_DIGITS; | |
892 | } | |
893 | if (requestedCapacity > fContext.digits) { | |
894 | decNumber *newBuffer = fStorage.resize(requestedCapacity, fStorage.getCapacity()); | |
895 | if (newBuffer == NULL) { | |
896 | status = U_MEMORY_ALLOCATION_ERROR; | |
897 | return; | |
898 | } | |
899 | fContext.digits = requestedCapacity; | |
900 | fDecNumber = newBuffer; | |
b75a7d8f A |
901 | } |
902 | } | |
903 | ||
904 | // ------------------------------------- | |
905 | ||
906 | /** | |
907 | * Round the representation to the given number of digits. | |
908 | * @param maximumDigits The maximum number of digits to be shown. | |
909 | * Upon return, count will be less than or equal to maximumDigits. | |
910 | */ | |
46f4442e | 911 | void |
b75a7d8f A |
912 | DigitList::round(int32_t maximumDigits) |
913 | { | |
2ca993e8 A |
914 | reduce(); |
915 | if (maximumDigits >= fDecNumber->digits) { | |
916 | return; | |
917 | } | |
729e4ab9 A |
918 | int32_t savedDigits = fContext.digits; |
919 | fContext.digits = maximumDigits; | |
920 | uprv_decNumberPlus(fDecNumber, fDecNumber, &fContext); | |
921 | fContext.digits = savedDigits; | |
922 | uprv_decNumberTrim(fDecNumber); | |
2ca993e8 | 923 | reduce(); |
51004dcb | 924 | internalClear(); |
b75a7d8f A |
925 | } |
926 | ||
46f4442e | 927 | |
729e4ab9 A |
928 | void |
929 | DigitList::roundFixedPoint(int32_t maximumFractionDigits) { | |
2ca993e8 | 930 | reduce(); // Remove trailing zeros. |
729e4ab9 A |
931 | if (fDecNumber->exponent >= -maximumFractionDigits) { |
932 | return; | |
b75a7d8f | 933 | } |
729e4ab9 A |
934 | decNumber scale; // Dummy decimal number, but with the desired number of |
935 | uprv_decNumberZero(&scale); // fraction digits. | |
936 | scale.exponent = -maximumFractionDigits; | |
937 | scale.lsu[0] = 1; | |
938 | ||
939 | uprv_decNumberQuantize(fDecNumber, fDecNumber, &scale, &fContext); | |
2ca993e8 | 940 | reduce(); |
51004dcb | 941 | internalClear(); |
b75a7d8f A |
942 | } |
943 | ||
944 | // ------------------------------------- | |
945 | ||
b75a7d8f | 946 | void |
729e4ab9 A |
947 | DigitList::toIntegralValue() { |
948 | uprv_decNumberToIntegralValue(fDecNumber, fDecNumber, &fContext); | |
b75a7d8f | 949 | } |
b75a7d8f | 950 | |
729e4ab9 A |
951 | |
952 | // ------------------------------------- | |
b75a7d8f A |
953 | UBool |
954 | DigitList::isZero() const | |
955 | { | |
729e4ab9 | 956 | return decNumberIsZero(fDecNumber); |
b75a7d8f A |
957 | } |
958 | ||
2ca993e8 A |
959 | // ------------------------------------- |
960 | int32_t | |
961 | DigitList::getUpperExponent() const { | |
962 | return fDecNumber->digits + fDecNumber->exponent; | |
963 | } | |
964 | ||
965 | DigitInterval & | |
966 | DigitList::getSmallestInterval(DigitInterval &result) const { | |
967 | result.setLeastSignificantInclusive(fDecNumber->exponent); | |
968 | result.setMostSignificantExclusive(getUpperExponent()); | |
969 | return result; | |
970 | } | |
971 | ||
972 | uint8_t | |
973 | DigitList::getDigitByExponent(int32_t exponent) const { | |
974 | int32_t idx = exponent - fDecNumber->exponent; | |
975 | if (idx < 0 || idx >= fDecNumber->digits) { | |
976 | return 0; | |
977 | } | |
978 | return fDecNumber->lsu[idx]; | |
979 | } | |
980 | ||
981 | void | |
982 | DigitList::appendDigitsTo(CharString &str, UErrorCode &status) const { | |
983 | str.append((const char *) fDecNumber->lsu, fDecNumber->digits, status); | |
984 | } | |
985 | ||
986 | void | |
987 | DigitList::roundAtExponent(int32_t exponent, int32_t maxSigDigits) { | |
988 | reduce(); | |
989 | if (maxSigDigits < fDecNumber->digits) { | |
990 | int32_t minExponent = getUpperExponent() - maxSigDigits; | |
991 | if (exponent < minExponent) { | |
992 | exponent = minExponent; | |
993 | } | |
994 | } | |
995 | if (exponent <= fDecNumber->exponent) { | |
996 | return; | |
997 | } | |
998 | int32_t digits = getUpperExponent() - exponent; | |
999 | if (digits > 0) { | |
1000 | round(digits); | |
1001 | } else { | |
1002 | roundFixedPoint(-exponent); | |
1003 | } | |
1004 | } | |
1005 | ||
1006 | void | |
1007 | DigitList::quantize(const DigitList &quantity, UErrorCode &status) { | |
1008 | if (U_FAILURE(status)) { | |
1009 | return; | |
1010 | } | |
1011 | div(quantity, status); | |
1012 | roundAtExponent(0); | |
1013 | mult(quantity, status); | |
1014 | reduce(); | |
1015 | } | |
1016 | ||
1017 | int32_t | |
1018 | DigitList::getScientificExponent( | |
1019 | int32_t minIntDigitCount, int32_t exponentMultiplier) const { | |
1020 | // The exponent for zero is always zero. | |
1021 | if (isZero()) { | |
1022 | return 0; | |
1023 | } | |
1024 | int32_t intDigitCount = getUpperExponent(); | |
1025 | int32_t exponent; | |
1026 | if (intDigitCount >= minIntDigitCount) { | |
1027 | int32_t maxAdjustment = intDigitCount - minIntDigitCount; | |
1028 | exponent = (maxAdjustment / exponentMultiplier) * exponentMultiplier; | |
1029 | } else { | |
1030 | int32_t minAdjustment = minIntDigitCount - intDigitCount; | |
1031 | exponent = ((minAdjustment + exponentMultiplier - 1) / exponentMultiplier) * -exponentMultiplier; | |
1032 | } | |
1033 | return exponent; | |
1034 | } | |
1035 | ||
1036 | int32_t | |
1037 | DigitList::toScientific( | |
1038 | int32_t minIntDigitCount, int32_t exponentMultiplier) { | |
1039 | int32_t exponent = getScientificExponent( | |
1040 | minIntDigitCount, exponentMultiplier); | |
1041 | shiftDecimalRight(-exponent); | |
1042 | return exponent; | |
1043 | } | |
1044 | ||
1045 | void | |
1046 | DigitList::shiftDecimalRight(int32_t n) { | |
1047 | fDecNumber->exponent += n; | |
1048 | internalClear(); | |
1049 | } | |
1050 | ||
b75a7d8f | 1051 | U_NAMESPACE_END |
46f4442e | 1052 | #endif // #if !UCONFIG_NO_FORMATTING |
b75a7d8f A |
1053 | |
1054 | //eof |