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