michael@0: /* michael@0: ****************************************************************************** michael@0: * michael@0: * Copyright (C) 1997-2012, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: * michael@0: ****************************************************************************** michael@0: * michael@0: * File DIGITLST.H michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 02/25/97 aliu Converted from java. michael@0: * 03/21/97 clhuang Updated per C++ implementation. michael@0: * 04/15/97 aliu Changed MAX_COUNT to DBL_DIG. Changed Digit to char. michael@0: * 09/09/97 aliu Adapted for exponential notation support. michael@0: * 08/02/98 stephen Added nearest/even rounding michael@0: * 06/29/99 stephen Made LONG_DIGITS a macro to satisfy SUN compiler michael@0: * 07/09/99 stephen Removed kMaxCount (unused, for HP compiler) michael@0: ****************************************************************************** michael@0: */ michael@0: michael@0: #ifndef DIGITLST_H michael@0: #define DIGITLST_H michael@0: michael@0: #include "unicode/uobject.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: #include "unicode/decimfmt.h" michael@0: #include michael@0: #include "decContext.h" michael@0: #include "decNumber.h" michael@0: #include "cmemory.h" michael@0: michael@0: // Decimal digits in a 64-bit int michael@0: #define INT64_DIGITS 19 michael@0: michael@0: typedef enum EDigitListValues { michael@0: MAX_DBL_DIGITS = DBL_DIG, michael@0: MAX_I64_DIGITS = INT64_DIGITS, michael@0: MAX_DIGITS = MAX_I64_DIGITS, michael@0: MAX_EXPONENT = DBL_DIG, michael@0: DIGIT_PADDING = 3, michael@0: DEFAULT_DIGITS = 40, // Initial storage size, will grow as needed. michael@0: michael@0: // "+." + fDigits + "e" + fDecimalAt michael@0: MAX_DEC_DIGITS = MAX_DIGITS + DIGIT_PADDING + MAX_EXPONENT michael@0: } EDigitListValues; michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: class CharString; michael@0: michael@0: // Export an explicit template instantiation of the MaybeStackHeaderAndArray that michael@0: // is used as a data member of DigitList. michael@0: // michael@0: // MSVC requires this, even though it should not be necessary. michael@0: // No direct access to the MaybeStackHeaderAndArray leaks out of the i18n library. michael@0: // michael@0: // Macintosh produces duplicate definition linker errors with the explicit template michael@0: // instantiation. michael@0: // michael@0: #if !U_PLATFORM_IS_DARWIN_BASED michael@0: template class U_I18N_API MaybeStackHeaderAndArray; michael@0: #endif michael@0: michael@0: michael@0: enum EStackMode { kOnStack }; michael@0: michael@0: enum EFastpathBits { kFastpathOk = 1, kNoDecimal = 2 }; michael@0: michael@0: /** michael@0: * Digit List is actually a Decimal Floating Point number. michael@0: * The original implementation has been replaced by a thin wrapper onto a michael@0: * decimal number from the decNumber library. michael@0: * michael@0: * The original DigitList API has been retained, to minimize the impact of michael@0: * the change on the rest of the ICU formatting code. michael@0: * michael@0: * The change to decNumber enables support for big decimal numbers, and michael@0: * allows rounding computations to be done directly in decimal, avoiding michael@0: * extra, and inaccurate, conversions to and from doubles. michael@0: * michael@0: * Original DigitList comments: michael@0: * michael@0: * Digit List utility class. Private to DecimalFormat. Handles the transcoding michael@0: * between numeric values and strings of characters. Only handles michael@0: * non-negative numbers. The division of labor between DigitList and michael@0: * DecimalFormat is that DigitList handles the radix 10 representation michael@0: * issues; DecimalFormat handles the locale-specific issues such as michael@0: * positive/negative, grouping, decimal point, currency, and so on. michael@0: *

michael@0: * A DigitList is really a representation of a floating point value. michael@0: * It may be an integer value; we assume that a double has sufficient michael@0: * precision to represent all digits of a long. michael@0: *

