/*
**********************************************************************
-* Copyright (C) 1997-2005, International Business Machines
+* Copyright (C) 1997-2012, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
*
******************************************************************************
*/
-#include "unicode/putil.h"
#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 <stdlib.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
+#include <limits>
// ***************************************************************************
// class DigitList
-// This class handles the transcoding between numeric values and strings of
-// characters. Only handles as non-negative numbers.
+// A wrapper onto decNumber.
+// Used to be standalone.
// ***************************************************************************
/**
- * This is the zero digit. Array elements fDigits[i] have values from
- * kZero to kZero + 9. Typically, this is '0'.
+ * 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";
+//static const char LONG_MIN_REP[] = "2147483648";
+//static const char I64_MIN_REP[] = "9223372036854775808";
-enum {
- LONG_MIN_REP_LENGTH = sizeof(LONG_MIN_REP) - 1, //Ignore the NULL at the end
- I64_MIN_REP_LENGTH = sizeof(I64_MIN_REP) - 1 //Ignore the NULL at the end
-};
-U_NAMESPACE_BEGIN
+static const uint8_t DIGIT_HAVE_NONE=0;
+static const uint8_t DIGIT_HAVE_DOUBLE=1;
+static const uint8_t DIGIT_HAVE_INT64=2;
+U_NAMESPACE_BEGIN
// -------------------------------------
// default constructor
DigitList::DigitList()
{
- fDigits = fDecimalDigits + 1; // skip the decimal
- clear();
+ 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(const DigitList &other)
{
- fDigits = fDecimalDigits + 1; // skip the decimal
+ fDecNumber = fStorage.getAlias();
*this = other;
}
+
// -------------------------------------
// assignment operator
{
if (this != &other)
{
- fDecimalAt = other.fDecimalAt;
- fCount = other.fCount;
- fIsPositive = other.fIsPositive;
- fRoundingMode = other.fRoundingMode;
- uprv_strncpy(fDigits, other.fDigits, fCount);
+ 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;
+ } else if(other.fHave==kInt64) {
+ fUnion.fInt64 = other.fUnion.fInt64;
+ }
+ fHave = other.fHave;
+ }
}
return *this;
}
// -------------------------------------
+// operator == (does not exactly match the old DigitList function)
UBool
DigitList::operator==(const DigitList& that) const
{
- return ((this == &that) ||
- (fDecimalAt == that.fDecimalAt &&
- fCount == that.fCount &&
- fIsPositive == that.fIsPositive &&
- fRoundingMode == that.fRoundingMode &&
- uprv_strncmp(fDigits, that.fDigits, fCount) == 0));
+ 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;
}
// -------------------------------------
-// Resets the digit list; sets all the digits to zero.
+// 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;
+ }
+}
-void
-DigitList::clear()
-{
- fDecimalAt = 0;
- fCount = 0;
- fIsPositive = TRUE;
- fRoundingMode = DecimalFormat::kRoundHalfEven;
- // Don't bother initializing fDigits because fCount is 0.
+// -------------------------------------
+// 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 number into a base 10 string representation, and NULL terminates it.
+ * 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
- * @param outputLen The maximum number of characters to put into outputStr
- * (including NULL).
+ * @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, int32_t outputLen)
-{
- char buffer[MAX_DIGITS + 1];
- int32_t bufferLen;
- int32_t result;
+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;
- if (outputLen > MAX_DIGITS) {
- outputLen = MAX_DIGITS; // Ignore NULL
+ int64_t n = number;
+ if (number < 0) { // Negative numbers are slightly larger than a postive
+ outputStr[--destIdx] = (char)(-(n % 10) + kZero);
+ n /= -10;
}
- else if (outputLen < 3) {
- return 0; // Not enough room
+ do {
+ outputStr[--destIdx] = (char)(n % 10 + kZero);
+ n /= 10;
+ } while (n > 0);
+
+ if (number < 0) {
+ outputStr[--destIdx] = '-';
}
- bufferLen = outputLen;
+ // 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);
- if (number < 0) { // Negative numbers are slightly larger than a postive
- buffer[bufferLen--] = (char)(-(number % 10) + kZero);
- number /= -10;
- *(outputStr++) = '-';
+ 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);
}
- else {
- *(outputStr++) = '+'; // allow +0
+ uprv_decContextSetRounding(&fContext, r);
+
+}
+
+
+// -------------------------------------
+
+void
+DigitList::setPositive(UBool s) {
+ if (s) {
+ fDecNumber->bits &= ~DECNEG;
+ } else {
+ fDecNumber->bits |= DECNEG;
}
- while (bufferLen >= 0 && number) { // Output the number
- buffer[bufferLen--] = (char)(number % 10 + kZero);
- number /= 10;
+ 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();
+}
- result = outputLen - bufferLen++;
+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;
+}
- while (bufferLen <= outputLen) { // Copy the number to output
- *(outputStr++) = buffer[bufferLen++];
+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;
}
- *outputStr = 0; // NULL terminate.
- return result;
+ 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 atof() to do its conversion.
+ * 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*/
+DigitList::getDouble() const
{
- double value;
-
- if (fCount == 0) {
- value = 0.0;
- }
- else {
- char* end = NULL;
- if (!gDecimal) {
- 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];
+ static char gDecimal = 0;
+ char decimalSeparator;
+ {
+ Mutex mutex;
+ if (fHave == kDouble) {
+ return fUnion.fDouble;
+ } else if(fHave == kInt64) {
+ return (double)fUnion.fInt64;
}
+ decimalSeparator = gDecimal;
+ }
- *fDecimalDigits = gDecimal;
- *(fDigits+fCount) = 'e'; // add an e after the digits.
- formatBase10(fDecimalAt,
- fDigits + fCount + 1, // skip the 'e'
- MAX_DEC_DIGITS - fCount - 3); // skip the 'e' and '.'
- value = uprv_strtod(fDecimalDigits, &end);
+ if (decimalSeparator == 0) {
+ // We need to know the decimal separator character that will be used with strtod().
+ // Depends on the C runtime global locale.
+ // Most commonly is '.'
+ // TODO: caching could fail if the global locale is changed on the fly.
+ char rep[MAX_DIGITS];
+ sprintf(rep, "%+1.1f", 1.0);
+ decimalSeparator = rep[2];
}
- return fIsPositive ? value : -value;
+ 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);
+
+ if (decimalSeparator != '.') {
+ char *decimalPt = strchr(s.getAlias(), '.');
+ if (decimalPt != NULL) {
+ *decimalPt = decimalSeparator;
+ }
+ }
+ char *end = NULL;
+ tDouble = uprv_strtod(s.getAlias(), &end);
+ }
+ {
+ Mutex mutex;
+ DigitList *nonConstThis = const_cast<DigitList *>(this);
+ nonConstThis->internalSetDouble(tDouble);
+ gDecimal = decimalSeparator;
+ }
+ return tDouble;
}
// -------------------------------------
/**
- * Make sure that fitsIntoLong() is called before calling this function.
+ * 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*/
{
- if (fCount == fDecimalAt) {
- int32_t value;
-
- fDigits[fCount] = 0; // NULL terminate
-
- // This conversion is bad on 64-bit platforms when we want to
- // be able to return a 64-bit number [grhoten]
- *fDecimalDigits = fIsPositive ? '+' : '-';
- value = (int32_t)atol(fDecimalDigits);
- return value;
+ int32_t result = 0;
+ if (fDecNumber->digits + fDecNumber->exponent > 10) {
+ // Overflow, absolute value too big.
+ return result;
}
- else {
- // This is 100% accurate in c++ because if we are representing
- // an integral value, we suffer nothing in the conversion to
- // double. If we are to support 64-bit longs later, getLong()
- // must be rewritten. [LIU]
- return (int32_t)getDouble();
+ 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;
}
/**
- * Make sure that fitsIntoInt64() is called before calling this function.
+ * 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*/
-{
- if (fCount == fDecimalAt) {
- uint64_t value;
-
- fDigits[fCount] = 0; // NULL terminate
+int64_t DigitList::getInt64() /*const*/ {
+ if(fHave==kInt64) {
+ return fUnion.fInt64;
+ }
+ // Truncate if non-integer.
+ // 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;
+ }
- // This conversion is bad on 64-bit platforms when we want to
- // be able to return a 64-bit number [grhoten]
- *fDecimalDigits = fIsPositive ? '+' : '-';
+ // 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 = fDecNumber->digits + fDecNumber->exponent;
+ 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 (fCount < LONG_MIN_REP_LENGTH) {
- return (int64_t)atol(fDecimalDigits);
+ 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;
+}
- // too big for atol, hand-roll atoi64
- value = 0;
- for (int i = 0; i < fCount; ++i) {
- int v = fDigits[i] - kZero;
- value = value * (uint64_t)10 + (uint64_t)v;
- }
- if (!fIsPositive) {
- value = ~value;
- value += 1;
- }
- int64_t svalue = (int64_t)value;
- 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;
}
- else {
- // todo: figure out best approach
- // This is 100% accurate in c++ because if we are representing
- // an integral value, we suffer nothing in the conversion to
- // double. If we are to support 64-bit longs later, getLong()
- // must be rewritten. [LIU]
- return (int64_t)getDouble();
+ // 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 the number represented by this object can fit into
- * a long.
+ * Return true if this is an integer value that can be held
+ * by an int32_t type.
*/
UBool
DigitList::fitsIntoLong(UBool ignoreNegativeZero) /*const*/
{
- // Figure out if the result will fit in a long. We have to
- // first look for nonzero digits after the decimal point;
- // then check the size.
-
- // Trim trailing zeros after the decimal point. This does not change
- // the represented value.
- while (fCount > fDecimalAt && fCount > 0 && fDigits[fCount - 1] == kZero)
- --fCount;
-
- if (fCount == 0) {
- // Positive zero fits into a long, but negative zero can only
- // be represented as a double. - bug 4162852
- return fIsPositive || ignoreNegativeZero;
+ if (decNumberIsSpecial(this->fDecNumber)) {
+ // NaN or Infinity. Does not fit in int32.
+ return FALSE;
}
-
- // If the digit list represents a double or this number is too
- // big for a long.
- if (fDecimalAt < fCount || fDecimalAt > LONG_MIN_REP_LENGTH)
+ uprv_decNumberTrim(this->fDecNumber);
+ if (fDecNumber->exponent < 0) {
+ // Number contains fraction digits.
return FALSE;
-
- // If number is small enough to fit in a long
- if (fDecimalAt < LONG_MIN_REP_LENGTH)
+ }
+ 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;
+ }
- // At this point we have fDecimalAt == fCount, and fCount == LONG_MIN_REP_LENGTH.
- // The number will overflow if it is larger than LONG_MAX
- // or smaller than LONG_MIN.
- for (int32_t i=0; i<fCount; ++i)
- {
- char dig = fDigits[i],
- max = LONG_MIN_REP[i];
- if (dig > max)
- return FALSE;
- if (dig < max)
- 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;
+}
- // At this point the first count digits match. If fDecimalAt is less
- // than count, then the remaining digits are zero, and we return true.
- if (fCount < fDecimalAt)
- return TRUE;
- // Now we have a representation of Long.MIN_VALUE, without the leading
- // negative sign. If this represents a positive value, then it does
- // not fit; otherwise it fits.
- return !fIsPositive;
-}
/**
* Return true if the number represented by this object can fit into
UBool
DigitList::fitsIntoInt64(UBool ignoreNegativeZero) /*const*/
{
- // Figure out if the result will fit in a long. We have to
- // first look for nonzero digits after the decimal point;
- // then check the size.
-
- // Trim trailing zeros after the decimal point. This does not change
- // the represented value.
- while (fCount > fDecimalAt && fCount > 0 && fDigits[fCount - 1] == kZero)
- --fCount;
-
- if (fCount == 0) {
- // Positive zero fits into a long, but negative zero can only
- // be represented as a double. - bug 4162852
- return fIsPositive || ignoreNegativeZero;
+ if (decNumberIsSpecial(this->fDecNumber)) {
+ // NaN or Infinity. Does not fit in int32.
+ return FALSE;
}
-
- // If the digit list represents a double or this number is too
- // big for a long.
- if (fDecimalAt < fCount || fDecimalAt > I64_MIN_REP_LENGTH)
+ uprv_decNumberTrim(this->fDecNumber);
+ if (fDecNumber->exponent < 0) {
+ // Number contains fraction digits.
return FALSE;
-
- // If number is small enough to fit in an int64
- if (fDecimalAt < I64_MIN_REP_LENGTH)
- return TRUE;
-
- // At this point we have fDecimalAt == fCount, and fCount == INT64_MIN_REP_LENGTH.
- // The number will overflow if it is larger than U_INT64_MAX
- // or smaller than U_INT64_MIN.
- for (int32_t i=0; i<fCount; ++i)
- {
- char dig = fDigits[i],
- max = I64_MIN_REP[i];
- if (dig > max)
- return FALSE;
- if (dig < max)
- return TRUE;
}
-
- // At this point the first count digits match. If fDecimalAt is less
- // than count, then the remaining digits are zero, and we return true.
- if (fCount < fDecimalAt)
+ 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;
+ }
- // Now we have a representation of INT64_MIN_VALUE, without the leading
- // negative sign. If this represents a positive value, then it does
- // not fit; otherwise it fits.
- return !fIsPositive;
+ // 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, int32_t maximumDigits)
+DigitList::set(int32_t source)
{
- set((int64_t)source, maximumDigits);
+ set((int64_t)source);
+ internalSetDouble(source);
}
// -------------------------------------
/**
- * @param maximumDigits The maximum digits to be generated. If zero,
- * there is no maximum -- generate all digits.
+ * Set an int64, via decnumber
*/
void
-DigitList::set(int64_t source, int32_t maximumDigits)
+DigitList::set(int64_t source)
{
- fCount = fDecimalAt = formatBase10(source, fDecimalDigits, MAX_DIGITS);
+ char str[MAX_DIGITS+2]; // Leave room for sign and trailing nul.
+ formatBase10(source, str);
+ U_ASSERT(uprv_strlen(str) < sizeof(str));
- fIsPositive = (*fDecimalDigits == '+');
-
- // Don't copy trailing zeros
- while (fCount > 1 && fDigits[fCount - 1] == kZero)
- --fCount;
-
- if(maximumDigits > 0)
- round(maximumDigits);
+ uprv_decNumberFromString(fDecNumber, str, &fContext);
+ internalSetDouble(source);
}
+/**
+ * Set an int64, with no decnumber
+ */
+void
+DigitList::setInteger(int64_t source)
+{
+ fDecNumber=NULL;
+ internalSetInt64(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(const 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; must not be Inf, -Inf, Nan,
- * or a value <= 0.
- * @param maximumDigits The most fractional or total digits which should
- * be converted. If total digits, and the value is zero, then
- * there is no maximum -- generate all digits.
- * @param fixedPoint If true, then maximumDigits is the maximum
- * fractional digits to be converted. If false, total digits.
+ * @param source Value to be converted.
*/
void
-DigitList::set(double source, int32_t maximumDigits, UBool fixedPoint)
+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)
- char *digitPtr = fDigits;
- char *repPtr = rep + 2; // +2 to skip the sign and decimal
- int32_t exponent = 0;
- fIsPositive = !uprv_isNegative(source); // Allow +0 and -0
+ // 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 = '.';
+ }
- // Generate a representation of the form /[+-][0-9]+e[+-][0-9]+/
- sprintf(rep, "%+1.*e", MAX_DBL_DIGITS - 1, source);
- fDecimalAt = 0;
- rep[2] = rep[1]; // remove decimal
+ // Create a decNumber from the string.
+ uprv_decNumberFromString(fDecNumber, rep, &fContext);
+ uprv_decNumberTrim(fDecNumber);
+ internalSetDouble(source);
+}
- while (*repPtr == kZero) {
- repPtr++;
- fDecimalAt--; // account for leading zeros
- }
+// -------------------------------------
- while (*repPtr != 'e') {
- *(digitPtr++) = *(repPtr++);
+/*
+ * 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);
}
- fCount = MAX_DBL_DIGITS + fDecimalAt;
+ uprv_decNumberMultiply(fDecNumber, fDecNumber, other.fDecNumber, &fContext);
+ internalClear();
+}
- // Parse an exponent of the form /[eE][+-][0-9]+/
- UBool negExp = (*(++repPtr) == '-');
- while (*(++repPtr) != 0) {
- exponent = 10*exponent + *repPtr - kZero;
- }
- if (negExp) {
- exponent = -exponent;
- }
- fDecimalAt += exponent + 1; // +1 for decimal removal
+// -------------------------------------
- // The negative of the exponent represents the number of leading
- // zeros between the decimal and the first non-zero digit, for
- // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this
- // is more than the maximum fraction digits, then we have an underflow
- // for the printed representation.
- if (fixedPoint && -fDecimalAt >= maximumDigits)
- {
- // If we round 0.0009 to 3 fractional digits, then we have to
- // create a new one digit in the least significant location.
- if (-fDecimalAt == maximumDigits && shouldRoundUp(0)) {
- fCount = 1;
- ++fDecimalAt;
- fDigits[0] = (char)'1';
- } else {
- // Handle an underflow to zero when we round something like
- // 0.0009 to 2 fractional digits.
- fCount = 0;
- }
+/*
+ * 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();
+}
+// -------------------------------------
- // Eliminate digits beyond maximum digits to be displayed.
- // Round up if appropriate. Do NOT round in the special
- // case where maximumDigits == 0 and fixedPoint is FALSE.
- if (fixedPoint || (0 < maximumDigits && maximumDigits < fCount)) {
- round(fixedPoint ? (maximumDigits + fDecimalAt) : maximumDigits);
+/*
+ * 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;
}
- else {
- // Eliminate trailing zeros.
- while (fCount > 1 && fDigits[fCount - 1] == kZero)
- --fCount;
+ 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;
}
}
* @param maximumDigits The maximum number of digits to be shown.
* Upon return, count will be less than or equal to maximumDigits.
*/
-void
+void
DigitList::round(int32_t maximumDigits)
{
- // Eliminate digits beyond maximum digits to be displayed.
- // Round up if appropriate.
- if (maximumDigits >= 0 && maximumDigits < fCount)
- {
- if (shouldRoundUp(maximumDigits)) {
- // Rounding up involved incrementing digits from LSD to MSD.
- // In most cases this is simple, but in a worst case situation
- // (9999..99) we have to adjust the decimalAt value.
- while (--maximumDigits >= 0 && ++fDigits[maximumDigits] > '9')
- ;
-
- if (maximumDigits < 0)
- {
- // We have all 9's, so we increment to a single digit
- // of one and adjust the exponent.
- fDigits[0] = (char) '1';
- ++fDecimalAt;
- maximumDigits = 1; // Adjust the count
- }
- else
- {
- ++maximumDigits; // Increment for use as count
- }
- }
- fCount = maximumDigits;
- }
-
- // Eliminate trailing zeros.
- while (fCount > 1 && fDigits[fCount-1] == kZero) {
- --fCount;
- }
-}
-
-/**
- * Return true if truncating the representation to the given number
- * of digits will result in an increment to the last digit. This
- * method implements the requested rounding mode.
- * [bnf]
- * @param maximumDigits the number of digits to keep, from 0 to
- * <code>count-1</code>. If 0, then all digits are rounded away, and
- * this method returns true if a one should be generated (e.g., formatting
- * 0.09 with "#.#").
- * @return true if digit <code>maximumDigits-1</code> should be
- * incremented
- */
-UBool DigitList::shouldRoundUp(int32_t maximumDigits) const {
- switch (fRoundingMode) {
- case DecimalFormat::kRoundCeiling:
- return fIsPositive;
- case DecimalFormat::kRoundFloor:
- return !fIsPositive;
- case DecimalFormat::kRoundDown:
- return FALSE;
- case DecimalFormat::kRoundUp:
- return TRUE;
- case DecimalFormat::kRoundHalfEven:
- case DecimalFormat::kRoundHalfDown:
- case DecimalFormat::kRoundHalfUp:
- default:
- if (fDigits[maximumDigits] == '5' ) {
- for (int i=maximumDigits+1; i<fCount; ++i) {
- if (fDigits[i] != kZero) {
- return TRUE;
- }
- }
- switch (fRoundingMode) {
- case DecimalFormat::kRoundHalfEven:
- default:
- // Implement IEEE half-even rounding
- return maximumDigits > 0 && (fDigits[maximumDigits-1] % 2 != 0);
- case DecimalFormat::kRoundHalfDown:
- return FALSE;
- case DecimalFormat::kRoundHalfUp:
- return TRUE;
- }
- }
- return (fDigits[maximumDigits] > '5');
- }
+ int32_t savedDigits = fContext.digits;
+ fContext.digits = maximumDigits;
+ uprv_decNumberPlus(fDecNumber, fDecNumber, &fContext);
+ fContext.digits = savedDigits;
+ uprv_decNumberTrim(fDecNumber);
+ internalClear();
}
-// -------------------------------------
-// In the Java implementation, we need a separate set(long) because 64-bit longs
-// have too much precision to fit into a 64-bit double. In C++, longs can just
-// be passed to set(double) as long as they are 32 bits in size. We currently
-// don't implement 64-bit longs in C++, although the code below would work for
-// that with slight modifications. [LIU]
-/*
void
-DigitList::set(long source)
-{
- // handle the special case of zero using a standard exponent of 0.
- // mathematically, the exponent can be any value.
- if (source == 0)
- {
- fcount = 0;
- fDecimalAt = 0;
+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();
+ internalClear();
+}
- // we don't accept negative numbers, with the exception of long_min.
- // long_min is treated specially by being represented as long_max+1,
- // which is actually an impossible signed long value, so there is no
- // ambiguity. we do this for convenience, so digitlist can easily
- // represent the digits of a long.
- bool islongmin = (source == long_min);
- if (islongmin)
- {
- source = -(source + 1); // that is, long_max
- islongmin = true;
- }
- sprintf(fdigits, "%d", source);
-
- // now we need to compute the exponent. it's easy in this case; it's
- // just the same as the count. e.g., 0.123 * 10^3 = 123.
- fcount = strlen(fdigits);
- fDecimalAt = fcount;
-
- // here's how we represent long_max + 1. note that we always know
- // that the last digit of long_max will not be 9, because long_max
- // is of the form (2^n)-1.
- if (islongmin)
- ++fdigits[fcount-1];
+// -------------------------------------
- // finally, we trim off trailing zeros. we don't alter fDecimalAt,
- // so this has no effect on the represented value. we know the first
- // digit is non-zero (see code above), so we only have to check down
- // to fdigits[1].
- while (fcount > 1 && fdigits[fcount-1] == kzero)
- --fcount;
+void
+DigitList::toIntegralValue() {
+ uprv_decNumberToIntegralValue(fDecNumber, fDecNumber, &fContext);
}
-*/
-/**
- * Return true if this object represents the value zero. Anything with
- * no digits, or all zero digits, is zero, regardless of fDecimalAt.
- */
+
+// -------------------------------------
UBool
DigitList::isZero() const
{
- for (int32_t i=0; i<fCount; ++i)
- if (fDigits[i] != kZero)
- return FALSE;
- return TRUE;
+ return decNumberIsZero(fDecNumber);
}
U_NAMESPACE_END
+#endif // #if !UCONFIG_NO_FORMATTING
//eof