]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/digitlst.cpp
ICU-511.25.tar.gz
[apple/icu.git] / icuSources / i18n / digitlst.cpp
index 7aa329e2e4569e16655fe02085c296a583299e80..50aa7a85eb5a87450aa1b6f56fd90ab1c73c3204 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-*   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);
 }
 
 // -------------------------------------
@@ -79,10 +91,11 @@ DigitList::~DigitList()
 
 DigitList::DigitList(const DigitList &other)
 {
-    fDigits = fDecimalDigits + 1;   // skip the decimal
+    fDecNumber = fStorage.getAlias();
     *this = other;
 }
 
+
 // -------------------------------------
 // assignment operator
 
@@ -91,253 +104,542 @@ DigitList::operator=(const DigitList& other)
 {
     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
@@ -346,162 +648,251 @@ DigitList::fitsIntoLong(UBool ignoreNegativeZero) /*const*/
 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;
     }
 }
 
@@ -512,155 +903,50 @@ DigitList::set(double source, int32_t maximumDigits, UBool fixedPoint)
  * @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