michael@0: /* michael@0: ********************************************************************** michael@0: * Copyright (C) 1997-2012, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: ********************************************************************** michael@0: * michael@0: * File DIGITLST.CPP michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 03/21/97 clhuang Converted from java. michael@0: * 03/21/97 clhuang Implemented with new APIs. michael@0: * 03/27/97 helena Updated to pass the simple test after code review. michael@0: * 03/31/97 aliu Moved isLONG_MIN to here, and fixed it. michael@0: * 04/15/97 aliu Changed MAX_COUNT to DBL_DIG. Changed Digit to char. michael@0: * Reworked representation by replacing fDecimalAt michael@0: * with fExponent. michael@0: * 04/16/97 aliu Rewrote set() and getDouble() to use sprintf/atof michael@0: * to do digit conversion. michael@0: * 09/09/97 aliu Modified for exponential notation support. michael@0: * 08/02/98 stephen Added nearest/even rounding michael@0: * Fixed bug in fitsIntoLong michael@0: ****************************************************************************** michael@0: */ michael@0: michael@0: #include "digitlst.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: #include "unicode/putil.h" michael@0: #include "charstr.h" michael@0: #include "cmemory.h" michael@0: #include "cstring.h" michael@0: #include "mutex.h" michael@0: #include "putilimp.h" michael@0: #include "uassert.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: // *************************************************************************** michael@0: // class DigitList michael@0: // A wrapper onto decNumber. michael@0: // Used to be standalone. michael@0: // *************************************************************************** michael@0: michael@0: /** michael@0: * This is the zero digit. The base for the digits returned by getDigit() michael@0: * Note that it is the platform invariant digit, and is not Unicode. michael@0: */ michael@0: #define kZero '0' michael@0: michael@0: michael@0: /* Only for 32 bit numbers. Ignore the negative sign. */ michael@0: //static const char LONG_MIN_REP[] = "2147483648"; michael@0: //static const char I64_MIN_REP[] = "9223372036854775808"; michael@0: michael@0: michael@0: static const uint8_t DIGIT_HAVE_NONE=0; michael@0: static const uint8_t DIGIT_HAVE_DOUBLE=1; michael@0: static const uint8_t DIGIT_HAVE_INT64=2; michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: // ------------------------------------- michael@0: // default constructor michael@0: michael@0: DigitList::DigitList() michael@0: { michael@0: uprv_decContextDefault(&fContext, DEC_INIT_BASE); michael@0: fContext.traps = 0; michael@0: uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN); michael@0: fContext.digits = fStorage.getCapacity(); michael@0: michael@0: fDecNumber = fStorage.getAlias(); michael@0: uprv_decNumberZero(fDecNumber); michael@0: michael@0: internalSetDouble(0.0); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: DigitList::~DigitList() michael@0: { michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // copy constructor michael@0: michael@0: DigitList::DigitList(const DigitList &other) michael@0: { michael@0: fDecNumber = fStorage.getAlias(); michael@0: *this = other; michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: // assignment operator michael@0: michael@0: DigitList& michael@0: DigitList::operator=(const DigitList& other) michael@0: { michael@0: if (this != &other) michael@0: { michael@0: uprv_memcpy(&fContext, &other.fContext, sizeof(decContext)); michael@0: michael@0: if (other.fStorage.getCapacity() > fStorage.getCapacity()) { michael@0: fDecNumber = fStorage.resize(other.fStorage.getCapacity()); michael@0: } michael@0: // Always reset the fContext.digits, even if fDecNumber was not reallocated, michael@0: // because above we copied fContext from other.fContext. michael@0: fContext.digits = fStorage.getCapacity(); michael@0: uprv_decNumberCopy(fDecNumber, other.fDecNumber); michael@0: michael@0: { michael@0: // fDouble is lazily created and cached. michael@0: // Avoid potential races with that happening with other.fDouble michael@0: // while we are doing the assignment. michael@0: Mutex mutex; michael@0: michael@0: if(other.fHave==kDouble) { michael@0: fUnion.fDouble = other.fUnion.fDouble; michael@0: } else if(other.fHave==kInt64) { michael@0: fUnion.fInt64 = other.fUnion.fInt64; michael@0: } michael@0: fHave = other.fHave; michael@0: } michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // operator == (does not exactly match the old DigitList function) michael@0: michael@0: UBool michael@0: DigitList::operator==(const DigitList& that) const michael@0: { michael@0: if (this == &that) { michael@0: return TRUE; michael@0: } michael@0: decNumber n; // Has space for only a none digit value. michael@0: decContext c; michael@0: uprv_decContextDefault(&c, DEC_INIT_BASE); michael@0: c.digits = 1; michael@0: c.traps = 0; michael@0: michael@0: uprv_decNumberCompare(&n, this->fDecNumber, that.fDecNumber, &c); michael@0: UBool result = decNumberIsZero(&n); michael@0: return result; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // comparison function. Returns michael@0: // Not Comparable : -2 michael@0: // < : -1 michael@0: // == : 0 michael@0: // > : +1 michael@0: int32_t DigitList::compare(const DigitList &other) { michael@0: decNumber result; michael@0: int32_t savedDigits = fContext.digits; michael@0: fContext.digits = 1; michael@0: uprv_decNumberCompare(&result, this->fDecNumber, other.fDecNumber, &fContext); michael@0: fContext.digits = savedDigits; michael@0: if (decNumberIsZero(&result)) { michael@0: return 0; michael@0: } else if (decNumberIsSpecial(&result)) { michael@0: return -2; michael@0: } else if (result.bits & DECNEG) { michael@0: return -1; michael@0: } else { michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: // Reduce - remove trailing zero digits. michael@0: void michael@0: DigitList::reduce() { michael@0: uprv_decNumberReduce(fDecNumber, fDecNumber, &fContext); michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: // trim - remove trailing fraction zero digits. michael@0: void michael@0: DigitList::trim() { michael@0: uprv_decNumberTrim(fDecNumber); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Resets the digit list; sets all the digits to zero. michael@0: michael@0: void michael@0: DigitList::clear() michael@0: { michael@0: uprv_decNumberZero(fDecNumber); michael@0: uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN); michael@0: internalSetDouble(0.0); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Formats a int64_t number into a base 10 string representation, and NULL terminates it. michael@0: * @param number The number to format michael@0: * @param outputStr The string to output to. Must be at least MAX_DIGITS+2 in length (21), michael@0: * to hold the longest int64_t value. michael@0: * @return the number of digits written, not including the sign. michael@0: */ michael@0: static int32_t michael@0: formatBase10(int64_t number, char *outputStr) { michael@0: // The number is output backwards, starting with the LSD. michael@0: // Fill the buffer from the far end. After the number is complete, michael@0: // slide the string contents to the front. michael@0: michael@0: const int32_t MAX_IDX = MAX_DIGITS+2; michael@0: int32_t destIdx = MAX_IDX; michael@0: outputStr[--destIdx] = 0; michael@0: michael@0: int64_t n = number; michael@0: if (number < 0) { // Negative numbers are slightly larger than a postive michael@0: outputStr[--destIdx] = (char)(-(n % 10) + kZero); michael@0: n /= -10; michael@0: } michael@0: do { michael@0: outputStr[--destIdx] = (char)(n % 10 + kZero); michael@0: n /= 10; michael@0: } while (n > 0); michael@0: michael@0: if (number < 0) { michael@0: outputStr[--destIdx] = '-'; michael@0: } michael@0: michael@0: // Slide the number to the start of the output str michael@0: U_ASSERT(destIdx >= 0); michael@0: int32_t length = MAX_IDX - destIdx; michael@0: uprv_memmove(outputStr, outputStr+MAX_IDX-length, length); michael@0: michael@0: return length; michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: // michael@0: // setRoundingMode() michael@0: // For most modes, the meaning and names are the same between the decNumber library michael@0: // (which DigitList follows) and the ICU Formatting Rounding Mode values. michael@0: // The flag constants are different, however. michael@0: // michael@0: // Note that ICU's kRoundingUnnecessary is not implemented directly by DigitList. michael@0: // This mode, inherited from Java, means that numbers that would not format exactly michael@0: // will return an error when formatting is attempted. michael@0: michael@0: void michael@0: DigitList::setRoundingMode(DecimalFormat::ERoundingMode m) { michael@0: enum rounding r; michael@0: michael@0: switch (m) { michael@0: case DecimalFormat::kRoundCeiling: r = DEC_ROUND_CEILING; break; michael@0: case DecimalFormat::kRoundFloor: r = DEC_ROUND_FLOOR; break; michael@0: case DecimalFormat::kRoundDown: r = DEC_ROUND_DOWN; break; michael@0: case DecimalFormat::kRoundUp: r = DEC_ROUND_UP; break; michael@0: case DecimalFormat::kRoundHalfEven: r = DEC_ROUND_HALF_EVEN; break; michael@0: case DecimalFormat::kRoundHalfDown: r = DEC_ROUND_HALF_DOWN; break; michael@0: case DecimalFormat::kRoundHalfUp: r = DEC_ROUND_HALF_UP; break; michael@0: case DecimalFormat::kRoundUnnecessary: r = DEC_ROUND_HALF_EVEN; break; michael@0: default: michael@0: // TODO: how to report the problem? michael@0: // Leave existing mode unchanged. michael@0: r = uprv_decContextGetRounding(&fContext); michael@0: } michael@0: uprv_decContextSetRounding(&fContext, r); michael@0: michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: DigitList::setPositive(UBool s) { michael@0: if (s) { michael@0: fDecNumber->bits &= ~DECNEG; michael@0: } else { michael@0: fDecNumber->bits |= DECNEG; michael@0: } michael@0: internalClear(); michael@0: } michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: DigitList::setDecimalAt(int32_t d) { michael@0: U_ASSERT((fDecNumber->bits & DECSPECIAL) == 0); // Not Infinity or NaN michael@0: U_ASSERT(d-1>-999999999); michael@0: U_ASSERT(d-1< 999999999); michael@0: int32_t adjustedDigits = fDecNumber->digits; michael@0: if (decNumberIsZero(fDecNumber)) { michael@0: // Account for difference in how zero is represented between DigitList & decNumber. michael@0: adjustedDigits = 0; michael@0: } michael@0: fDecNumber->exponent = d - adjustedDigits; michael@0: internalClear(); michael@0: } michael@0: michael@0: int32_t michael@0: DigitList::getDecimalAt() { michael@0: U_ASSERT((fDecNumber->bits & DECSPECIAL) == 0); // Not Infinity or NaN michael@0: if (decNumberIsZero(fDecNumber) || ((fDecNumber->bits & DECSPECIAL) != 0)) { michael@0: return fDecNumber->exponent; // Exponent should be zero for these cases. michael@0: } michael@0: return fDecNumber->exponent + fDecNumber->digits; michael@0: } michael@0: michael@0: void michael@0: DigitList::setCount(int32_t c) { michael@0: U_ASSERT(c <= fContext.digits); michael@0: if (c == 0) { michael@0: // For a value of zero, DigitList sets all fields to zero, while michael@0: // decNumber keeps one digit (with that digit being a zero) michael@0: c = 1; michael@0: fDecNumber->lsu[0] = 0; michael@0: } michael@0: fDecNumber->digits = c; michael@0: internalClear(); michael@0: } michael@0: michael@0: int32_t michael@0: DigitList::getCount() const { michael@0: if (decNumberIsZero(fDecNumber) && fDecNumber->exponent==0) { michael@0: // The extra test for exponent==0 is needed because parsing sometimes appends michael@0: // zero digits. It's bogus, decimalFormatter parsing needs to be cleaned up. michael@0: return 0; michael@0: } else { michael@0: return fDecNumber->digits; michael@0: } michael@0: } michael@0: michael@0: void michael@0: DigitList::setDigit(int32_t i, char v) { michael@0: int32_t count = fDecNumber->digits; michael@0: U_ASSERT(i='0' && v<='9'); michael@0: v &= 0x0f; michael@0: fDecNumber->lsu[count-i-1] = v; michael@0: internalClear(); michael@0: } michael@0: michael@0: char michael@0: DigitList::getDigit(int32_t i) { michael@0: int32_t count = fDecNumber->digits; michael@0: U_ASSERT(ilsu[count-i-1] + '0'; michael@0: } michael@0: michael@0: // copied from DigitList::getDigit() michael@0: uint8_t michael@0: DigitList::getDigitValue(int32_t i) { michael@0: int32_t count = fDecNumber->digits; michael@0: U_ASSERT(ilsu[count-i-1]; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Appends the digit to the digit list if it's not out of scope. michael@0: // Ignores the digit, otherwise. michael@0: // michael@0: // This function is horribly inefficient to implement with decNumber because michael@0: // the digits are stored least significant first, which requires moving all michael@0: // existing digits down one to make space for the new one to be appended. michael@0: // michael@0: void michael@0: DigitList::append(char digit) michael@0: { michael@0: U_ASSERT(digit>='0' && digit<='9'); michael@0: // Ignore digits which exceed the precision we can represent michael@0: // And don't fix for larger precision. Fix callers instead. michael@0: if (decNumberIsZero(fDecNumber)) { michael@0: // Zero needs to be special cased because of the difference in the way michael@0: // that the old DigitList and decNumber represent it. michael@0: // digit cout was zero for digitList, is one for decNumber michael@0: fDecNumber->lsu[0] = digit & 0x0f; michael@0: fDecNumber->digits = 1; michael@0: fDecNumber->exponent--; // To match the old digit list implementation. michael@0: } else { michael@0: int32_t nDigits = fDecNumber->digits; michael@0: if (nDigits < fContext.digits) { michael@0: int i; michael@0: for (i=nDigits; i>0; i--) { michael@0: fDecNumber->lsu[i] = fDecNumber->lsu[i-1]; michael@0: } michael@0: fDecNumber->lsu[0] = digit & 0x0f; michael@0: fDecNumber->digits++; michael@0: // DigitList emulation - appending doesn't change the magnitude of existing michael@0: // digits. With decNumber's decimal being after the michael@0: // least signficant digit, we need to adjust the exponent. michael@0: fDecNumber->exponent--; michael@0: } michael@0: } michael@0: internalClear(); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /** michael@0: * Currently, getDouble() depends on strtod() to do its conversion. michael@0: * michael@0: * WARNING!! michael@0: * This is an extremely costly function. ~1/2 of the conversion time michael@0: * can be linked to this function. michael@0: */ michael@0: double michael@0: DigitList::getDouble() const michael@0: { michael@0: static char gDecimal = 0; michael@0: char decimalSeparator; michael@0: { michael@0: Mutex mutex; michael@0: if (fHave == kDouble) { michael@0: return fUnion.fDouble; michael@0: } else if(fHave == kInt64) { michael@0: return (double)fUnion.fInt64; michael@0: } michael@0: decimalSeparator = gDecimal; michael@0: } michael@0: michael@0: if (decimalSeparator == 0) { michael@0: // We need to know the decimal separator character that will be used with strtod(). michael@0: // Depends on the C runtime global locale. michael@0: // Most commonly is '.' michael@0: // TODO: caching could fail if the global locale is changed on the fly. michael@0: char rep[MAX_DIGITS]; michael@0: sprintf(rep, "%+1.1f", 1.0); michael@0: decimalSeparator = rep[2]; michael@0: } michael@0: michael@0: double tDouble = 0.0; michael@0: if (isZero()) { michael@0: tDouble = 0.0; michael@0: if (decNumberIsNegative(fDecNumber)) { michael@0: tDouble /= -1; michael@0: } michael@0: } else if (isInfinite()) { michael@0: if (std::numeric_limits::has_infinity) { michael@0: tDouble = std::numeric_limits::infinity(); michael@0: } else { michael@0: tDouble = std::numeric_limits::max(); michael@0: } michael@0: if (!isPositive()) { michael@0: tDouble = -tDouble; //this was incorrectly "-fDouble" originally. michael@0: } michael@0: } else { michael@0: MaybeStackArray s; michael@0: // Note: 14 is a magic constant from the decNumber library documentation, michael@0: // the max number of extra characters beyond the number of digits michael@0: // needed to represent the number in string form. Add a few more michael@0: // for the additional digits we retain. michael@0: michael@0: // Round down to appx. double precision, if the number is longer than that. michael@0: // Copy the number first, so that we don't modify the original. michael@0: if (getCount() > MAX_DBL_DIGITS + 3) { michael@0: DigitList numToConvert(*this); michael@0: numToConvert.reduce(); // Removes any trailing zeros, so that digit count is good. michael@0: numToConvert.round(MAX_DBL_DIGITS+3); michael@0: uprv_decNumberToString(numToConvert.fDecNumber, s.getAlias()); michael@0: // TODO: how many extra digits should be included for an accurate conversion? michael@0: } else { michael@0: uprv_decNumberToString(this->fDecNumber, s.getAlias()); michael@0: } michael@0: U_ASSERT(uprv_strlen(&s[0]) < MAX_DBL_DIGITS+18); michael@0: michael@0: if (decimalSeparator != '.') { michael@0: char *decimalPt = strchr(s.getAlias(), '.'); michael@0: if (decimalPt != NULL) { michael@0: *decimalPt = decimalSeparator; michael@0: } michael@0: } michael@0: char *end = NULL; michael@0: tDouble = uprv_strtod(s.getAlias(), &end); michael@0: } michael@0: { michael@0: Mutex mutex; michael@0: DigitList *nonConstThis = const_cast(this); michael@0: nonConstThis->internalSetDouble(tDouble); michael@0: gDecimal = decimalSeparator; michael@0: } michael@0: return tDouble; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /** michael@0: * convert this number to an int32_t. Round if there is a fractional part. michael@0: * Return zero if the number cannot be represented. michael@0: */ michael@0: int32_t DigitList::getLong() /*const*/ michael@0: { michael@0: int32_t result = 0; michael@0: if (fDecNumber->digits + fDecNumber->exponent > 10) { michael@0: // Overflow, absolute value too big. michael@0: return result; michael@0: } michael@0: if (fDecNumber->exponent != 0) { michael@0: // Force to an integer, with zero exponent, rounding if necessary. michael@0: // (decNumberToInt32 will only work if the exponent is exactly zero.) michael@0: DigitList copy(*this); michael@0: DigitList zero; michael@0: uprv_decNumberQuantize(copy.fDecNumber, copy.fDecNumber, zero.fDecNumber, &fContext); michael@0: result = uprv_decNumberToInt32(copy.fDecNumber, &fContext); michael@0: } else { michael@0: result = uprv_decNumberToInt32(fDecNumber, &fContext); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * convert this number to an int64_t. Truncate if there is a fractional part. michael@0: * Return zero if the number cannot be represented. michael@0: */ michael@0: int64_t DigitList::getInt64() /*const*/ { michael@0: if(fHave==kInt64) { michael@0: return fUnion.fInt64; michael@0: } michael@0: // Truncate if non-integer. michael@0: // Return 0 if out of range. michael@0: // Range of in64_t is -9223372036854775808 to 9223372036854775807 (19 digits) michael@0: // michael@0: if (fDecNumber->digits + fDecNumber->exponent > 19) { michael@0: // Overflow, absolute value too big. michael@0: return 0; michael@0: } michael@0: michael@0: // The number of integer digits may differ from the number of digits stored michael@0: // in the decimal number. michael@0: // for 12.345 numIntDigits = 2, number->digits = 5 michael@0: // for 12E4 numIntDigits = 6, number->digits = 2 michael@0: // The conversion ignores the fraction digits in the first case, michael@0: // and fakes up extra zero digits in the second. michael@0: // TODO: It would be faster to store a table of powers of ten to multiply by michael@0: // instead of looping over zero digits, multiplying each time. michael@0: michael@0: int32_t numIntDigits = fDecNumber->digits + fDecNumber->exponent; michael@0: uint64_t value = 0; michael@0: for (int32_t i = 0; i < numIntDigits; i++) { michael@0: // Loop is iterating over digits starting with the most significant. michael@0: // Numbers are stored with the least significant digit at index zero. michael@0: int32_t digitIndex = fDecNumber->digits - i - 1; michael@0: int32_t v = (digitIndex >= 0) ? fDecNumber->lsu[digitIndex] : 0; michael@0: value = value * (uint64_t)10 + (uint64_t)v; michael@0: } michael@0: michael@0: if (decNumberIsNegative(fDecNumber)) { michael@0: value = ~value; michael@0: value += 1; michael@0: } michael@0: int64_t svalue = (int64_t)value; michael@0: michael@0: // Check overflow. It's convenient that the MSD is 9 only on overflow, the amount of michael@0: // overflow can't wrap too far. The test will also fail -0, but michael@0: // that does no harm; the right answer is 0. michael@0: if (numIntDigits == 19) { michael@0: if (( decNumberIsNegative(fDecNumber) && svalue>0) || michael@0: (!decNumberIsNegative(fDecNumber) && svalue<0)) { michael@0: svalue = 0; michael@0: } michael@0: } michael@0: michael@0: return svalue; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Return a string form of this number. michael@0: * Format is as defined by the decNumber library, for interchange of michael@0: * decimal numbers. michael@0: */ michael@0: void DigitList::getDecimal(CharString &str, UErrorCode &status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: // A decimal number in string form can, worst case, be 14 characters longer michael@0: // than the number of digits. So says the decNumber library doc. michael@0: int32_t maxLength = fDecNumber->digits + 14; michael@0: int32_t capacity = 0; michael@0: char *buffer = str.clear().getAppendBuffer(maxLength, 0, capacity, status); michael@0: if (U_FAILURE(status)) { michael@0: return; // Memory allocation error on growing the string. michael@0: } michael@0: U_ASSERT(capacity >= maxLength); michael@0: uprv_decNumberToString(this->fDecNumber, buffer); michael@0: U_ASSERT((int32_t)uprv_strlen(buffer) <= maxLength); michael@0: str.append(buffer, -1, status); michael@0: } michael@0: michael@0: /** michael@0: * Return true if this is an integer value that can be held michael@0: * by an int32_t type. michael@0: */ michael@0: UBool michael@0: DigitList::fitsIntoLong(UBool ignoreNegativeZero) /*const*/ michael@0: { michael@0: if (decNumberIsSpecial(this->fDecNumber)) { michael@0: // NaN or Infinity. Does not fit in int32. michael@0: return FALSE; michael@0: } michael@0: uprv_decNumberTrim(this->fDecNumber); michael@0: if (fDecNumber->exponent < 0) { michael@0: // Number contains fraction digits. michael@0: return FALSE; michael@0: } michael@0: if (decNumberIsZero(this->fDecNumber) && !ignoreNegativeZero && michael@0: (fDecNumber->bits & DECNEG) != 0) { michael@0: // Negative Zero, not ingored. Cannot represent as a long. michael@0: return FALSE; michael@0: } michael@0: if (fDecNumber->digits + fDecNumber->exponent < 10) { michael@0: // The number is 9 or fewer digits. michael@0: // The max and min int32 are 10 digts, so this number fits. michael@0: // This is the common case. michael@0: return TRUE; michael@0: } michael@0: michael@0: // TODO: Should cache these constants; construction is relatively costly. michael@0: // But not of huge consequence; they're only needed for 10 digit ints. michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: DigitList min32; min32.set("-2147483648", status); michael@0: if (this->compare(min32) < 0) { michael@0: return FALSE; michael@0: } michael@0: DigitList max32; max32.set("2147483647", status); michael@0: if (this->compare(max32) > 0) { michael@0: return FALSE; michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: michael@0: michael@0: /** michael@0: * Return true if the number represented by this object can fit into michael@0: * a long. michael@0: */ michael@0: UBool michael@0: DigitList::fitsIntoInt64(UBool ignoreNegativeZero) /*const*/ michael@0: { michael@0: if (decNumberIsSpecial(this->fDecNumber)) { michael@0: // NaN or Infinity. Does not fit in int32. michael@0: return FALSE; michael@0: } michael@0: uprv_decNumberTrim(this->fDecNumber); michael@0: if (fDecNumber->exponent < 0) { michael@0: // Number contains fraction digits. michael@0: return FALSE; michael@0: } michael@0: if (decNumberIsZero(this->fDecNumber) && !ignoreNegativeZero && michael@0: (fDecNumber->bits & DECNEG) != 0) { michael@0: // Negative Zero, not ingored. Cannot represent as a long. michael@0: return FALSE; michael@0: } michael@0: if (fDecNumber->digits + fDecNumber->exponent < 19) { michael@0: // The number is 18 or fewer digits. michael@0: // The max and min int64 are 19 digts, so this number fits. michael@0: // This is the common case. michael@0: return TRUE; michael@0: } michael@0: michael@0: // TODO: Should cache these constants; construction is relatively costly. michael@0: // But not of huge consequence; they're only needed for 19 digit ints. michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: DigitList min64; min64.set("-9223372036854775808", status); michael@0: if (this->compare(min64) < 0) { michael@0: return FALSE; michael@0: } michael@0: DigitList max64; max64.set("9223372036854775807", status); michael@0: if (this->compare(max64) > 0) { michael@0: return FALSE; michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: DigitList::set(int32_t source) michael@0: { michael@0: set((int64_t)source); michael@0: internalSetDouble(source); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: /** michael@0: * Set an int64, via decnumber michael@0: */ michael@0: void michael@0: DigitList::set(int64_t source) michael@0: { michael@0: char str[MAX_DIGITS+2]; // Leave room for sign and trailing nul. michael@0: formatBase10(source, str); michael@0: U_ASSERT(uprv_strlen(str) < sizeof(str)); michael@0: michael@0: uprv_decNumberFromString(fDecNumber, str, &fContext); michael@0: internalSetDouble(source); michael@0: } michael@0: michael@0: /** michael@0: * Set an int64, with no decnumber michael@0: */ michael@0: void michael@0: DigitList::setInteger(int64_t source) michael@0: { michael@0: fDecNumber=NULL; michael@0: internalSetInt64(source); michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: /** michael@0: * Set the DigitList from a decimal number string. michael@0: * michael@0: * The incoming string _must_ be nul terminated, even though it is arriving michael@0: * as a StringPiece because that is what the decNumber library wants. michael@0: * We can get away with this for an internal function; it would not michael@0: * be acceptable for a public API. michael@0: */ michael@0: void michael@0: DigitList::set(const StringPiece &source, UErrorCode &status, uint32_t /*fastpathBits*/) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: #if 0 michael@0: if(fastpathBits==(kFastpathOk|kNoDecimal)) { michael@0: int32_t size = source.size(); michael@0: const char *data = source.data(); michael@0: int64_t r = 0; michael@0: int64_t m = 1; michael@0: // fast parse michael@0: while(size>0) { michael@0: char ch = data[--size]; michael@0: if(ch=='+') { michael@0: break; michael@0: } else if(ch=='-') { michael@0: r = -r; michael@0: break; michael@0: } else { michael@0: int64_t d = ch-'0'; michael@0: //printf("CH[%d]=%c, %d, *=%d\n", size,ch, (int)d, (int)m); michael@0: r+=(d)*m; michael@0: m *= 10; michael@0: } michael@0: } michael@0: //printf("R=%d\n", r); michael@0: set(r); michael@0: } else michael@0: #endif michael@0: { michael@0: // Figure out a max number of digits to use during the conversion, and michael@0: // resize the number up if necessary. michael@0: int32_t numDigits = source.length(); michael@0: if (numDigits > fContext.digits) { michael@0: // fContext.digits == fStorage.getCapacity() michael@0: decNumber *t = fStorage.resize(numDigits, fStorage.getCapacity()); michael@0: if (t == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: fDecNumber = t; michael@0: fContext.digits = numDigits; michael@0: } michael@0: michael@0: fContext.status = 0; michael@0: uprv_decNumberFromString(fDecNumber, source.data(), &fContext); michael@0: if ((fContext.status & DEC_Conversion_syntax) != 0) { michael@0: status = U_DECIMAL_NUMBER_SYNTAX_ERROR; michael@0: } michael@0: } michael@0: internalClear(); michael@0: } michael@0: michael@0: /** michael@0: * Set the digit list to a representation of the given double value. michael@0: * This method supports both fixed-point and exponential notation. michael@0: * @param source Value to be converted. michael@0: */ michael@0: void michael@0: DigitList::set(double source) michael@0: { michael@0: // for now, simple implementation; later, do proper IEEE stuff michael@0: char rep[MAX_DIGITS + 8]; // Extra space for '+', '.', e+NNN, and '\0' (actually +8 is enough) michael@0: michael@0: // Generate a representation of the form /[+-][0-9].[0-9]+e[+-][0-9]+/ michael@0: // Can also generate /[+-]nan/ or /[+-]inf/ michael@0: // TODO: Use something other than sprintf() here, since it's behavior is somewhat platform specific. michael@0: // That is why infinity is special cased here. michael@0: if (uprv_isInfinite(source)) { michael@0: if (uprv_isNegativeInfinity(source)) { michael@0: uprv_strcpy(rep,"-inf"); // Handle negative infinity michael@0: } else { michael@0: uprv_strcpy(rep,"inf"); michael@0: } michael@0: } else { michael@0: sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source); michael@0: } michael@0: U_ASSERT(uprv_strlen(rep) < sizeof(rep)); michael@0: michael@0: // uprv_decNumberFromString() will parse the string expecting '.' as a michael@0: // decimal separator, however sprintf() can use ',' in certain locales. michael@0: // Overwrite a ',' with '.' here before proceeding. michael@0: char *decimalSeparator = strchr(rep, ','); michael@0: if (decimalSeparator != NULL) { michael@0: *decimalSeparator = '.'; michael@0: } michael@0: michael@0: // Create a decNumber from the string. michael@0: uprv_decNumberFromString(fDecNumber, rep, &fContext); michael@0: uprv_decNumberTrim(fDecNumber); michael@0: internalSetDouble(source); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /* michael@0: * Multiply michael@0: * The number will be expanded if need be to retain full precision. michael@0: * In practice, for formatting, multiply is by 10, 100 or 1000, so more digits michael@0: * will not be required for this use. michael@0: */ michael@0: void michael@0: DigitList::mult(const DigitList &other, UErrorCode &status) { michael@0: fContext.status = 0; michael@0: int32_t requiredDigits = this->digits() + other.digits(); michael@0: if (requiredDigits > fContext.digits) { michael@0: reduce(); // Remove any trailing zeros michael@0: int32_t requiredDigits = this->digits() + other.digits(); michael@0: ensureCapacity(requiredDigits, status); michael@0: } michael@0: uprv_decNumberMultiply(fDecNumber, fDecNumber, other.fDecNumber, &fContext); michael@0: internalClear(); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /* michael@0: * Divide michael@0: * The number will _not_ be expanded for inexact results. michael@0: * TODO: probably should expand some, for rounding increments that michael@0: * could add a few digits, e.g. .25, but not expand arbitrarily. michael@0: */ michael@0: void michael@0: DigitList::div(const DigitList &other, UErrorCode &status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: uprv_decNumberDivide(fDecNumber, fDecNumber, other.fDecNumber, &fContext); michael@0: internalClear(); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /* michael@0: * ensureCapacity. Grow the digit storage for the number if it's less than the requested michael@0: * amount. Never reduce it. Available size is kept in fContext.digits. michael@0: */ michael@0: void michael@0: DigitList::ensureCapacity(int32_t requestedCapacity, UErrorCode &status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: if (requestedCapacity <= 0) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: if (requestedCapacity > DEC_MAX_DIGITS) { michael@0: // Don't report an error for requesting too much. michael@0: // Arithemetic Results will be rounded to what can be supported. michael@0: // At 999,999,999 max digits, exceeding the limit is not too likely! michael@0: requestedCapacity = DEC_MAX_DIGITS; michael@0: } michael@0: if (requestedCapacity > fContext.digits) { michael@0: decNumber *newBuffer = fStorage.resize(requestedCapacity, fStorage.getCapacity()); michael@0: if (newBuffer == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: fContext.digits = requestedCapacity; michael@0: fDecNumber = newBuffer; michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: /** michael@0: * Round the representation to the given number of digits. michael@0: * @param maximumDigits The maximum number of digits to be shown. michael@0: * Upon return, count will be less than or equal to maximumDigits. michael@0: */ michael@0: void michael@0: DigitList::round(int32_t maximumDigits) michael@0: { michael@0: int32_t savedDigits = fContext.digits; michael@0: fContext.digits = maximumDigits; michael@0: uprv_decNumberPlus(fDecNumber, fDecNumber, &fContext); michael@0: fContext.digits = savedDigits; michael@0: uprv_decNumberTrim(fDecNumber); michael@0: internalClear(); michael@0: } michael@0: michael@0: michael@0: void michael@0: DigitList::roundFixedPoint(int32_t maximumFractionDigits) { michael@0: trim(); // Remove trailing zeros. michael@0: if (fDecNumber->exponent >= -maximumFractionDigits) { michael@0: return; michael@0: } michael@0: decNumber scale; // Dummy decimal number, but with the desired number of michael@0: uprv_decNumberZero(&scale); // fraction digits. michael@0: scale.exponent = -maximumFractionDigits; michael@0: scale.lsu[0] = 1; michael@0: michael@0: uprv_decNumberQuantize(fDecNumber, fDecNumber, &scale, &fContext); michael@0: trim(); michael@0: internalClear(); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: void michael@0: DigitList::toIntegralValue() { michael@0: uprv_decNumberToIntegralValue(fDecNumber, fDecNumber, &fContext); michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: UBool michael@0: DigitList::isZero() const michael@0: { michael@0: return decNumberIsZero(fDecNumber); michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: #endif // #if !UCONFIG_NO_FORMATTING michael@0: michael@0: //eof