]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/digitlst.h
ICU-57131.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / digitlst.h
index 98e1ac9e5a2958885bc8fc5e462819c6499bb88c..1715e2c7bb7ba78a6d10a165d04adc8ef6898770 100644 (file)
@@ -1,7 +1,7 @@
 /*
 ******************************************************************************
 *
-*   Copyright (C) 1997-2004, International Business Machines
+*   Copyright (C) 1997-2015, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *
 ******************************************************************************
 #ifndef DIGITLST_H
 #define DIGITLST_H
  
-#include "unicode/utypes.h"
 #include "unicode/uobject.h"
+
+#if !UCONFIG_NO_FORMATTING
+#include "unicode/decimfmt.h"
 #include <float.h>
+#include "decContext.h"
+#include "decNumber.h"
+#include "cmemory.h"
 
 // Decimal digits in a 64-bit int
-//#define LONG_DIGITS 19 
 #define INT64_DIGITS 19
 
 typedef enum EDigitListValues {
@@ -38,6 +42,7 @@ typedef enum EDigitListValues {
     MAX_DIGITS = MAX_I64_DIGITS,
     MAX_EXPONENT = DBL_DIG,
     DIGIT_PADDING = 3,
+    DEFAULT_DIGITS = 40,   // Initial storage size, will grow as needed.
 
      // "+." + fDigits + "e" + fDecimalAt
     MAX_DEC_DIGITS = MAX_DIGITS + DIGIT_PADDING + MAX_EXPONENT
@@ -45,7 +50,41 @@ typedef enum EDigitListValues {
 
 U_NAMESPACE_BEGIN
 
+class CharString;
+class DigitInterval; 
+
+// Export an explicit template instantiation of the MaybeStackHeaderAndArray that
+//    is used as a data member of DigitList.
+//
+//    MSVC requires this, even though it should not be necessary. 
+//    No direct access to the MaybeStackHeaderAndArray leaks out of the i18n library.
+//
+//    Macintosh produces duplicate definition linker errors with the explicit template
+//    instantiation.
+//
+#if !U_PLATFORM_IS_DARWIN_BASED
+template class U_I18N_API MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS>;
+#endif
+
+
+enum EStackMode { kOnStack };
+
+enum EFastpathBits { kFastpathOk = 1, kNoDecimal = 2 };
+
 /**
+ * Digit List is actually a Decimal Floating Point number.
+ * The original implementation has been replaced by a thin wrapper onto a 
+ * decimal number from the decNumber library.
+ *
+ * The original DigitList API has been retained, to minimize the impact of
+ * the change on the rest of the ICU formatting code.
+ *
+ * The change to decNumber enables support for big decimal numbers, and
+ * allows rounding computations to be done directly in decimal, avoiding
+ * extra, and inaccurate, conversions to and from doubles.
+ *
+ * Original DigitList comments:
+ *
  * Digit List utility class. Private to DecimalFormat.  Handles the transcoding
  * between numeric values and strings of characters.  Only handles
  * non-negative numbers.  The division of labor between DigitList and
@@ -63,9 +102,31 @@ U_NAMESPACE_BEGIN
  * object can be computed by mulitplying the fraction f, where 0 <= f < 1,
  * derived by placing all the digits of the list to the right of the
  * decimal point, by 10^exponent.
+ *
+ * --------
+ *
+ * DigitList vs. decimalNumber:
+ *
+ *    DigitList stores digits with the most significant first.
+ *    decNumber stores digits with the least significant first.
+ *
+ *    DigitList, decimal point is before the most significant.
+ *    decNumber, decimal point is after the least signficant digit.
+ *
+ *       digitList:    0.ddddd * 10 ^ exp
+ *       decNumber:    ddddd. * 10 ^ exp
+ *
+ *       digitList exponent = decNumber exponent + digit count
+ *
+ *    digitList, digits are platform invariant chars, '0' - '9'
+ *    decNumber, digits are binary, one per byte, 0 - 9.
+ *
+ *       (decNumber library is configurable in how digits are stored, ICU has configured
+ *        it this way for convenience in replacing the old DigitList implementation.)
  */
 class U_I18N_API DigitList : public UMemory { // Declare external to make compiler happy
 public:
+
     DigitList();
     ~DigitList();
 
@@ -89,13 +150,10 @@ public:
      */
     UBool operator==(const DigitList& other) const;
 
-    /**
-     * Return true if another object is semantically unequal to this one.
-     * @param other The DigitList to  be compared for inequality
-     * @return true if another object is semantically unequal to this one.
-     * return false otherwise.
-     */
-    UBool operator!=(const DigitList& other) const { return !operator==(other); }
+    int32_t  compare(const DigitList& other);
+
+
+    inline UBool operator!=(const DigitList& other) const { return !operator==(other); }
 
     /**
      * Clears out the digits.
@@ -107,18 +165,31 @@ public:
     void clear(void);
 
     /**
-     * Appends digits to the list. Ignores all digits beyond the first DBL_DIG,
-     * since they are not significant for either longs or doubles.
+     *  Remove, by rounding, any fractional part of the decimal number,
+     *  leaving an integer value.
+     */
+    void toIntegralValue();
+    
+    /**
+     * Appends digits to the list. 
+     *    CAUTION:  this function is not recommended for new code.
+     *              In the original DigitList implementation, decimal numbers were
+     *              parsed by appending them to a digit list as they were encountered.
+     *              With the revamped DigitList based on decNumber, append is very
+     *              inefficient, and the interaction with the exponent value is confusing.
+     *              Best avoided.
+     *              TODO:  remove this function once all use has been replaced.
+     *              TODO:  describe alternative to append()
      * @param digit The digit to be appended.
      */
-    inline void append(char digit);
+    void append(char digit);
 
     /**
      * Utility routine to get the value of the digit list
      * Returns 0.0 if zero length.
      * @return the value of the digit list.
      */
-    double getDouble(void) /*const*/;
+    double getDouble(void) const;
 
     /**
      * Utility routine to get the value of the digit list
@@ -136,6 +207,11 @@ public:
      */
     int64_t getInt64(void) /*const*/;
 
+    /**
+     *  Utility routine to get the value of the digit list as a decimal string.
+     */
+    void getDecimal(CharString &str, UErrorCode &status);
+
     /**
      * Return true if the number represented by this object can fit into
      * a long.
@@ -155,49 +231,208 @@ public:
     UBool fitsIntoInt64(UBool ignoreNegativeZero) /*const*/;
 
     /**
-     * Utility routine to set the value of the digit list from a double
-     * Input must be non-negative, and must not be Inf, -Inf, or NaN.
-     * The maximum fraction digits helps us round properly.
+     * Utility routine to set the value of the digit list from a double.
      * @param source The value to be set
-     * @param maximunDigits The maximum number of digits to be shown
-     * @param fixedPoint True if the point is fixed
      */
-    void set(double source, int32_t maximumDigits, UBool fixedPoint = TRUE);
+    void set(double source);
 
     /**
      * Utility routine to set the value of the digit list from a long.
      * If a non-zero maximumDigits is specified, no more than that number of
      * significant digits will be produced.
      * @param source The value to be set
-     * @param maximunDigits The maximum number of digits to be shown
      */
-    void set(int32_t source, int32_t maximumDigits = 0);
+    void set(int32_t source);
 
     /**
      * Utility routine to set the value of the digit list from an int64.
      * If a non-zero maximumDigits is specified, no more than that number of
      * significant digits will be produced.
      * @param source The value to be set
-     * @param maximunDigits The maximum number of digits to be shown
      */
-    void set(int64_t source, int32_t maximumDigits = 0);
+    void set(int64_t source);
+
+    /**
+     * Utility routine to set the value of the digit list from an int64.
+     * Does not set the decnumber unless requested later
+     * If a non-zero maximumDigits is specified, no more than that number of
+     * significant digits will be produced.
+     * @param source The value to be set
+     */
+    void setInteger(int64_t source);
+
+   /**
+     * Utility routine to set the value of the digit list from a decimal number
+     * string.
+     * @param source The value to be set.  The string must be nul-terminated.
+     * @param fastpathBits special flags for fast parsing
+     */
+    void set(const StringPiece &source, UErrorCode &status, uint32_t fastpathBits = 0);
+
+    /**
+     * Multiply    this = this * arg
+     *    This digitlist will be expanded if necessary to accomodate the result.
+     *  @param arg  the number to multiply by.
+     */
+    void mult(const DigitList &arg, UErrorCode &status);
 
     /**
-     * Return true if this is a representation of zero.
-     * @return true if this is a representation of zero.
+     *   Divide    this = this / arg
+     */
+    void div(const DigitList &arg, UErrorCode &status);
+
+    //  The following functions replace direct access to the original DigitList implmentation
+    //  data structures.
+
+    void setRoundingMode(DecimalFormat::ERoundingMode m); 
+
+    /** Test a number for zero.
+     * @return  TRUE if the number is zero
      */
     UBool isZero(void) const;
 
+    /** Test for a Nan
+     * @return  TRUE if the number is a NaN
+     */
+    UBool isNaN(void) const {return decNumberIsNaN(fDecNumber);}
+
+    UBool isInfinite() const {return decNumberIsInfinite(fDecNumber);}
+
+    /**  Reduce, or normalize.  Removes trailing zeroes, adjusts exponent appropriately. */
+    void     reduce();
+
+    /**  Remove trailing fraction zeros, adjust exponent accordingly. */
+    void     trim();
+
+    /** Set to zero */
+    void     setToZero() {uprv_decNumberZero(fDecNumber);}
+
+    /** get the number of digits in the decimal number */
+    int32_t  digits() const {return fDecNumber->digits;}
+
     /**
-     * Return true if this is a representation of LONG_MIN.  You must use
-     * this method to determine if this is so; you cannot check directly,
-     * because a special format is used to handle this.
+     * Round the number 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.
+     * result is guaranteed to be trimmed. 
      */
-    // This code is unused.
-    //UBool isLONG_MIN(void) const;
+    void round(int32_t maximumDigits);
+
+    void roundFixedPoint(int32_t maximumFractionDigits);
+
+    /** Ensure capacity for digits.  Grow the storage if it is currently less than
+     *      the requested size.   Capacity is not reduced if it is already greater
+     *      than requested.
+     */
+    void  ensureCapacity(int32_t  requestedSize, UErrorCode &status); 
+
+    UBool    isPositive(void) const { return decNumberIsNegative(fDecNumber) == 0;}
+    void     setPositive(UBool s); 
+
+    void     setDecimalAt(int32_t d);
+    int32_t  getDecimalAt();
+
+    void     setCount(int32_t c);
+    int32_t  getCount() const;
+    
+    /**
+     * Set the digit in platform (invariant) format, from '0'..'9'
+     * @param i index of digit
+     * @param v digit value, from '0' to '9' in platform invariant format
+     */
+    void     setDigit(int32_t i, char v);
 
-public:
     /**
+     * Get the digit in platform (invariant) format, from '0'..'9' inclusive
+     * @param i index of digit
+     * @return invariant format of the digit
+     */
+    char     getDigit(int32_t i);
+
+
+    /**
+     * Get the digit's value, as an integer from 0..9 inclusive.
+     * Note that internally this value is a decNumberUnit, but ICU configures it to be a uint8_t.
+     * @param i index of digit
+     * @return value of that digit
+     */
+    uint8_t     getDigitValue(int32_t i);
+
+    /**
+     * Gets the upper bound exponent for this value. For 987, returns 3
+     * because 10^3 is the smallest power of 10 that is just greater than
+     * 987.
+     */
+    int32_t getUpperExponent() const;
+
+    /**
+     * Gets the lower bound exponent for this value. For 98.7, returns -1
+     * because the right most digit, is the 10^-1 place.
+     */
+    int32_t getLowerExponent() const { return fDecNumber->exponent; }
+
+    /**
+     * Sets result to the smallest DigitInterval needed to display this
+     * DigitList in fixed point form and returns result.
+     */
+    DigitInterval& getSmallestInterval(DigitInterval &result) const;
+
+    /**
+     * Like getDigitValue, but the digit is identified by exponent.
+     * For example, getDigitByExponent(7) returns the 10^7 place of this
+     * DigitList. Unlike getDigitValue, there are no upper or lower bounds
+     * for passed parameter. Instead, getDigitByExponent returns 0 if
+     * the exponent falls outside the interval for this DigitList.
+     */
+    uint8_t getDigitByExponent(int32_t exponent) const;
+
+    /**
+     * Appends the digits in this object to a CharString.
+     * 3 is appended as (char) 3, not '3'
+     */
+    void appendDigitsTo(CharString &str, UErrorCode &status) const;
+
+    /**
+     * Equivalent to roundFixedPoint(-digitExponent) except unlike
+     * roundFixedPoint, this works for any digitExponent value.
+     * If maxSigDigits is set then this instance is rounded to have no more
+     * than maxSigDigits. The end result is guaranteed to be trimmed.
+     */
+    void roundAtExponent(int32_t digitExponent, int32_t maxSigDigits=INT32_MAX);
+
+    /**
+     * Quantizes according to some amount and rounds according to the
+     * context of this instance. Quantizing 3.233 with 0.05 gives 3.25.
+     */
+    void quantize(const DigitList &amount, UErrorCode &status);
+
+    /**
+     * Like toScientific but only returns the exponent
+     * leaving this instance unchanged.
+     */ 
+    int32_t getScientificExponent(
+            int32_t minIntDigitCount, int32_t exponentMultiplier) const;
+
+    /**
+     * Converts this instance to scientific notation. This instance
+     * becomes the mantissa and the exponent is returned.
+     * @param minIntDigitCount minimum integer digits in mantissa
+     *   Exponent is set so that the actual number of integer digits
+     *   in mantissa is as close to the minimum as possible.
+     * @param exponentMultiplier The exponent is always a multiple of
+     *  This number. Usually 1, but set to 3 for engineering notation.
+     * @return exponent
+     */
+    int32_t toScientific(
+            int32_t minIntDigitCount, int32_t exponentMultiplier);
+
+    /**
+     * Shifts decimal to the right.
+     */
+    void shiftDecimalRight(int32_t numPlaces);
+
+private:
+    /*
      * These data members are intentionally public and can be set directly.
      *<P>
      * The value represented is given by placing the decimal point before
@@ -217,40 +452,75 @@ public:
      * <P>
      * Zero is represented by any DigitList with fCount == 0 or with each fDigits[i]
      * for all i <= fCount == '0'.
+     *
+     * int32_t                         fDecimalAt;
+     * int32_t                         fCount;
+     * UBool                           fIsPositive;
+     * char                            *fDigits;
+     * DecimalFormat::ERoundingMode    fRoundingMode;
      */
-    int32_t     fDecimalAt;
-    int32_t     fCount;
-    UBool       fIsPositive;
-    char        *fDigits;
+
+public:
+    decContext    fContext;   // public access to status flags.  
 
 private:
+    decNumber     *fDecNumber;
+    MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS>  fStorage;
+
+    /* Cached double value corresponding to this decimal number.
+     * This is an optimization for the formatting implementation, which may
+     * ask for the double value multiple times.
+     */
+    union DoubleOrInt64 {
+      double        fDouble;
+      int64_t       fInt64;
+    } fUnion;
+    enum EHave {
+      kNone=0,
+      kDouble
+    } fHave;
+
+
 
-    /* One character before fDigits for the decimal*/
-    char        fDecimalDigits[MAX_DEC_DIGITS + 1];
+    UBool shouldRoundUp(int32_t maximumDigits) const;
+
+ public:
+
+#if U_OVERRIDE_CXX_ALLOCATION
+    using UMemory::operator new;
+    using UMemory::operator delete;
+#else
+    static inline void * U_EXPORT2 operator new(size_t size) U_NO_THROW { return ::operator new(size); };
+    static inline void U_EXPORT2 operator delete(void *ptr )  U_NO_THROW { ::operator delete(ptr); };
+#endif
+    static char U_EXPORT2 getStrtodDecimalSeparator();
 
     /**
-     * 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.
+     * Placement new for stack usage
+     * @internal
      */
-    void round(int32_t maximumDigits);
+    static inline void * U_EXPORT2 operator new(size_t /*size*/, void * onStack, EStackMode  /*mode*/) U_NO_THROW { return onStack; }
 
-    UBool shouldRoundUp(int32_t maximumDigits) const;
+    /**
+     * Placement delete for stack usage
+     * @internal
+     */
+    static inline void U_EXPORT2 operator delete(void * /*ptr*/, void * /*onStack*/, EStackMode /*mode*/)  U_NO_THROW {}
+
+ private:
+    inline void internalSetDouble(double d) {
+      fHave = kDouble;
+      fUnion.fDouble=d;
+    }
+    inline void internalClear() {
+      fHave = kNone;
+    }
 };
-// -------------------------------------
-// Appends the digit to the digit list if it's not out of scope.
-// Ignores the digit, otherwise.
-
-inline void
-DigitList::append(char digit)
-{
-    // Ignore digits which exceed the precision we can represent
-    if (fCount < MAX_DIGITS)
-        fDigits[fCount++] = digit;
-}
+
 
 U_NAMESPACE_END
+
+#endif // #if !UCONFIG_NO_FORMATTING
 #endif // _DIGITLST
 
 //eof