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