1/* 2********************************************************************** 3* Copyright (C) 1997-2007, International Business Machines 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 "digitlst.h" 28 29#if !UCONFIG_NO_FORMATTING 30#include "unicode/putil.h" 31#include "cstring.h" 32#include "putilimp.h" 33#include "uassert.h" 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 42// characters. Only handles as non-negative numbers. 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"; 55static const char I64_MIN_REP[] = "9223372036854775808"; 56 57enum { 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 60}; 61 62U_NAMESPACE_BEGIN 63 64 65// ------------------------------------- 66// default constructor 67 68DigitList::DigitList() 69{ 70 fDigits = fDecimalDigits + 1; // skip the decimal 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; 100 fRoundingMode = other.fRoundingMode; 101 uprv_strncpy(fDigits, other.fDigits, fCount); 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 && 115 fRoundingMode == that.fRoundingMode && 116 uprv_strncmp(fDigits, that.fDigits, fCount) == 0)); 117} 118 119// ------------------------------------- 120// Resets the digit list; sets all the digits to zero. 121 122void 123DigitList::clear() 124{ 125 fDecimalAt = 0; 126 fCount = 0; 127 fIsPositive = TRUE; 128 fRoundingMode = DecimalFormat::kRoundHalfEven; 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 146formatBase10(int64_t number, char *outputStr, int32_t outputLen) 147{ 148 char buffer[MAX_DIGITS + 1]; 149 int32_t bufferLen; 150 int32_t result; 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 174 result = outputLen - bufferLen++; 175 176 while (bufferLen <= outputLen) { // Copy the number to output 177 *(outputStr++) = buffer[bufferLen++]; 178 } 179 *outputStr = 0; // NULL terminate. 180 return result; 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 191DigitList::getDouble() /*const*/ 192{ 193 double value; 194 195 if (fCount == 0) { 196 value = 0.0; 197 } 198 else { 199 char* end = NULL; 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 '.' 214 value = uprv_strtod(fDecimalDigits, &end); 215 } 216 217 return fIsPositive ? value : -value; 218} 219 220// ------------------------------------- 221 222/** 223 * Make sure that fitsIntoLong() is called before calling this function. 224 */ 225int32_t DigitList::getLong() /*const*/ 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 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 262 // emulate a platform independent atoi64() 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 { 276 // TODO: figure out best approach 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 286/** 287 * Return true if the number represented by this object can fit into 288 * a long. 289 */ 290UBool 291DigitList::fitsIntoLong(UBool ignoreNegativeZero) /*const*/ 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; 295 // then check the size. 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 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 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 397// ------------------------------------- 398 399void 400DigitList::set(int32_t source, int32_t maximumDigits) 401{ 402 set((int64_t)source, maximumDigits); 403} 404 405// ------------------------------------- 406/** 407 * @param maximumDigits The maximum digits to be generated. If zero, 408 * there is no maximum -- generate all digits. 409 */ 410void 411DigitList::set(int64_t source, int32_t maximumDigits) 412{ 413 fCount = fDecimalAt = formatBase10(source, fDecimalDigits, MAX_DIGITS); 414 415 fIsPositive = (*fDecimalDigits == '+'); 416 417 // Don't copy trailing zeros 418 while (fCount > 1 && fDigits[fCount - 1] == kZero) 419 --fCount; 420 421 if(maximumDigits > 0) 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]+/ 448 sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source); 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 } 460 fCount = MAX_DBL_DIGITS + fDecimalAt; 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 */ 514void 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 553 * method implements the requested rounding mode. 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 */ 562UBool DigitList::shouldRoundUp(int32_t maximumDigits) const { 563 int i = 0; 564 if (fRoundingMode == DecimalFormat::kRoundDown || 565 fRoundingMode == DecimalFormat::kRoundFloor && fIsPositive || 566 fRoundingMode == DecimalFormat::kRoundCeiling && !fIsPositive) { 567 return FALSE; 568 } 569 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) { 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: 587 return TRUE; 588 } 589 } 590 return (fDigits[maximumDigits] > '5'); 591 } 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; 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 671U_NAMESPACE_END 672#endif // #if !UCONFIG_NO_FORMATTING 673 674//eof 675