summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/digitlst.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu/source/i18n/digitlst.cpp')
-rw-r--r--intl/icu/source/i18n/digitlst.cpp1094
1 files changed, 1094 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/digitlst.cpp b/intl/icu/source/i18n/digitlst.cpp
new file mode 100644
index 000000000..ab5b5571a
--- /dev/null
+++ b/intl/icu/source/i18n/digitlst.cpp
@@ -0,0 +1,1094 @@
+// Copyright (C) 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 <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits>
+
+#if !defined(U_USE_STRTOD_L)
+# if U_PLATFORM_USES_ONLY_WIN32_API
+# define U_USE_STRTOD_L 1
+# 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_PLATFORM_USES_ONLY_WIN32_API || U_PLATFORM == U_PF_CYGWIN
+# include <locale.h>
+# else
+# include <xlocale.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 {
+ sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source);
+ }
+ 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