intl/icu/source/i18n/digitlst.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial