diff options
Diffstat (limited to 'Build/source/libs/icu/icu-src/source/i18n/digitlst.cpp')
-rw-r--r-- | Build/source/libs/icu/icu-src/source/i18n/digitlst.cpp | 1143 |
1 files changed, 0 insertions, 1143 deletions
diff --git a/Build/source/libs/icu/icu-src/source/i18n/digitlst.cpp b/Build/source/libs/icu/icu-src/source/i18n/digitlst.cpp deleted file mode 100644 index 978bb60004f..00000000000 --- a/Build/source/libs/icu/icu-src/source/i18n/digitlst.cpp +++ /dev/null @@ -1,1143 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -********************************************************************** -* Copyright (C) 1997-2015, 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 -****************************************************************************** -*/ - -#if defined(__CYGWIN__) && !defined(_GNU_SOURCE) -#define _GNU_SOURCE -#endif - -#include "digitlst.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/putil.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "mutex.h" -#include "putilimp.h" -#include "uassert.h" -#include "digitinterval.h" -#include "ucln_in.h" -#include "umutex.h" -#include "double-conversion.h" -#include <stdlib.h> -#include <limits.h> -#include <string.h> -#include <stdio.h> -#include <limits> - -using icu::double_conversion::DoubleToStringConverter; - -#if !defined(U_USE_STRTOD_L) -# if U_PLATFORM_USES_ONLY_WIN32_API -# define U_USE_STRTOD_L 1 -# define U_HAVE_XLOCALE_H 0 -# elif defined(U_HAVE_STRTOD_L) -# define U_USE_STRTOD_L U_HAVE_STRTOD_L -# else -# define U_USE_STRTOD_L 0 -# endif -#endif - -#if U_USE_STRTOD_L -# if U_HAVE_XLOCALE_H -# include <xlocale.h> -# else -# include <locale.h> -# endif -#endif - -// *************************************************************************** -// 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' - - -/* 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); - - internalSetDouble(0.0); -} - -// ------------------------------------- - -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 is lazily created and cached. - // Avoid potential races with that happening with other.fDouble - // while we are doing the assignment. - Mutex mutex; - - if(other.fHave==kDouble) { - fUnion.fDouble = other.fUnion.fDouble; - } - fHave = other.fHave; - } - } - 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); - internalSetDouble(0.0); -} - - -/** - * 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; -} - - -// ------------------------------------- -// -// setRoundingMode() -// For most modes, the meaning and names are the same between the decNumber library -// (which DigitList follows) and the ICU Formatting Rounding Mode values. -// The flag constants are different, however. -// -// Note that ICU's kRoundingUnnecessary is not implemented directly by DigitList. -// This mode, inherited from Java, means that numbers that would not format exactly -// will return an error when formatting is attempted. - -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; - case DecimalFormat::kRoundUnnecessary: r = DEC_ROUND_HALF_EVEN; 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; - } - internalClear(); -} -// ------------------------------------- - -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; - internalClear(); -} - -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; - internalClear(); -} - -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; - internalClear(); -} - -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--; - } - } - internalClear(); -} - -// ------------------------------------- - -/** - * Currently, getDouble() depends on strtod() 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 -{ - { - Mutex mutex; - if (fHave == kDouble) { - return fUnion.fDouble; - } - } - - double tDouble = 0.0; - if (isZero()) { - tDouble = 0.0; - if (decNumberIsNegative(fDecNumber)) { - tDouble /= -1; - } - } else if (isInfinite()) { - if (std::numeric_limits<double>::has_infinity) { - tDouble = std::numeric_limits<double>::infinity(); - } else { - tDouble = std::numeric_limits<double>::max(); - } - if (!isPositive()) { - tDouble = -tDouble; //this was incorrectly "-fDouble" originally. - } - } 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.getAlias()); - // TODO: how many extra digits should be included for an accurate conversion? - } else { - uprv_decNumberToString(this->fDecNumber, s.getAlias()); - } - U_ASSERT(uprv_strlen(&s[0]) < MAX_DBL_DIGITS+18); - - char *end = NULL; - tDouble = decimalStrToDouble(s.getAlias(), &end); - } - { - Mutex mutex; - DigitList *nonConstThis = const_cast<DigitList *>(this); - nonConstThis->internalSetDouble(tDouble); - } - return tDouble; -} - -#if U_USE_STRTOD_L && U_PLATFORM_USES_ONLY_WIN32_API -# define locale_t _locale_t -# define freelocale _free_locale -# define strtod_l _strtod_l -#endif - -#if U_USE_STRTOD_L -static locale_t gCLocale = (locale_t)0; -#endif -static icu::UInitOnce gCLocaleInitOnce = U_INITONCE_INITIALIZER; - -U_CDECL_BEGIN -// Cleanup callback func -static UBool U_CALLCONV digitList_cleanup(void) -{ -#if U_USE_STRTOD_L - if (gCLocale != (locale_t)0) { - freelocale(gCLocale); - } -#endif - return TRUE; -} -// C Locale initialization func -static void U_CALLCONV initCLocale(void) { - ucln_i18n_registerCleanup(UCLN_I18N_DIGITLIST, digitList_cleanup); -#if U_USE_STRTOD_L -# if U_PLATFORM_USES_ONLY_WIN32_API - gCLocale = _create_locale(LC_ALL, "C"); -# else - gCLocale = newlocale(LC_ALL_MASK, "C", (locale_t)0); -# endif -#endif -} -U_CDECL_END - -double -DigitList::decimalStrToDouble(char *decstr, char **end) { - umtx_initOnce(gCLocaleInitOnce, &initCLocale); -#if U_USE_STRTOD_L - return strtod_l(decstr, end, gCLocale); -#else - char *decimalPt = strchr(decstr, '.'); - if (decimalPt) { - // We need to know the decimal separator character that will be used with strtod(). - // Depends on the C runtime global locale. - // Most commonly is '.' - char rep[MAX_DIGITS]; - sprintf(rep, "%+1.1f", 1.0); - *decimalPt = rep[2]; - } - return uprv_strtod(decstr, end); -#endif -} - -// ------------------------------------- - -/** - * 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 (getUpperExponent() > 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. Truncate if there is a fractional part. - * Return zero if the number cannot be represented. - */ -int64_t DigitList::getInt64() /*const*/ { - // TODO: fast conversion if fHave == fDouble - - // Truncate if non-integer. - // Return 0 if out of range. - // Range of in64_t is -9223372036854775808 to 9223372036854775807 (19 digits) - // - if (getUpperExponent() > 19) { - // Overflow, absolute value too big. - return 0; - } - - // The number of integer digits may differ from the number of digits stored - // in the decimal number. - // for 12.345 numIntDigits = 2, number->digits = 5 - // for 12E4 numIntDigits = 6, number->digits = 2 - // The conversion ignores the fraction digits in the first case, - // and fakes up extra zero digits in the second. - // TODO: It would be faster to store a table of powers of ten to multiply by - // instead of looping over zero digits, multiplying each time. - - int32_t numIntDigits = getUpperExponent(); - uint64_t value = 0; - for (int32_t i = 0; i < numIntDigits; i++) { - // Loop is iterating over digits starting with the most significant. - // Numbers are stored with the least significant digit at index zero. - int32_t digitIndex = fDecNumber->digits - i - 1; - int32_t v = (digitIndex >= 0) ? fDecNumber->lsu[digitIndex] : 0; - value = value * (uint64_t)10 + (uint64_t)v; - } - - if (decNumberIsNegative(fDecNumber)) { - 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 (numIntDigits == 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 (getUpperExponent() < 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 (getUpperExponent() < 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); - internalSetDouble(source); -} - -// ------------------------------------- -/** - * Set an int64, via decnumber - */ -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); - internalSetDouble(static_cast<double>(source)); -} - -// ------------------------------------- -/** - * 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(StringPiece source, UErrorCode &status, uint32_t /*fastpathBits*/) { - if (U_FAILURE(status)) { - return; - } - -#if 0 - if(fastpathBits==(kFastpathOk|kNoDecimal)) { - int32_t size = source.size(); - const char *data = source.data(); - int64_t r = 0; - int64_t m = 1; - // fast parse - while(size>0) { - char ch = data[--size]; - if(ch=='+') { - break; - } else if(ch=='-') { - r = -r; - break; - } else { - int64_t d = ch-'0'; - //printf("CH[%d]=%c, %d, *=%d\n", size,ch, (int)d, (int)m); - r+=(d)*m; - m *= 10; - } - } - //printf("R=%d\n", r); - set(r); - } else -#endif - { - // 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; - } - } - internalClear(); -} - -/** - * 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].[0-9]+e[+-][0-9]+/ - // Can also generate /[+-]nan/ or /[+-]inf/ - // TODO: Use something other than sprintf() here, since it's behavior is somewhat platform specific. - // That is why infinity is special cased here. - if (uprv_isInfinite(source)) { - if (uprv_isNegativeInfinity(source)) { - uprv_strcpy(rep,"-inf"); // Handle negative infinity - } else { - uprv_strcpy(rep,"inf"); - } - } else if (uprv_isNaN(source)) { - uprv_strcpy(rep, "NaN"); - } else { - bool sign; - int32_t length; - int32_t point; - DoubleToStringConverter::DoubleToAscii( - source, - DoubleToStringConverter::DtoaMode::SHORTEST, - 0, - rep + 1, - sizeof(rep), - &sign, - &length, - &point - ); - - // Convert the raw buffer into a string for decNumber - int32_t power = point - length; - if (sign) { - rep[0] = '-'; - } else { - rep[0] = '0'; - } - length++; - rep[length++] = 'E'; - if (power < 0) { - rep[length++] = '-'; - power = -power; - } else { - rep[length++] = '+'; - } - if (power < 10) { - rep[length++] = power + '0'; - } else if (power < 100) { - rep[length++] = (power / 10) + '0'; - rep[length++] = (power % 10) + '0'; - } else { - U_ASSERT(power < 1000); - rep[length + 2] = (power % 10) + '0'; - power /= 10; - rep[length + 1] = (power % 10) + '0'; - power /= 10; - rep[length] = power + '0'; - length += 3; - } - rep[length++] = 0; - } - U_ASSERT(uprv_strlen(rep) < sizeof(rep)); - - // uprv_decNumberFromString() will parse the string expecting '.' as a - // decimal separator, however sprintf() can use ',' in certain locales. - // Overwrite a ',' with '.' here before proceeding. - char *decimalSeparator = strchr(rep, ','); - if (decimalSeparator != NULL) { - *decimalSeparator = '.'; - } - - // Create a decNumber from the string. - uprv_decNumberFromString(fDecNumber, rep, &fContext); - uprv_decNumberTrim(fDecNumber); - internalSetDouble(source); -} - -// ------------------------------------- - -/* - * 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) { - if (U_FAILURE(status)) { - return; - } - 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); - internalClear(); -} - -// ------------------------------------- - -/* - * 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); - internalClear(); -} - -// ------------------------------------- - -/* - * 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) -{ - reduce(); - if (maximumDigits >= fDecNumber->digits) { - return; - } - int32_t savedDigits = fContext.digits; - fContext.digits = maximumDigits; - uprv_decNumberPlus(fDecNumber, fDecNumber, &fContext); - fContext.digits = savedDigits; - uprv_decNumberTrim(fDecNumber); - reduce(); - internalClear(); -} - - -void -DigitList::roundFixedPoint(int32_t maximumFractionDigits) { - reduce(); // 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); - reduce(); - internalClear(); -} - -// ------------------------------------- - -void -DigitList::toIntegralValue() { - uprv_decNumberToIntegralValue(fDecNumber, fDecNumber, &fContext); -} - - -// ------------------------------------- -UBool -DigitList::isZero() const -{ - return decNumberIsZero(fDecNumber); -} - -// ------------------------------------- -int32_t -DigitList::getUpperExponent() const { - return fDecNumber->digits + fDecNumber->exponent; -} - -DigitInterval & -DigitList::getSmallestInterval(DigitInterval &result) const { - result.setLeastSignificantInclusive(fDecNumber->exponent); - result.setMostSignificantExclusive(getUpperExponent()); - return result; -} - -uint8_t -DigitList::getDigitByExponent(int32_t exponent) const { - int32_t idx = exponent - fDecNumber->exponent; - if (idx < 0 || idx >= fDecNumber->digits) { - return 0; - } - return fDecNumber->lsu[idx]; -} - -void -DigitList::appendDigitsTo(CharString &str, UErrorCode &status) const { - str.append((const char *) fDecNumber->lsu, fDecNumber->digits, status); -} - -void -DigitList::roundAtExponent(int32_t exponent, int32_t maxSigDigits) { - reduce(); - if (maxSigDigits < fDecNumber->digits) { - int32_t minExponent = getUpperExponent() - maxSigDigits; - if (exponent < minExponent) { - exponent = minExponent; - } - } - if (exponent <= fDecNumber->exponent) { - return; - } - int32_t digits = getUpperExponent() - exponent; - if (digits > 0) { - round(digits); - } else { - roundFixedPoint(-exponent); - } -} - -void -DigitList::quantize(const DigitList &quantity, UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - div(quantity, status); - roundAtExponent(0); - mult(quantity, status); - reduce(); -} - -int32_t -DigitList::getScientificExponent( - int32_t minIntDigitCount, int32_t exponentMultiplier) const { - // The exponent for zero is always zero. - if (isZero()) { - return 0; - } - int32_t intDigitCount = getUpperExponent(); - int32_t exponent; - if (intDigitCount >= minIntDigitCount) { - int32_t maxAdjustment = intDigitCount - minIntDigitCount; - exponent = (maxAdjustment / exponentMultiplier) * exponentMultiplier; - } else { - int32_t minAdjustment = minIntDigitCount - intDigitCount; - exponent = ((minAdjustment + exponentMultiplier - 1) / exponentMultiplier) * -exponentMultiplier; - } - return exponent; -} - -int32_t -DigitList::toScientific( - int32_t minIntDigitCount, int32_t exponentMultiplier) { - int32_t exponent = getScientificExponent( - minIntDigitCount, exponentMultiplier); - shiftDecimalRight(-exponent); - return exponent; -} - -void -DigitList::shiftDecimalRight(int32_t n) { - fDecNumber->exponent += n; - internalClear(); -} - -U_NAMESPACE_END -#endif // #if !UCONFIG_NO_FORMATTING - -//eof |