]>
git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/digitlst.cpp
2 **********************************************************************
3 * Copyright (C) 1997-2007, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 **********************************************************************
9 * Modification History:
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
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 ******************************************************************************
29 #if !UCONFIG_NO_FORMATTING
30 #include "unicode/putil.h"
39 // ***************************************************************************
41 // This class handles the transcoding between numeric values and strings of
42 // characters. Only handles as non-negative numbers.
43 // ***************************************************************************
46 * This is the zero digit. Array elements fDigits[i] have values from
47 * kZero to kZero + 9. Typically, this is '0'.
51 static char gDecimal
= 0;
53 /* Only for 32 bit numbers. Ignore the negative sign. */
54 static const char LONG_MIN_REP
[] = "2147483648";
55 static const char I64_MIN_REP
[] = "9223372036854775808";
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
65 // -------------------------------------
66 // default constructor
68 DigitList::DigitList()
70 fDigits
= fDecimalDigits
+ 1; // skip the decimal
74 // -------------------------------------
76 DigitList::~DigitList()
80 // -------------------------------------
83 DigitList::DigitList(const DigitList
&other
)
85 fDigits
= fDecimalDigits
+ 1; // skip the decimal
89 // -------------------------------------
90 // assignment operator
93 DigitList::operator=(const DigitList
& other
)
97 fDecimalAt
= other
.fDecimalAt
;
98 fCount
= other
.fCount
;
99 fIsPositive
= other
.fIsPositive
;
100 fRoundingMode
= other
.fRoundingMode
;
101 uprv_strncpy(fDigits
, other
.fDigits
, fCount
);
106 // -------------------------------------
109 DigitList::operator==(const DigitList
& that
) const
111 return ((this == &that
) ||
112 (fDecimalAt
== that
.fDecimalAt
&&
113 fCount
== that
.fCount
&&
114 fIsPositive
== that
.fIsPositive
&&
115 fRoundingMode
== that
.fRoundingMode
&&
116 uprv_strncmp(fDigits
, that
.fDigits
, fCount
) == 0));
119 // -------------------------------------
120 // Resets the digit list; sets all the digits to zero.
128 fRoundingMode
= DecimalFormat::kRoundHalfEven
;
130 // Don't bother initializing fDigits because fCount is 0.
135 // -------------------------------------
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
143 * @return the number of digits written, not including the sign.
146 formatBase10(int64_t number
, char *outputStr
, int32_t outputLen
)
148 char buffer
[MAX_DIGITS
+ 1];
152 if (outputLen
> MAX_DIGITS
) {
153 outputLen
= MAX_DIGITS
; // Ignore NULL
155 else if (outputLen
< 3) {
156 return 0; // Not enough room
159 bufferLen
= outputLen
;
161 if (number
< 0) { // Negative numbers are slightly larger than a postive
162 buffer
[bufferLen
--] = (char)(-(number
% 10) + kZero
);
164 *(outputStr
++) = '-';
167 *(outputStr
++) = '+'; // allow +0
169 while (bufferLen
>= 0 && number
) { // Output the number
170 buffer
[bufferLen
--] = (char)(number
% 10 + kZero
);
174 result
= outputLen
- bufferLen
++;
176 while (bufferLen
<= outputLen
) { // Copy the number to output
177 *(outputStr
++) = buffer
[bufferLen
++];
179 *outputStr
= 0; // NULL terminate.
184 * Currently, getDouble() depends on atof() to do its conversion.
187 * This is an extremely costly function. ~1/2 of the conversion time
188 * can be linked to this function.
191 DigitList::getDouble() /*const*/
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);
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 '.'
214 value
= uprv_strtod(fDecimalDigits
, &end
);
217 return fIsPositive
? value
: -value
;
220 // -------------------------------------
223 * Make sure that fitsIntoLong() is called before calling this function.
225 int32_t DigitList::getLong() /*const*/
227 if (fCount
== fDecimalAt
) {
230 fDigits
[fCount
] = 0; // NULL terminate
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
);
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();
249 * Make sure that fitsIntoInt64() is called before calling this function.
251 int64_t DigitList::getInt64() /*const*/
253 if (fCount
== fDecimalAt
) {
256 fDigits
[fCount
] = 0; // NULL terminate
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
? '+' : '-';
262 // emulate a platform independent atoi64()
264 for (int i
= 0; i
< fCount
; ++i
) {
265 int v
= fDigits
[i
] - kZero
;
266 value
= value
* (uint64_t)10 + (uint64_t)v
;
272 int64_t svalue
= (int64_t)value
;
276 // TODO: figure out best approach
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();
287 * Return true if the number represented by this object can fit into
291 DigitList::fitsIntoLong(UBool ignoreNegativeZero
) /*const*/
293 // Figure out if the result will fit in a long. We have to
294 // first look for nonzero digits after the decimal point;
295 // then check the size.
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
)
303 // Positive zero fits into a long, but negative zero can only
304 // be represented as a double. - bug 4162852
305 return fIsPositive
|| ignoreNegativeZero
;
308 // If the digit list represents a double or this number is too
310 if (fDecimalAt
< fCount
|| fDecimalAt
> LONG_MIN_REP_LENGTH
)
313 // If number is small enough to fit in a long
314 if (fDecimalAt
< LONG_MIN_REP_LENGTH
)
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
)
322 char dig
= fDigits
[i
],
323 max
= LONG_MIN_REP
[i
];
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
)
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.
342 * Return true if the number represented by this object can fit into
346 DigitList::fitsIntoInt64(UBool ignoreNegativeZero
) /*const*/
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.
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
)
358 // Positive zero fits into a long, but negative zero can only
359 // be represented as a double. - bug 4162852
360 return fIsPositive
|| ignoreNegativeZero
;
363 // If the digit list represents a double or this number is too
365 if (fDecimalAt
< fCount
|| fDecimalAt
> I64_MIN_REP_LENGTH
)
368 // If number is small enough to fit in an int64
369 if (fDecimalAt
< I64_MIN_REP_LENGTH
)
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
)
377 char dig
= fDigits
[i
],
378 max
= I64_MIN_REP
[i
];
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
)
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.
397 // -------------------------------------
400 DigitList::set(int32_t source
, int32_t maximumDigits
)
402 set((int64_t)source
, maximumDigits
);
405 // -------------------------------------
407 * @param maximumDigits The maximum digits to be generated. If zero,
408 * there is no maximum -- generate all digits.
411 DigitList::set(int64_t source
, int32_t maximumDigits
)
413 fCount
= fDecimalAt
= formatBase10(source
, fDecimalDigits
, MAX_DIGITS
);
415 fIsPositive
= (*fDecimalDigits
== '+');
417 // Don't copy trailing zeros
418 while (fCount
> 1 && fDigits
[fCount
- 1] == kZero
)
421 if(maximumDigits
> 0)
422 round(maximumDigits
);
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,
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.
437 DigitList::set(double source
, int32_t maximumDigits
, UBool fixedPoint
)
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;
445 fIsPositive
= !uprv_isNegative(source
); // Allow +0 and -0
447 // Generate a representation of the form /[+-][0-9]+e[+-][0-9]+/
448 sprintf(rep
, "%+1.*e", MAX_DBL_DIGITS
- 1, source
);
450 rep
[2] = rep
[1]; // remove decimal
452 while (*repPtr
== kZero
) {
454 fDecimalAt
--; // account for leading zeros
457 while (*repPtr
!= 'e') {
458 *(digitPtr
++) = *(repPtr
++);
460 fCount
= MAX_DBL_DIGITS
+ fDecimalAt
;
462 // Parse an exponent of the form /[eE][+-][0-9]+/
463 UBool negExp
= (*(++repPtr
) == '-');
464 while (*(++repPtr
) != 0) {
465 exponent
= 10*exponent
+ *repPtr
- kZero
;
468 exponent
= -exponent
;
470 fDecimalAt
+= exponent
+ 1; // +1 for decimal removal
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
)
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)) {
484 fDigits
[0] = (char)'1';
486 // Handle an underflow to zero when we round something like
487 // 0.0009 to 2 fractional digits.
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
);
501 // Eliminate trailing zeros.
502 while (fCount
> 1 && fDigits
[fCount
- 1] == kZero
)
507 // -------------------------------------
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.
515 DigitList::round(int32_t maximumDigits
)
517 // Eliminate digits beyond maximum digits to be displayed.
518 // Round up if appropriate.
519 if (maximumDigits
>= 0 && maximumDigits
< fCount
)
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')
528 if (maximumDigits
< 0)
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';
534 maximumDigits
= 1; // Adjust the count
538 ++maximumDigits
; // Increment for use as count
541 fCount
= maximumDigits
;
544 // Eliminate trailing zeros.
545 while (fCount
> 1 && fDigits
[fCount
-1] == kZero
) {
551 * Return true if truncating the representation to the given number
552 * of digits will result in an increment to the last digit. This
553 * method implements the requested rounding mode.
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
559 * @return true if digit <code>maximumDigits-1</code> should be
562 UBool
DigitList::shouldRoundUp(int32_t maximumDigits
) const {
564 if (fRoundingMode
== DecimalFormat::kRoundDown
||
565 fRoundingMode
== DecimalFormat::kRoundFloor
&& fIsPositive
||
566 fRoundingMode
== DecimalFormat::kRoundCeiling
&& !fIsPositive
) {
570 if (fRoundingMode
== DecimalFormat::kRoundHalfEven
||
571 fRoundingMode
== DecimalFormat::kRoundHalfDown
||
572 fRoundingMode
== DecimalFormat::kRoundHalfUp
) {
573 if (fDigits
[maximumDigits
] == '5' ) {
574 for (i
=maximumDigits
+1; i
<fCount
; ++i
) {
575 if (fDigits
[i
] != kZero
) {
579 switch (fRoundingMode
) {
580 case DecimalFormat::kRoundHalfEven
:
582 // Implement IEEE half-even rounding
583 return maximumDigits
> 0 && (fDigits
[maximumDigits
-1] % 2 != 0);
584 case DecimalFormat::kRoundHalfDown
:
586 case DecimalFormat::kRoundHalfUp
:
590 return (fDigits
[maximumDigits
] > '5');
593 U_ASSERT(fRoundingMode
== DecimalFormat::kRoundUp
||
594 fRoundingMode
== DecimalFormat::kRoundFloor
&& !fIsPositive
||
595 fRoundingMode
== DecimalFormat::kRoundCeiling
&& fIsPositive
);
597 for (i
=maximumDigits
; i
<fCount
; ++i
) {
598 if (fDigits
[i
] != kZero
) {
605 // -------------------------------------
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]
614 DigitList::set(long source)
616 // handle the special case of zero using a standard exponent of 0.
617 // mathematically, the exponent can be any value.
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);
633 source = -(source + 1); // that is, long_max
636 sprintf(fdigits, "%d", source);
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);
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.
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
653 while (fcount > 1 && fdigits[fcount-1] == kzero)
659 * Return true if this object represents the value zero. Anything with
660 * no digits, or all zero digits, is zero, regardless of fDecimalAt.
663 DigitList::isZero() const
665 for (int32_t i
=0; i
<fCount
; ++i
)
666 if (fDigits
[i
] != kZero
)
672 #endif // #if !UCONFIG_NO_FORMATTING