michael@0: * The DigitList representation consists of a string of characters, michael@0: * which are the digits radix 10, from '0' to '9'. It also has a radix michael@0: * 10 exponent associated with it. The value represented by a DigitList michael@0: * object can be computed by mulitplying the fraction f, where 0 <= f < 1, michael@0: * derived by placing all the digits of the list to the right of the michael@0: * decimal point, by 10^exponent. michael@0: * michael@0: * -------- michael@0: * michael@0: * DigitList vs. decimalNumber: michael@0: * michael@0: * DigitList stores digits with the most significant first. michael@0: * decNumber stores digits with the least significant first. michael@0: * michael@0: * DigitList, decimal point is before the most significant. michael@0: * decNumber, decimal point is after the least signficant digit. michael@0: * michael@0: * digitList: 0.ddddd * 10 ^ exp michael@0: * decNumber: ddddd. * 10 ^ exp michael@0: * michael@0: * digitList exponent = decNumber exponent + digit count michael@0: * michael@0: * digitList, digits are platform invariant chars, '0' - '9' michael@0: * decNumber, digits are binary, one per byte, 0 - 9. michael@0: * michael@0: * (decNumber library is configurable in how digits are stored, ICU has configured michael@0: * it this way for convenience in replacing the old DigitList implementation.) michael@0: */ michael@0: class U_I18N_API DigitList : public UMemory { // Declare external to make compiler happy michael@0: public: michael@0: michael@0: DigitList(); michael@0: ~DigitList(); michael@0: michael@0: /* copy constructor michael@0: * @param DigitList The object to be copied. michael@0: * @return the newly created object. michael@0: */ michael@0: DigitList(const DigitList&); // copy constructor michael@0: michael@0: /* assignment operator michael@0: * @param DigitList The object to be copied. michael@0: * @return the newly created object. michael@0: */ michael@0: DigitList& operator=(const DigitList&); // assignment operator michael@0: michael@0: /** michael@0: * Return true if another object is semantically equal to this one. michael@0: * @param other The DigitList to be compared for equality michael@0: * @return true if another object is semantically equal to this one. michael@0: * return false otherwise. michael@0: */ michael@0: UBool operator==(const DigitList& other) const; michael@0: michael@0: int32_t compare(const DigitList& other); michael@0: michael@0: michael@0: inline UBool operator!=(const DigitList& other) const { return !operator==(other); } michael@0: michael@0: /** michael@0: * Clears out the digits. michael@0: * Use before appending them. michael@0: * Typically, you set a series of digits with append, then at the point michael@0: * you hit the decimal point, you set myDigitList.fDecimalAt = myDigitList.fCount; michael@0: * then go on appending digits. michael@0: */ michael@0: void clear(void); michael@0: michael@0: /** michael@0: * Remove, by rounding, any fractional part of the decimal number, michael@0: * leaving an integer value. michael@0: */ michael@0: void toIntegralValue(); michael@0: michael@0: /** michael@0: * Appends digits to the list. michael@0: * CAUTION: this function is not recommended for new code. michael@0: * In the original DigitList implementation, decimal numbers were michael@0: * parsed by appending them to a digit list as they were encountered. michael@0: * With the revamped DigitList based on decNumber, append is very michael@0: * inefficient, and the interaction with the exponent value is confusing. michael@0: * Best avoided. michael@0: * TODO: remove this function once all use has been replaced. michael@0: * TODO: describe alternative to append() michael@0: * @param digit The digit to be appended. michael@0: */ michael@0: void append(char digit); michael@0: michael@0: /** michael@0: * Utility routine to get the value of the digit list michael@0: * Returns 0.0 if zero length. michael@0: * @return the value of the digit list. michael@0: */ michael@0: double getDouble(void) const; michael@0: michael@0: /** michael@0: * Utility routine to get the value of the digit list michael@0: * Make sure that fitsIntoLong() is called before calling this function. michael@0: * Returns 0 if zero length. michael@0: * @return the value of the digit list, return 0 if it is zero length michael@0: */ michael@0: int32_t getLong(void) /*const*/; michael@0: michael@0: /** michael@0: * Utility routine to get the value of the digit list michael@0: * Make sure that fitsIntoInt64() is called before calling this function. michael@0: * Returns 0 if zero length. michael@0: * @return the value of the digit list, return 0 if it is zero length michael@0: */ michael@0: int64_t getInt64(void) /*const*/; michael@0: michael@0: /** michael@0: * Utility routine to get the value of the digit list as a decimal string. michael@0: */ michael@0: void getDecimal(CharString &str, UErrorCode &status); michael@0: michael@0: /** michael@0: * Return true if the number represented by this object can fit into michael@0: * a long. michael@0: * @param ignoreNegativeZero True if negative zero is ignored. michael@0: * @return true if the number represented by this object can fit into michael@0: * a long, return false otherwise. michael@0: */ michael@0: UBool fitsIntoLong(UBool ignoreNegativeZero) /*const*/; michael@0: michael@0: /** michael@0: * Return true if the number represented by this object can fit into michael@0: * an int64_t. michael@0: * @param ignoreNegativeZero True if negative zero is ignored. michael@0: * @return true if the number represented by this object can fit into michael@0: * a long, return false otherwise. michael@0: */ michael@0: UBool fitsIntoInt64(UBool ignoreNegativeZero) /*const*/; michael@0: michael@0: /** michael@0: * Utility routine to set the value of the digit list from a double. michael@0: * @param source The value to be set michael@0: */ michael@0: void set(double source); michael@0: michael@0: /** michael@0: * Utility routine to set the value of the digit list from a long. michael@0: * If a non-zero maximumDigits is specified, no more than that number of michael@0: * significant digits will be produced. michael@0: * @param source The value to be set michael@0: */ michael@0: void set(int32_t source); michael@0: michael@0: /** michael@0: * Utility routine to set the value of the digit list from an int64. michael@0: * If a non-zero maximumDigits is specified, no more than that number of michael@0: * significant digits will be produced. michael@0: * @param source The value to be set michael@0: */ michael@0: void set(int64_t source); michael@0: michael@0: /** michael@0: * Utility routine to set the value of the digit list from an int64. michael@0: * Does not set the decnumber unless requested later michael@0: * If a non-zero maximumDigits is specified, no more than that number of michael@0: * significant digits will be produced. michael@0: * @param source The value to be set michael@0: */ michael@0: void setInteger(int64_t source); michael@0: michael@0: /** michael@0: * Utility routine to set the value of the digit list from a decimal number michael@0: * string. michael@0: * @param source The value to be set. The string must be nul-terminated. michael@0: * @param fastpathBits special flags for fast parsing michael@0: */ michael@0: void set(const StringPiece &source, UErrorCode &status, uint32_t fastpathBits = 0); michael@0: michael@0: /** michael@0: * Multiply this = this * arg michael@0: * This digitlist will be expanded if necessary to accomodate the result. michael@0: * @param arg the number to multiply by. michael@0: */ michael@0: void mult(const DigitList &arg, UErrorCode &status); michael@0: michael@0: /** michael@0: * Divide this = this / arg michael@0: */ michael@0: void div(const DigitList &arg, UErrorCode &status); michael@0: michael@0: // The following functions replace direct access to the original DigitList implmentation michael@0: // data structures. michael@0: michael@0: void setRoundingMode(DecimalFormat::ERoundingMode m); michael@0: michael@0: /** Test a number for zero. michael@0: * @return TRUE if the number is zero michael@0: */ michael@0: UBool isZero(void) const; michael@0: michael@0: /** Test for a Nan michael@0: * @return TRUE if the number is a NaN michael@0: */ michael@0: UBool isNaN(void) const {return decNumberIsNaN(fDecNumber);} michael@0: michael@0: UBool isInfinite() const {return decNumberIsInfinite(fDecNumber);} michael@0: michael@0: /** Reduce, or normalize. Removes trailing zeroes, adjusts exponent appropriately. */ michael@0: void reduce(); michael@0: michael@0: /** Remove trailing fraction zeros, adjust exponent accordingly. */ michael@0: void trim(); michael@0: michael@0: /** Set to zero */ michael@0: void setToZero() {uprv_decNumberZero(fDecNumber);} michael@0: michael@0: /** get the number of digits in the decimal number */ michael@0: int32_t digits() const {return fDecNumber->digits;} michael@0: michael@0: /** michael@0: * Round the number to the given number of digits. michael@0: * @param maximumDigits The maximum number of digits to be shown. michael@0: * Upon return, count will be less than or equal to maximumDigits. michael@0: */ michael@0: void round(int32_t maximumDigits); michael@0: michael@0: void roundFixedPoint(int32_t maximumFractionDigits); michael@0: michael@0: /** Ensure capacity for digits. Grow the storage if it is currently less than michael@0: * the requested size. Capacity is not reduced if it is already greater michael@0: * than requested. michael@0: */ michael@0: void ensureCapacity(int32_t requestedSize, UErrorCode &status); michael@0: michael@0: UBool isPositive(void) const { return decNumberIsNegative(fDecNumber) == 0;} michael@0: void setPositive(UBool s); michael@0: michael@0: void setDecimalAt(int32_t d); michael@0: int32_t getDecimalAt(); michael@0: michael@0: void setCount(int32_t c); michael@0: int32_t getCount() const; michael@0: michael@0: /** michael@0: * Set the digit in platform (invariant) format, from '0'..'9' michael@0: * @param i index of digit michael@0: * @param v digit value, from '0' to '9' in platform invariant format michael@0: */ michael@0: void setDigit(int32_t i, char v); michael@0: michael@0: /** michael@0: * Get the digit in platform (invariant) format, from '0'..'9' inclusive michael@0: * @param i index of digit michael@0: * @return invariant format of the digit michael@0: */ michael@0: char getDigit(int32_t i); michael@0: michael@0: michael@0: /** michael@0: * Get the digit's value, as an integer from 0..9 inclusive. michael@0: * Note that internally this value is a decNumberUnit, but ICU configures it to be a uint8_t. michael@0: * @param i index of digit michael@0: * @return value of that digit michael@0: */ michael@0: uint8_t getDigitValue(int32_t i); michael@0: michael@0: michael@0: private: michael@0: /* michael@0: * These data members are intentionally public and can be set directly. michael@0: *

michael@0: * The value represented is given by placing the decimal point before michael@0: * fDigits[fDecimalAt]. If fDecimalAt is < 0, then leading zeros between michael@0: * the decimal point and the first nonzero digit are implied. If fDecimalAt michael@0: * is > fCount, then trailing zeros between the fDigits[fCount-1] and the michael@0: * decimal point are implied. michael@0: *

michael@0: * Equivalently, the represented value is given by f * 10^fDecimalAt. Here michael@0: * f is a value 0.1 <= f < 1 arrived at by placing the digits in fDigits to michael@0: * the right of the decimal. michael@0: *

michael@0: * DigitList is normalized, so if it is non-zero, fDigits[0] is non-zero. We michael@0: * don't allow denormalized numbers because our exponent is effectively of michael@0: * unlimited magnitude. The fCount value contains the number of significant michael@0: * digits present in fDigits[]. michael@0: *

michael@0: * Zero is represented by any DigitList with fCount == 0 or with each fDigits[i] michael@0: * for all i <= fCount == '0'. michael@0: * michael@0: * int32_t fDecimalAt; michael@0: * int32_t fCount; michael@0: * UBool fIsPositive; michael@0: * char *fDigits; michael@0: * DecimalFormat::ERoundingMode fRoundingMode; michael@0: */ michael@0: michael@0: public: michael@0: decContext fContext; // public access to status flags. michael@0: michael@0: private: michael@0: decNumber *fDecNumber; michael@0: MaybeStackHeaderAndArray fStorage; michael@0: michael@0: /* Cached double value corresponding to this decimal number. michael@0: * This is an optimization for the formatting implementation, which may michael@0: * ask for the double value multiple times. michael@0: */ michael@0: union DoubleOrInt64 { michael@0: double fDouble; michael@0: int64_t fInt64; michael@0: } fUnion; michael@0: enum EHave { michael@0: kNone=0, michael@0: kDouble, michael@0: kInt64 michael@0: } fHave; michael@0: michael@0: michael@0: michael@0: UBool shouldRoundUp(int32_t maximumDigits) const; michael@0: michael@0: public: michael@0: michael@0: using UMemory::operator new; michael@0: using UMemory::operator delete; michael@0: michael@0: /** michael@0: * Placement new for stack usage michael@0: * @internal michael@0: */ michael@0: static inline void * U_EXPORT2 operator new(size_t /*size*/, void * onStack, EStackMode /*mode*/) U_NO_THROW { return onStack; } michael@0: michael@0: /** michael@0: * Placement delete for stack usage michael@0: * @internal michael@0: */ michael@0: static inline void U_EXPORT2 operator delete(void * /*ptr*/, void * /*onStack*/, EStackMode /*mode*/) U_NO_THROW {} michael@0: michael@0: private: michael@0: inline void internalSetDouble(double d) { michael@0: fHave = kDouble; michael@0: fUnion.fDouble=d; michael@0: } michael@0: inline void internalSetInt64(int64_t d) { michael@0: fHave = kInt64; michael@0: fUnion.fInt64=d; michael@0: } michael@0: inline void internalClear() { michael@0: fHave = kNone; michael@0: } michael@0: }; michael@0: michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif // #if !UCONFIG_NO_FORMATTING michael@0: #endif // _DIGITLST michael@0: michael@0: //eof