1/*
2******************************************************************************
3*
4*   Copyright (C) 1997-2010, International Business Machines
5*   Corporation and others.  All Rights Reserved.
6*
7******************************************************************************
8*
9* File DIGITLST.H
10*
11* Modification History:
12*
13*   Date        Name        Description
14*   02/25/97    aliu        Converted from java.
15*   03/21/97    clhuang     Updated per C++ implementation.
16*   04/15/97    aliu        Changed MAX_COUNT to DBL_DIG.  Changed Digit to char.
17*   09/09/97    aliu        Adapted for exponential notation support.
18*   08/02/98    stephen     Added nearest/even rounding
19*   06/29/99    stephen     Made LONG_DIGITS a macro to satisfy SUN compiler
20*   07/09/99    stephen     Removed kMaxCount (unused, for HP compiler)
21******************************************************************************
22*/
23
24#ifndef DIGITLST_H
25#define DIGITLST_H
26
27#include "unicode/uobject.h"
28
29#if !UCONFIG_NO_FORMATTING
30#include "unicode/decimfmt.h"
31#include <float.h>
32#include "decContext.h"
33#include "decNumber.h"
34#include "cmemory.h"
35
36// Decimal digits in a 64-bit int
37#define INT64_DIGITS 19
38
39typedef enum EDigitListValues {
40    MAX_DBL_DIGITS = DBL_DIG,
41    MAX_I64_DIGITS = INT64_DIGITS,
42    MAX_DIGITS = MAX_I64_DIGITS,
43    MAX_EXPONENT = DBL_DIG,
44    DIGIT_PADDING = 3,
45    DEFAULT_DIGITS = 40,   // Initial storage size, will grow as needed.
46
47     // "+." + fDigits + "e" + fDecimalAt
48    MAX_DEC_DIGITS = MAX_DIGITS + DIGIT_PADDING + MAX_EXPONENT
49} EDigitListValues;
50
51U_NAMESPACE_BEGIN
52
53class CharString;
54
55// Export an explicit template instantiation of the MaybeStackHeaderAndArray that
56//    is used as a data member of DigitList.
57//
58//    MSVC requires this, even though it should not be necessary.
59//    No direct access to the MaybeStackHeaderAndArray leaks out of the i18n library.
60//
61//    Macintosh produces duplicate definition linker errors with the explicit template
62//    instantiation.
63//
64#if !defined(U_DARWIN)
65template class U_I18N_API MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS>;
66#endif
67
68
69/**
70 * Digit List is actually a Decimal Floating Point number.
71 * The original implementation has been replaced by a thin wrapper onto a
72 * decimal number from the decNumber library.
73 *
74 * The original DigitList API has been retained, to minimize the impact of
75 * the change on the rest of the ICU formatting code.
76 *
77 * The change to decNumber enables support for big decimal numbers, and
78 * allows rounding computations to be done directly in decimal, avoiding
79 * extra, and inaccurate, conversions to and from doubles.
80 *
81 * Original DigitList comments:
82 *
83 * Digit List utility class. Private to DecimalFormat.  Handles the transcoding
84 * between numeric values and strings of characters.  Only handles
85 * non-negative numbers.  The division of labor between DigitList and
86 * DecimalFormat is that DigitList handles the radix 10 representation
87 * issues; DecimalFormat handles the locale-specific issues such as
88 * positive/negative, grouping, decimal point, currency, and so on.
89 * <P>
90 * A DigitList is really a representation of a floating point value.
91 * It may be an integer value; we assume that a double has sufficient
92 * precision to represent all digits of a long.
93 * <P>
94 * The DigitList representation consists of a string of characters,
95 * which are the digits radix 10, from '0' to '9'.  It also has a radix
96 * 10 exponent associated with it.  The value represented by a DigitList
97 * object can be computed by mulitplying the fraction f, where 0 <= f < 1,
98 * derived by placing all the digits of the list to the right of the
99 * decimal point, by 10^exponent.
100 *
101 * --------
102 *
103 * DigitList vs. decimalNumber:
104 *
105 *    DigitList stores digits with the most significant first.
106 *    decNumber stores digits with the least significant first.
107 *
108 *    DigitList, decimal point is before the most significant.
109 *    decNumber, decimal point is after the least signficant digit.
110 *
111 *       digitList:    0.ddddd * 10 ^ exp
112 *       decNumber:    ddddd. * 10 ^ exp
113 *
114 *       digitList exponent = decNumber exponent + digit count
115 *
116 *    digitList, digits are platform invariant chars, '0' - '9'
117 *    decNumber, digits are binary, one per byte, 0 - 9.
118 *
119 *       (decNumber library is configurable in how digits are stored, ICU has configured
120 *        it this way for convenience in replacing the old DigitList implementation.)
121 */
122class U_I18N_API DigitList : public UMemory { // Declare external to make compiler happy
123public:
124
125    DigitList();
126    ~DigitList();
127
128    /* copy constructor
129     * @param DigitList The object to be copied.
130     * @return the newly created object.
131     */
132    DigitList(const DigitList&); // copy constructor
133
134    /* assignment operator
135     * @param DigitList The object to be copied.
136     * @return the newly created object.
137     */
138    DigitList& operator=(const DigitList&);  // assignment operator
139
140    /**
141     * Return true if another object is semantically equal to this one.
142     * @param other The DigitList to be compared for equality
143     * @return true if another object is semantically equal to this one.
144     * return false otherwise.
145     */
146    UBool operator==(const DigitList& other) const;
147
148    int32_t  compare(const DigitList& other);
149
150
151    inline UBool operator!=(const DigitList& other) const { return !operator==(other); };
152
153    /**
154     * Clears out the digits.
155     * Use before appending them.
156     * Typically, you set a series of digits with append, then at the point
157     * you hit the decimal point, you set myDigitList.fDecimalAt = myDigitList.fCount;
158     * then go on appending digits.
159     */
160    void clear(void);
161
162    /**
163     *  Remove, by rounding, any fractional part of the decimal number,
164     *  leaving an integer value.
165     */
166    void toIntegralValue();
167
168    /**
169     * Appends digits to the list.
170     *    CAUTION:  this function is not recommended for new code.
171     *              In the original DigitList implementation, decimal numbers were
172     *              parsed by appending them to a digit list as they were encountered.
173     *              With the revamped DigitList based on decNumber, append is very
174     *              inefficient, and the interaction with the exponent value is confusing.
175     *              Best avoided.
176     *              TODO:  remove this function once all use has been replaced.
177     *              TODO:  describe alternative to append()
178     * @param digit The digit to be appended.
179     */
180    void append(char digit);
181
182    /**
183     * Utility routine to get the value of the digit list
184     * Returns 0.0 if zero length.
185     * @return the value of the digit list.
186     */
187    double getDouble(void) const;
188
189    /**
190     * Utility routine to get the value of the digit list
191     * Make sure that fitsIntoLong() is called before calling this function.
192     * Returns 0 if zero length.
193     * @return the value of the digit list, return 0 if it is zero length
194     */
195    int32_t getLong(void) /*const*/;
196
197    /**
198     * Utility routine to get the value of the digit list
199     * Make sure that fitsIntoInt64() is called before calling this function.
200     * Returns 0 if zero length.
201     * @return the value of the digit list, return 0 if it is zero length
202     */
203    int64_t getInt64(void) /*const*/;
204
205    /**
206     *  Utility routine to get the value of the digit list as a decimal string.
207     */
208    void getDecimal(CharString &str, UErrorCode &status);
209
210    /**
211     * Return true if the number represented by this object can fit into
212     * a long.
213     * @param ignoreNegativeZero True if negative zero is ignored.
214     * @return true if the number represented by this object can fit into
215     * a long, return false otherwise.
216     */
217    UBool fitsIntoLong(UBool ignoreNegativeZero) /*const*/;
218
219    /**
220     * Return true if the number represented by this object can fit into
221     * an int64_t.
222     * @param ignoreNegativeZero True if negative zero is ignored.
223     * @return true if the number represented by this object can fit into
224     * a long, return false otherwise.
225     */
226    UBool fitsIntoInt64(UBool ignoreNegativeZero) /*const*/;
227
228    /**
229     * Utility routine to set the value of the digit list from a double.
230     * @param source The value to be set
231     */
232    void set(double source);
233
234    /**
235     * Utility routine to set the value of the digit list from a long.
236     * If a non-zero maximumDigits is specified, no more than that number of
237     * significant digits will be produced.
238     * @param source The value to be set
239     */
240    void set(int32_t source);
241
242    /**
243     * Utility routine to set the value of the digit list from an int64.
244     * If a non-zero maximumDigits is specified, no more than that number of
245     * significant digits will be produced.
246     * @param source The value to be set
247     */
248    void set(int64_t source);
249
250   /**
251     * Utility routine to set the value of the digit list from a decimal number
252     * string.
253     * @param source The value to be set.  The string must be nul-terminated.
254     */
255    void set(const StringPiece &source, UErrorCode &status);
256
257    /**
258     * Multiply    this = this * arg
259     *    This digitlist will be expanded if necessary to accomodate the result.
260     *  @param arg  the number to multiply by.
261     */
262    void mult(const DigitList &arg, UErrorCode &status);
263
264    /**
265     *   Divide    this = this / arg
266     */
267    void div(const DigitList &arg, UErrorCode &status);
268
269    //  The following functions replace direct access to the original DigitList implmentation
270    //  data structures.
271
272    void setRoundingMode(DecimalFormat::ERoundingMode m);
273
274    /** Test a number for zero.
275     * @return  TRUE if the number is zero
276     */
277    UBool isZero(void) const;
278
279    /** Test for a Nan
280     * @return  TRUE if the number is a NaN
281     */
282    UBool isNaN(void) const {return decNumberIsNaN(fDecNumber);};
283
284    UBool isInfinite() const {return decNumberIsInfinite(fDecNumber);};
285
286    /**  Reduce, or normalize.  Removes trailing zeroes, adjusts exponent appropriately. */
287    void     reduce();
288
289    /**  Remove trailing fraction zeros, adjust exponent accordingly. */
290    void     trim();
291
292    /** Set to zero */
293    void     setToZero() {uprv_decNumberZero(fDecNumber);};
294
295    /** get the number of digits in the decimal number */
296    int32_t  digits() const {return fDecNumber->digits;};
297
298    /**
299     * Round the number to the given number of digits.
300     * @param maximumDigits The maximum number of digits to be shown.
301     * Upon return, count will be less than or equal to maximumDigits.
302     */
303    void round(int32_t maximumDigits);
304
305    void roundFixedPoint(int32_t maximumFractionDigits);
306
307    /** Ensure capacity for digits.  Grow the storage if it is currently less than
308     *      the requested size.   Capacity is not reduced if it is already greater
309     *      than requested.
310     */
311    void  ensureCapacity(int32_t  requestedSize, UErrorCode &status);
312
313    UBool    isPositive(void) const { return decNumberIsNegative(fDecNumber) == 0;};
314    void     setPositive(UBool s);
315
316    void     setDecimalAt(int32_t d);
317    int32_t  getDecimalAt();
318
319    void     setCount(int32_t c);
320    int32_t  getCount() const;
321
322    /**
323     * Set the digit in platform (invariant) format, from '0'..'9'
324     * @param i index of digit
325     * @param v digit value, from '0' to '9' in platform invariant format
326     */
327    void     setDigit(int32_t i, char v);
328
329    /**
330     * Get the digit in platform (invariant) format, from '0'..'9' inclusive
331     * @param i index of digit
332     * @return invariant format of the digit
333     */
334    char     getDigit(int32_t i);
335
336
337    /**
338     * Get the digit's value, as an integer from 0..9 inclusive.
339     * Note that internally this value is a decNumberUnit, but ICU configures it to be a uint8_t.
340     * @param i index of digit
341     * @return value of that digit
342     */
343    uint8_t     getDigitValue(int32_t i);
344
345
346private:
347    /*
348     * These data members are intentionally public and can be set directly.
349     *<P>
350     * The value represented is given by placing the decimal point before
351     * fDigits[fDecimalAt].  If fDecimalAt is < 0, then leading zeros between
352     * the decimal point and the first nonzero digit are implied.  If fDecimalAt
353     * is > fCount, then trailing zeros between the fDigits[fCount-1] and the
354     * decimal point are implied.
355     * <P>
356     * Equivalently, the represented value is given by f * 10^fDecimalAt.  Here
357     * f is a value 0.1 <= f < 1 arrived at by placing the digits in fDigits to
358     * the right of the decimal.
359     * <P>
360     * DigitList is normalized, so if it is non-zero, fDigits[0] is non-zero.  We
361     * don't allow denormalized numbers because our exponent is effectively of
362     * unlimited magnitude.  The fCount value contains the number of significant
363     * digits present in fDigits[].
364     * <P>
365     * Zero is represented by any DigitList with fCount == 0 or with each fDigits[i]
366     * for all i <= fCount == '0'.
367     *
368     * int32_t                         fDecimalAt;
369     * int32_t                         fCount;
370     * UBool                           fIsPositive;
371     * char                            *fDigits;
372     * DecimalFormat::ERoundingMode    fRoundingMode;
373     */
374
375private:
376
377    decContext    fContext;
378    decNumber     *fDecNumber;
379    MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS>  fStorage;
380
381    /* Cached double value corresponding to this decimal number.
382     * This is an optimization for the formatting implementation, which may
383     * ask for the double value multiple times.
384     */
385    double        fDouble;
386    UBool         fHaveDouble;
387
388
389
390    UBool shouldRoundUp(int32_t maximumDigits) const;
391};
392
393
394U_NAMESPACE_END
395
396#endif // #if !UCONFIG_NO_FORMATTING
397#endif // _DIGITLST
398
399//eof
400