Index: icu46/source/i18n/digitlst.cpp |
=================================================================== |
--- icu46/source/i18n/digitlst.cpp (revision 0) |
+++ icu46/source/i18n/digitlst.cpp (revision 0) |
@@ -0,0 +1,861 @@ |
+/* |
+********************************************************************** |
+* 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 "charstr.h" |
+#include "cmemory.h" |
+#include "cstring.h" |
+#include "putilimp.h" |
+#include "uassert.h" |
+#include <stdlib.h> |
+#include <limits.h> |
+#include <string.h> |
+#include <stdio.h> |
+#include <limits> |
+ |
+// *************************************************************************** |
+// class DigitList |
+// A wrapper onto decNumber. |
+// Used to be standalone. |
+// *************************************************************************** |
+ |
+/** |
+ * This is the zero digit. The base for the digits returned by getDigit() |
+ * Note that it is the platform invariant digit, and is not Unicode. |
+ */ |
+#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(); |
+ |
+ fDecNumber = fStorage.getAlias(); |
+ uprv_decNumberZero(fDecNumber); |
+ |
+ fDouble = 0.0; |
+ fHaveDouble = TRUE; |
+} |
+ |
+// ------------------------------------- |
+ |
+DigitList::~DigitList() |
+{ |
+} |
+ |
+// ------------------------------------- |
+// copy constructor |
+ |
+DigitList::DigitList(const DigitList &other) |
+{ |
+ fDecNumber = fStorage.getAlias(); |
+ *this = other; |
+} |
+ |
+ |
+// ------------------------------------- |
+// assignment operator |
+ |
+DigitList& |
+DigitList::operator=(const DigitList& other) |
+{ |
+ if (this != &other) |
+ { |
+ uprv_memcpy(&fContext, &other.fContext, sizeof(decContext)); |
+ |
+ if (other.fStorage.getCapacity() > fStorage.getCapacity()) { |
+ fDecNumber = fStorage.resize(other.fStorage.getCapacity()); |
+ } |
+ // Always reset the fContext.digits, even if fDecNumber was not reallocated, |
+ // because above we copied fContext from other.fContext. |
+ fContext.digits = fStorage.getCapacity(); |
+ 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<count); |
+ U_ASSERT(v>='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(i<count); |
+ return fDecNumber->lsu[count-i-1] + '0'; |
+} |
+ |
+// copied from DigitList::getDigit() |
+uint8_t |
+DigitList::getDigitValue(int32_t i) { |
+ int32_t count = fDecNumber->digits; |
+ U_ASSERT(i<count); |
+ return fDecNumber->lsu[count-i-1]; |
+} |
+ |
+// ------------------------------------- |
+// 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<DigitList *>(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<double>::has_infinity) { |
+ nonConstThis->fDouble = std::numeric_limits<double>::infinity(); |
+ } else { |
+ nonConstThis->fDouble = std::numeric_limits<double>::max(); |
+ } |
+ if (!isPositive()) { |
+ nonConstThis->fDouble = -fDouble; |
+ } |
+ } else { |
+ MaybeStackArray<char, MAX_DBL_DIGITS+18> 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(CharString &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 + 14; |
+ int32_t capacity = 0; |
+ char *buffer = str.clear().getAppendBuffer(maxLength, 0, capacity, status); |
+ if (U_FAILURE(status)) { |
+ return; // Memory allocation error on growing the string. |
+ } |
+ U_ASSERT(capacity >= maxLength); |
+ uprv_decNumberToString(this->fDecNumber, buffer); |
+ U_ASSERT((int32_t)uprv_strlen(buffer) <= maxLength); |
+ str.append(buffer, -1, 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 though 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 == fStorage.getCapacity() |
+ decNumber *t = fStorage.resize(numDigits, fStorage.getCapacity()); |
+ if (t == NULL) { |
+ status = U_MEMORY_ALLOCATION_ERROR; |
+ return; |
+ } |
+ fDecNumber = t; |
+ fContext.digits = numDigits; |
+ } |
+ |
+ 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) { |
+ decNumber *newBuffer = fStorage.resize(requestedCapacity, fStorage.getCapacity()); |
+ if (newBuffer == NULL) { |
+ status = U_MEMORY_ALLOCATION_ERROR; |
+ return; |
+ } |
+ fContext.digits = requestedCapacity; |
+ fDecNumber = newBuffer; |
+ } |
+} |
+ |
+// ------------------------------------- |
+ |
+/** |
+ * 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 |
Property changes on: icu46/source/i18n/digitlst.cpp |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |