]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/digitlst.cpp
ICU-400.40.tar.gz
[apple/icu.git] / icuSources / i18n / digitlst.cpp
CommitLineData
b75a7d8f
A
1/*
2**********************************************************************
46f4442e 3* Copyright (C) 1997-2007, 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"
374ca955
A
31#include "cstring.h"
32#include "putilimp.h"
46f4442e 33#include "uassert.h"
b75a7d8f
A
34#include <stdlib.h>
35#include <limits.h>
36#include <string.h>
37#include <stdio.h>
38
39// ***************************************************************************
40// class DigitList
41// This class handles the transcoding between numeric values and strings of
46f4442e 42// characters. Only handles as non-negative numbers.
b75a7d8f
A
43// ***************************************************************************
44
45/**
46 * This is the zero digit. Array elements fDigits[i] have values from
47 * kZero to kZero + 9. Typically, this is '0'.
48 */
49#define kZero '0'
50
51static char gDecimal = 0;
52
53/* Only for 32 bit numbers. Ignore the negative sign. */
54static const char LONG_MIN_REP[] = "2147483648";
374ca955
A
55static const char I64_MIN_REP[] = "9223372036854775808";
56
b75a7d8f 57enum {
374ca955
A
58 LONG_MIN_REP_LENGTH = sizeof(LONG_MIN_REP) - 1, //Ignore the NULL at the end
59 I64_MIN_REP_LENGTH = sizeof(I64_MIN_REP) - 1 //Ignore the NULL at the end
b75a7d8f
A
60};
61
62U_NAMESPACE_BEGIN
63
64
65// -------------------------------------
66// default constructor
67
68DigitList::DigitList()
69{
374ca955 70 fDigits = fDecimalDigits + 1; // skip the decimal
b75a7d8f
A
71 clear();
72}
73
74// -------------------------------------
75
76DigitList::~DigitList()
77{
78}
79
80// -------------------------------------
81// copy constructor
82
83DigitList::DigitList(const DigitList &other)
84{
85 fDigits = fDecimalDigits + 1; // skip the decimal
86 *this = other;
87}
88
89// -------------------------------------
90// assignment operator
91
92DigitList&
93DigitList::operator=(const DigitList& other)
94{
95 if (this != &other)
96 {
97 fDecimalAt = other.fDecimalAt;
98 fCount = other.fCount;
99 fIsPositive = other.fIsPositive;
03115e54 100 fRoundingMode = other.fRoundingMode;
374ca955 101 uprv_strncpy(fDigits, other.fDigits, fCount);
b75a7d8f
A
102 }
103 return *this;
104}
105
106// -------------------------------------
107
108UBool
109DigitList::operator==(const DigitList& that) const
110{
111 return ((this == &that) ||
112 (fDecimalAt == that.fDecimalAt &&
113 fCount == that.fCount &&
114 fIsPositive == that.fIsPositive &&
03115e54 115 fRoundingMode == that.fRoundingMode &&
374ca955 116 uprv_strncmp(fDigits, that.fDigits, fCount) == 0));
b75a7d8f
A
117}
118
119// -------------------------------------
120// Resets the digit list; sets all the digits to zero.
121
122void
123DigitList::clear()
124{
b75a7d8f
A
125 fDecimalAt = 0;
126 fCount = 0;
127 fIsPositive = TRUE;
03115e54 128 fRoundingMode = DecimalFormat::kRoundHalfEven;
b75a7d8f
A
129
130 // Don't bother initializing fDigits because fCount is 0.
131}
132
133
134
135// -------------------------------------
136
137/**
138 * Formats a number into a base 10 string representation, and NULL terminates it.
139 * @param number The number to format
140 * @param outputStr The string to output to
141 * @param outputLen The maximum number of characters to put into outputStr
142 * (including NULL).
143 * @return the number of digits written, not including the sign.
144 */
145static int32_t
46f4442e 146formatBase10(int64_t number, char *outputStr, int32_t outputLen)
b75a7d8f
A
147{
148 char buffer[MAX_DIGITS + 1];
149 int32_t bufferLen;
374ca955 150 int32_t result;
b75a7d8f
A
151
152 if (outputLen > MAX_DIGITS) {
153 outputLen = MAX_DIGITS; // Ignore NULL
154 }
155 else if (outputLen < 3) {
156 return 0; // Not enough room
157 }
158
159 bufferLen = outputLen;
160
161 if (number < 0) { // Negative numbers are slightly larger than a postive
162 buffer[bufferLen--] = (char)(-(number % 10) + kZero);
163 number /= -10;
164 *(outputStr++) = '-';
165 }
166 else {
167 *(outputStr++) = '+'; // allow +0
168 }
169 while (bufferLen >= 0 && number) { // Output the number
170 buffer[bufferLen--] = (char)(number % 10 + kZero);
171 number /= 10;
172 }
173
374ca955 174 result = outputLen - bufferLen++;
b75a7d8f 175
374ca955 176 while (bufferLen <= outputLen) { // Copy the number to output
b75a7d8f
A
177 *(outputStr++) = buffer[bufferLen++];
178 }
179 *outputStr = 0; // NULL terminate.
374ca955 180 return result;
b75a7d8f
A
181}
182
183/**
184 * Currently, getDouble() depends on atof() to do its conversion.
185 *
186 * WARNING!!
187 * This is an extremely costly function. ~1/2 of the conversion time
188 * can be linked to this function.
189 */
190double
374ca955 191DigitList::getDouble() /*const*/
b75a7d8f
A
192{
193 double value;
194
195 if (fCount == 0) {
196 value = 0.0;
197 }
198 else {
374ca955 199 char* end = NULL;
b75a7d8f
A
200 if (!gDecimal) {
201 char rep[MAX_DIGITS];
202 // For machines that decide to change the decimal on you,
203 // and try to be too smart with localization.
204 // This normally should be just a '.'.
205 sprintf(rep, "%+1.1f", 1.0);
206 gDecimal = rep[2];
207 }
208
209 *fDecimalDigits = gDecimal;
210 *(fDigits+fCount) = 'e'; // add an e after the digits.
211 formatBase10(fDecimalAt,
212 fDigits + fCount + 1, // skip the 'e'
213 MAX_DEC_DIGITS - fCount - 3); // skip the 'e' and '.'
374ca955 214 value = uprv_strtod(fDecimalDigits, &end);
b75a7d8f
A
215 }
216
217 return fIsPositive ? value : -value;
218}
219
220// -------------------------------------
221
222/**
223 * Make sure that fitsIntoLong() is called before calling this function.
224 */
374ca955 225int32_t DigitList::getLong() /*const*/
b75a7d8f
A
226{
227 if (fCount == fDecimalAt) {
228 int32_t value;
229
230 fDigits[fCount] = 0; // NULL terminate
231
232 // This conversion is bad on 64-bit platforms when we want to
233 // be able to return a 64-bit number [grhoten]
234 *fDecimalDigits = fIsPositive ? '+' : '-';
235 value = (int32_t)atol(fDecimalDigits);
236 return value;
237 }
238 else {
239 // This is 100% accurate in c++ because if we are representing
240 // an integral value, we suffer nothing in the conversion to
241 // double. If we are to support 64-bit longs later, getLong()
242 // must be rewritten. [LIU]
243 return (int32_t)getDouble();
244 }
245}
246
374ca955
A
247
248/**
249 * Make sure that fitsIntoInt64() is called before calling this function.
250 */
251int64_t DigitList::getInt64() /*const*/
252{
253 if (fCount == fDecimalAt) {
254 uint64_t value;
255
256 fDigits[fCount] = 0; // NULL terminate
257
258 // This conversion is bad on 64-bit platforms when we want to
259 // be able to return a 64-bit number [grhoten]
260 *fDecimalDigits = fIsPositive ? '+' : '-';
261
46f4442e 262 // emulate a platform independent atoi64()
374ca955
A
263 value = 0;
264 for (int i = 0; i < fCount; ++i) {
265 int v = fDigits[i] - kZero;
266 value = value * (uint64_t)10 + (uint64_t)v;
267 }
268 if (!fIsPositive) {
269 value = ~value;
270 value += 1;
271 }
272 int64_t svalue = (int64_t)value;
273 return svalue;
274 }
275 else {
46f4442e 276 // TODO: figure out best approach
374ca955
A
277
278 // This is 100% accurate in c++ because if we are representing
279 // an integral value, we suffer nothing in the conversion to
280 // double. If we are to support 64-bit longs later, getLong()
281 // must be rewritten. [LIU]
282 return (int64_t)getDouble();
283 }
284}
285
b75a7d8f
A
286/**
287 * Return true if the number represented by this object can fit into
288 * a long.
289 */
290UBool
374ca955 291DigitList::fitsIntoLong(UBool ignoreNegativeZero) /*const*/
b75a7d8f
A
292{
293 // Figure out if the result will fit in a long. We have to
294 // first look for nonzero digits after the decimal point;
374ca955 295 // then check the size.
b75a7d8f
A
296
297 // Trim trailing zeros after the decimal point. This does not change
298 // the represented value.
299 while (fCount > fDecimalAt && fCount > 0 && fDigits[fCount - 1] == kZero)
300 --fCount;
301
302 if (fCount == 0) {
303 // Positive zero fits into a long, but negative zero can only
304 // be represented as a double. - bug 4162852
305 return fIsPositive || ignoreNegativeZero;
306 }
307
b75a7d8f
A
308 // If the digit list represents a double or this number is too
309 // big for a long.
310 if (fDecimalAt < fCount || fDecimalAt > LONG_MIN_REP_LENGTH)
311 return FALSE;
312
313 // If number is small enough to fit in a long
314 if (fDecimalAt < LONG_MIN_REP_LENGTH)
315 return TRUE;
316
317 // At this point we have fDecimalAt == fCount, and fCount == LONG_MIN_REP_LENGTH.
318 // The number will overflow if it is larger than LONG_MAX
319 // or smaller than LONG_MIN.
320 for (int32_t i=0; i<fCount; ++i)
321 {
322 char dig = fDigits[i],
323 max = LONG_MIN_REP[i];
324 if (dig > max)
325 return FALSE;
326 if (dig < max)
327 return TRUE;
328 }
329
330 // At this point the first count digits match. If fDecimalAt is less
331 // than count, then the remaining digits are zero, and we return true.
332 if (fCount < fDecimalAt)
333 return TRUE;
334
335 // Now we have a representation of Long.MIN_VALUE, without the leading
336 // negative sign. If this represents a positive value, then it does
337 // not fit; otherwise it fits.
338 return !fIsPositive;
339}
340
374ca955
A
341/**
342 * Return true if the number represented by this object can fit into
343 * a long.
344 */
345UBool
346DigitList::fitsIntoInt64(UBool ignoreNegativeZero) /*const*/
347{
348 // Figure out if the result will fit in a long. We have to
349 // first look for nonzero digits after the decimal point;
350 // then check the size.
351
352 // Trim trailing zeros after the decimal point. This does not change
353 // the represented value.
354 while (fCount > fDecimalAt && fCount > 0 && fDigits[fCount - 1] == kZero)
355 --fCount;
356
357 if (fCount == 0) {
358 // Positive zero fits into a long, but negative zero can only
359 // be represented as a double. - bug 4162852
360 return fIsPositive || ignoreNegativeZero;
361 }
362
363 // If the digit list represents a double or this number is too
364 // big for a long.
365 if (fDecimalAt < fCount || fDecimalAt > I64_MIN_REP_LENGTH)
366 return FALSE;
367
368 // If number is small enough to fit in an int64
369 if (fDecimalAt < I64_MIN_REP_LENGTH)
370 return TRUE;
371
372 // At this point we have fDecimalAt == fCount, and fCount == INT64_MIN_REP_LENGTH.
373 // The number will overflow if it is larger than U_INT64_MAX
374 // or smaller than U_INT64_MIN.
375 for (int32_t i=0; i<fCount; ++i)
376 {
377 char dig = fDigits[i],
378 max = I64_MIN_REP[i];
379 if (dig > max)
380 return FALSE;
381 if (dig < max)
382 return TRUE;
383 }
384
385 // At this point the first count digits match. If fDecimalAt is less
386 // than count, then the remaining digits are zero, and we return true.
387 if (fCount < fDecimalAt)
388 return TRUE;
389
390 // Now we have a representation of INT64_MIN_VALUE, without the leading
391 // negative sign. If this represents a positive value, then it does
392 // not fit; otherwise it fits.
393 return !fIsPositive;
394}
395
396
b75a7d8f
A
397// -------------------------------------
398
374ca955
A
399void
400DigitList::set(int32_t source, int32_t maximumDigits)
401{
402 set((int64_t)source, maximumDigits);
403}
404
405// -------------------------------------
b75a7d8f
A
406/**
407 * @param maximumDigits The maximum digits to be generated. If zero,
408 * there is no maximum -- generate all digits.
409 */
410void
374ca955 411DigitList::set(int64_t source, int32_t maximumDigits)
b75a7d8f
A
412{
413 fCount = fDecimalAt = formatBase10(source, fDecimalDigits, MAX_DIGITS);
414
415 fIsPositive = (*fDecimalDigits == '+');
46f4442e 416
b75a7d8f 417 // Don't copy trailing zeros
46f4442e 418 while (fCount > 1 && fDigits[fCount - 1] == kZero)
b75a7d8f 419 --fCount;
46f4442e
A
420
421 if(maximumDigits > 0)
b75a7d8f
A
422 round(maximumDigits);
423}
424
425/**
426 * Set the digit list to a representation of the given double value.
427 * This method supports both fixed-point and exponential notation.
428 * @param source Value to be converted; must not be Inf, -Inf, Nan,
429 * or a value <= 0.
430 * @param maximumDigits The most fractional or total digits which should
431 * be converted. If total digits, and the value is zero, then
432 * there is no maximum -- generate all digits.
433 * @param fixedPoint If true, then maximumDigits is the maximum
434 * fractional digits to be converted. If false, total digits.
435 */
436void
437DigitList::set(double source, int32_t maximumDigits, UBool fixedPoint)
438{
439 // for now, simple implementation; later, do proper IEEE stuff
440 char rep[MAX_DIGITS + 8]; // Extra space for '+', '.', e+NNN, and '\0' (actually +8 is enough)
441 char *digitPtr = fDigits;
442 char *repPtr = rep + 2; // +2 to skip the sign and decimal
443 int32_t exponent = 0;
444
445 fIsPositive = !uprv_isNegative(source); // Allow +0 and -0
446
447 // Generate a representation of the form /[+-][0-9]+e[+-][0-9]+/
374ca955 448 sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source);
b75a7d8f
A
449 fDecimalAt = 0;
450 rep[2] = rep[1]; // remove decimal
451
452 while (*repPtr == kZero) {
453 repPtr++;
454 fDecimalAt--; // account for leading zeros
455 }
456
457 while (*repPtr != 'e') {
458 *(digitPtr++) = *(repPtr++);
459 }
374ca955 460 fCount = MAX_DBL_DIGITS + fDecimalAt;
b75a7d8f
A
461
462 // Parse an exponent of the form /[eE][+-][0-9]+/
463 UBool negExp = (*(++repPtr) == '-');
464 while (*(++repPtr) != 0) {
465 exponent = 10*exponent + *repPtr - kZero;
466 }
467 if (negExp) {
468 exponent = -exponent;
469 }
470 fDecimalAt += exponent + 1; // +1 for decimal removal
471
472 // The negative of the exponent represents the number of leading
473 // zeros between the decimal and the first non-zero digit, for
474 // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this
475 // is more than the maximum fraction digits, then we have an underflow
476 // for the printed representation.
477 if (fixedPoint && -fDecimalAt >= maximumDigits)
478 {
479 // If we round 0.0009 to 3 fractional digits, then we have to
480 // create a new one digit in the least significant location.
481 if (-fDecimalAt == maximumDigits && shouldRoundUp(0)) {
482 fCount = 1;
483 ++fDecimalAt;
484 fDigits[0] = (char)'1';
485 } else {
486 // Handle an underflow to zero when we round something like
487 // 0.0009 to 2 fractional digits.
488 fCount = 0;
489 }
490 return;
491 }
492
493
494 // Eliminate digits beyond maximum digits to be displayed.
495 // Round up if appropriate. Do NOT round in the special
496 // case where maximumDigits == 0 and fixedPoint is FALSE.
497 if (fixedPoint || (0 < maximumDigits && maximumDigits < fCount)) {
498 round(fixedPoint ? (maximumDigits + fDecimalAt) : maximumDigits);
499 }
500 else {
501 // Eliminate trailing zeros.
502 while (fCount > 1 && fDigits[fCount - 1] == kZero)
503 --fCount;
504 }
505}
506
507// -------------------------------------
508
509/**
510 * Round the representation to the given number of digits.
511 * @param maximumDigits The maximum number of digits to be shown.
512 * Upon return, count will be less than or equal to maximumDigits.
513 */
46f4442e 514void
b75a7d8f
A
515DigitList::round(int32_t maximumDigits)
516{
517 // Eliminate digits beyond maximum digits to be displayed.
518 // Round up if appropriate.
519 if (maximumDigits >= 0 && maximumDigits < fCount)
520 {
521 if (shouldRoundUp(maximumDigits)) {
522 // Rounding up involved incrementing digits from LSD to MSD.
523 // In most cases this is simple, but in a worst case situation
524 // (9999..99) we have to adjust the decimalAt value.
525 while (--maximumDigits >= 0 && ++fDigits[maximumDigits] > '9')
526 ;
527
528 if (maximumDigits < 0)
529 {
530 // We have all 9's, so we increment to a single digit
531 // of one and adjust the exponent.
532 fDigits[0] = (char) '1';
533 ++fDecimalAt;
534 maximumDigits = 1; // Adjust the count
535 }
536 else
537 {
538 ++maximumDigits; // Increment for use as count
539 }
540 }
541 fCount = maximumDigits;
542 }
543
544 // Eliminate trailing zeros.
545 while (fCount > 1 && fDigits[fCount-1] == kZero) {
546 --fCount;
547 }
548}
549
550/**
551 * Return true if truncating the representation to the given number
552 * of digits will result in an increment to the last digit. This
03115e54 553 * method implements the requested rounding mode.
b75a7d8f
A
554 * [bnf]
555 * @param maximumDigits the number of digits to keep, from 0 to
556 * <code>count-1</code>. If 0, then all digits are rounded away, and
557 * this method returns true if a one should be generated (e.g., formatting
558 * 0.09 with "#.#").
559 * @return true if digit <code>maximumDigits-1</code> should be
560 * incremented
561 */
374ca955 562UBool DigitList::shouldRoundUp(int32_t maximumDigits) const {
46f4442e
A
563 int i = 0;
564 if (fRoundingMode == DecimalFormat::kRoundDown ||
565 fRoundingMode == DecimalFormat::kRoundFloor && fIsPositive ||
566 fRoundingMode == DecimalFormat::kRoundCeiling && !fIsPositive) {
03115e54 567 return FALSE;
46f4442e
A
568 }
569
570 if (fRoundingMode == DecimalFormat::kRoundHalfEven ||
571 fRoundingMode == DecimalFormat::kRoundHalfDown ||
572 fRoundingMode == DecimalFormat::kRoundHalfUp) {
03115e54 573 if (fDigits[maximumDigits] == '5' ) {
46f4442e 574 for (i=maximumDigits+1; i<fCount; ++i) {
03115e54
A
575 if (fDigits[i] != kZero) {
576 return TRUE;
577 }
578 }
579 switch (fRoundingMode) {
580 case DecimalFormat::kRoundHalfEven:
581 default:
582 // Implement IEEE half-even rounding
583 return maximumDigits > 0 && (fDigits[maximumDigits-1] % 2 != 0);
584 case DecimalFormat::kRoundHalfDown:
585 return FALSE;
586 case DecimalFormat::kRoundHalfUp:
b75a7d8f
A
587 return TRUE;
588 }
589 }
03115e54 590 return (fDigits[maximumDigits] > '5');
b75a7d8f 591 }
46f4442e
A
592
593 U_ASSERT(fRoundingMode == DecimalFormat::kRoundUp ||
594 fRoundingMode == DecimalFormat::kRoundFloor && !fIsPositive ||
595 fRoundingMode == DecimalFormat::kRoundCeiling && fIsPositive);
596
597 for (i=maximumDigits; i<fCount; ++i) {
598 if (fDigits[i] != kZero) {
599 return TRUE;
600 }
601 }
602 return false;
b75a7d8f
A
603}
604
605// -------------------------------------
606
607// In the Java implementation, we need a separate set(long) because 64-bit longs
608// have too much precision to fit into a 64-bit double. In C++, longs can just
609// be passed to set(double) as long as they are 32 bits in size. We currently
610// don't implement 64-bit longs in C++, although the code below would work for
611// that with slight modifications. [LIU]
612/*
613void
614DigitList::set(long source)
615{
616 // handle the special case of zero using a standard exponent of 0.
617 // mathematically, the exponent can be any value.
618 if (source == 0)
619 {
620 fcount = 0;
621 fDecimalAt = 0;
622 return;
623 }
624
625 // we don't accept negative numbers, with the exception of long_min.
626 // long_min is treated specially by being represented as long_max+1,
627 // which is actually an impossible signed long value, so there is no
628 // ambiguity. we do this for convenience, so digitlist can easily
629 // represent the digits of a long.
630 bool islongmin = (source == long_min);
631 if (islongmin)
632 {
633 source = -(source + 1); // that is, long_max
634 islongmin = true;
635 }
636 sprintf(fdigits, "%d", source);
637
638 // now we need to compute the exponent. it's easy in this case; it's
639 // just the same as the count. e.g., 0.123 * 10^3 = 123.
640 fcount = strlen(fdigits);
641 fDecimalAt = fcount;
642
643 // here's how we represent long_max + 1. note that we always know
644 // that the last digit of long_max will not be 9, because long_max
645 // is of the form (2^n)-1.
646 if (islongmin)
647 ++fdigits[fcount-1];
648
649 // finally, we trim off trailing zeros. we don't alter fDecimalAt,
650 // so this has no effect on the represented value. we know the first
651 // digit is non-zero (see code above), so we only have to check down
652 // to fdigits[1].
653 while (fcount > 1 && fdigits[fcount-1] == kzero)
654 --fcount;
655}
656*/
657
658/**
659 * Return true if this object represents the value zero. Anything with
660 * no digits, or all zero digits, is zero, regardless of fDecimalAt.
661 */
662UBool
663DigitList::isZero() const
664{
665 for (int32_t i=0; i<fCount; ++i)
666 if (fDigits[i] != kZero)
667 return FALSE;
668 return TRUE;
669}
670
b75a7d8f 671U_NAMESPACE_END
46f4442e 672#endif // #if !UCONFIG_NO_FORMATTING
b75a7d8f
A
673
674//eof