]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | ****************************************************************************** | |
3 | * | |
4 | * Copyright (C) 1997-2012, 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 | ||
39 | typedef 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 | ||
51 | U_NAMESPACE_BEGIN | |
52 | ||
53 | class 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 !U_PLATFORM_IS_DARWIN_BASED | |
65 | template class U_I18N_API MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS>; | |
66 | #endif | |
67 | ||
68 | ||
69 | enum EStackMode { kOnStack }; | |
70 | ||
71 | enum EFastpathBits { kFastpathOk = 1, kNoDecimal = 2 }; | |
72 | ||
73 | /** | |
74 | * Digit List is actually a Decimal Floating Point number. | |
75 | * The original implementation has been replaced by a thin wrapper onto a | |
76 | * decimal number from the decNumber library. | |
77 | * | |
78 | * The original DigitList API has been retained, to minimize the impact of | |
79 | * the change on the rest of the ICU formatting code. | |
80 | * | |
81 | * The change to decNumber enables support for big decimal numbers, and | |
82 | * allows rounding computations to be done directly in decimal, avoiding | |
83 | * extra, and inaccurate, conversions to and from doubles. | |
84 | * | |
85 | * Original DigitList comments: | |
86 | * | |
87 | * Digit List utility class. Private to DecimalFormat. Handles the transcoding | |
88 | * between numeric values and strings of characters. Only handles | |
89 | * non-negative numbers. The division of labor between DigitList and | |
90 | * DecimalFormat is that DigitList handles the radix 10 representation | |
91 | * issues; DecimalFormat handles the locale-specific issues such as | |
92 | * positive/negative, grouping, decimal point, currency, and so on. | |
93 | * <P> | |
94 | * A DigitList is really a representation of a floating point value. | |
95 | * It may be an integer value; we assume that a double has sufficient | |
96 | * precision to represent all digits of a long. | |
97 | * <P> | |
98 | * The DigitList representation consists of a string of characters, | |
99 | * which are the digits radix 10, from '0' to '9'. It also has a radix | |
100 | * 10 exponent associated with it. The value represented by a DigitList | |
101 | * object can be computed by mulitplying the fraction f, where 0 <= f < 1, | |
102 | * derived by placing all the digits of the list to the right of the | |
103 | * decimal point, by 10^exponent. | |
104 | * | |
105 | * -------- | |
106 | * | |
107 | * DigitList vs. decimalNumber: | |
108 | * | |
109 | * DigitList stores digits with the most significant first. | |
110 | * decNumber stores digits with the least significant first. | |
111 | * | |
112 | * DigitList, decimal point is before the most significant. | |
113 | * decNumber, decimal point is after the least signficant digit. | |
114 | * | |
115 | * digitList: 0.ddddd * 10 ^ exp | |
116 | * decNumber: ddddd. * 10 ^ exp | |
117 | * | |
118 | * digitList exponent = decNumber exponent + digit count | |
119 | * | |
120 | * digitList, digits are platform invariant chars, '0' - '9' | |
121 | * decNumber, digits are binary, one per byte, 0 - 9. | |
122 | * | |
123 | * (decNumber library is configurable in how digits are stored, ICU has configured | |
124 | * it this way for convenience in replacing the old DigitList implementation.) | |
125 | */ | |
126 | class U_I18N_API DigitList : public UMemory { // Declare external to make compiler happy | |
127 | public: | |
128 | ||
129 | DigitList(); | |
130 | ~DigitList(); | |
131 | ||
132 | /* copy constructor | |
133 | * @param DigitList The object to be copied. | |
134 | * @return the newly created object. | |
135 | */ | |
136 | DigitList(const DigitList&); // copy constructor | |
137 | ||
138 | /* assignment operator | |
139 | * @param DigitList The object to be copied. | |
140 | * @return the newly created object. | |
141 | */ | |
142 | DigitList& operator=(const DigitList&); // assignment operator | |
143 | ||
144 | /** | |
145 | * Return true if another object is semantically equal to this one. | |
146 | * @param other The DigitList to be compared for equality | |
147 | * @return true if another object is semantically equal to this one. | |
148 | * return false otherwise. | |
149 | */ | |
150 | UBool operator==(const DigitList& other) const; | |
151 | ||
152 | int32_t compare(const DigitList& other); | |
153 | ||
154 | ||
155 | inline UBool operator!=(const DigitList& other) const { return !operator==(other); } | |
156 | ||
157 | /** | |
158 | * Clears out the digits. | |
159 | * Use before appending them. | |
160 | * Typically, you set a series of digits with append, then at the point | |
161 | * you hit the decimal point, you set myDigitList.fDecimalAt = myDigitList.fCount; | |
162 | * then go on appending digits. | |
163 | */ | |
164 | void clear(void); | |
165 | ||
166 | /** | |
167 | * Remove, by rounding, any fractional part of the decimal number, | |
168 | * leaving an integer value. | |
169 | */ | |
170 | void toIntegralValue(); | |
171 | ||
172 | /** | |
173 | * Appends digits to the list. | |
174 | * CAUTION: this function is not recommended for new code. | |
175 | * In the original DigitList implementation, decimal numbers were | |
176 | * parsed by appending them to a digit list as they were encountered. | |
177 | * With the revamped DigitList based on decNumber, append is very | |
178 | * inefficient, and the interaction with the exponent value is confusing. | |
179 | * Best avoided. | |
180 | * TODO: remove this function once all use has been replaced. | |
181 | * TODO: describe alternative to append() | |
182 | * @param digit The digit to be appended. | |
183 | */ | |
184 | void append(char digit); | |
185 | ||
186 | /** | |
187 | * Utility routine to get the value of the digit list | |
188 | * Returns 0.0 if zero length. | |
189 | * @return the value of the digit list. | |
190 | */ | |
191 | double getDouble(void) const; | |
192 | ||
193 | /** | |
194 | * Utility routine to get the value of the digit list | |
195 | * Make sure that fitsIntoLong() is called before calling this function. | |
196 | * Returns 0 if zero length. | |
197 | * @return the value of the digit list, return 0 if it is zero length | |
198 | */ | |
199 | int32_t getLong(void) /*const*/; | |
200 | ||
201 | /** | |
202 | * Utility routine to get the value of the digit list | |
203 | * Make sure that fitsIntoInt64() is called before calling this function. | |
204 | * Returns 0 if zero length. | |
205 | * @return the value of the digit list, return 0 if it is zero length | |
206 | */ | |
207 | int64_t getInt64(void) /*const*/; | |
208 | ||
209 | /** | |
210 | * Utility routine to get the value of the digit list as a decimal string. | |
211 | */ | |
212 | void getDecimal(CharString &str, UErrorCode &status); | |
213 | ||
214 | /** | |
215 | * Return true if the number represented by this object can fit into | |
216 | * a long. | |
217 | * @param ignoreNegativeZero True if negative zero is ignored. | |
218 | * @return true if the number represented by this object can fit into | |
219 | * a long, return false otherwise. | |
220 | */ | |
221 | UBool fitsIntoLong(UBool ignoreNegativeZero) /*const*/; | |
222 | ||
223 | /** | |
224 | * Return true if the number represented by this object can fit into | |
225 | * an int64_t. | |
226 | * @param ignoreNegativeZero True if negative zero is ignored. | |
227 | * @return true if the number represented by this object can fit into | |
228 | * a long, return false otherwise. | |
229 | */ | |
230 | UBool fitsIntoInt64(UBool ignoreNegativeZero) /*const*/; | |
231 | ||
232 | /** | |
233 | * Utility routine to set the value of the digit list from a double. | |
234 | * @param source The value to be set | |
235 | */ | |
236 | void set(double source); | |
237 | ||
238 | /** | |
239 | * Utility routine to set the value of the digit list from a long. | |
240 | * If a non-zero maximumDigits is specified, no more than that number of | |
241 | * significant digits will be produced. | |
242 | * @param source The value to be set | |
243 | */ | |
244 | void set(int32_t source); | |
245 | ||
246 | /** | |
247 | * Utility routine to set the value of the digit list from an int64. | |
248 | * If a non-zero maximumDigits is specified, no more than that number of | |
249 | * significant digits will be produced. | |
250 | * @param source The value to be set | |
251 | */ | |
252 | void set(int64_t source); | |
253 | ||
254 | /** | |
255 | * Utility routine to set the value of the digit list from an int64. | |
256 | * Does not set the decnumber unless requested later | |
257 | * If a non-zero maximumDigits is specified, no more than that number of | |
258 | * significant digits will be produced. | |
259 | * @param source The value to be set | |
260 | */ | |
261 | void setInteger(int64_t source); | |
262 | ||
263 | /** | |
264 | * Utility routine to set the value of the digit list from a decimal number | |
265 | * string. | |
266 | * @param source The value to be set. The string must be nul-terminated. | |
267 | * @param fastpathBits special flags for fast parsing | |
268 | */ | |
269 | void set(const StringPiece &source, UErrorCode &status, uint32_t fastpathBits = 0); | |
270 | ||
271 | /** | |
272 | * Multiply this = this * arg | |
273 | * This digitlist will be expanded if necessary to accomodate the result. | |
274 | * @param arg the number to multiply by. | |
275 | */ | |
276 | void mult(const DigitList &arg, UErrorCode &status); | |
277 | ||
278 | /** | |
279 | * Divide this = this / arg | |
280 | */ | |
281 | void div(const DigitList &arg, UErrorCode &status); | |
282 | ||
283 | // The following functions replace direct access to the original DigitList implmentation | |
284 | // data structures. | |
285 | ||
286 | void setRoundingMode(DecimalFormat::ERoundingMode m); | |
287 | ||
288 | /** Test a number for zero. | |
289 | * @return TRUE if the number is zero | |
290 | */ | |
291 | UBool isZero(void) const; | |
292 | ||
293 | /** Test for a Nan | |
294 | * @return TRUE if the number is a NaN | |
295 | */ | |
296 | UBool isNaN(void) const {return decNumberIsNaN(fDecNumber);} | |
297 | ||
298 | UBool isInfinite() const {return decNumberIsInfinite(fDecNumber);} | |
299 | ||
300 | /** Reduce, or normalize. Removes trailing zeroes, adjusts exponent appropriately. */ | |
301 | void reduce(); | |
302 | ||
303 | /** Remove trailing fraction zeros, adjust exponent accordingly. */ | |
304 | void trim(); | |
305 | ||
306 | /** Set to zero */ | |
307 | void setToZero() {uprv_decNumberZero(fDecNumber);} | |
308 | ||
309 | /** get the number of digits in the decimal number */ | |
310 | int32_t digits() const {return fDecNumber->digits;} | |
311 | ||
312 | /** | |
313 | * Round the number to the given number of digits. | |
314 | * @param maximumDigits The maximum number of digits to be shown. | |
315 | * Upon return, count will be less than or equal to maximumDigits. | |
316 | */ | |
317 | void round(int32_t maximumDigits); | |
318 | ||
319 | void roundFixedPoint(int32_t maximumFractionDigits); | |
320 | ||
321 | /** Ensure capacity for digits. Grow the storage if it is currently less than | |
322 | * the requested size. Capacity is not reduced if it is already greater | |
323 | * than requested. | |
324 | */ | |
325 | void ensureCapacity(int32_t requestedSize, UErrorCode &status); | |
326 | ||
327 | UBool isPositive(void) const { return decNumberIsNegative(fDecNumber) == 0;} | |
328 | void setPositive(UBool s); | |
329 | ||
330 | void setDecimalAt(int32_t d); | |
331 | int32_t getDecimalAt(); | |
332 | ||
333 | void setCount(int32_t c); | |
334 | int32_t getCount() const; | |
335 | ||
336 | /** | |
337 | * Set the digit in platform (invariant) format, from '0'..'9' | |
338 | * @param i index of digit | |
339 | * @param v digit value, from '0' to '9' in platform invariant format | |
340 | */ | |
341 | void setDigit(int32_t i, char v); | |
342 | ||
343 | /** | |
344 | * Get the digit in platform (invariant) format, from '0'..'9' inclusive | |
345 | * @param i index of digit | |
346 | * @return invariant format of the digit | |
347 | */ | |
348 | char getDigit(int32_t i); | |
349 | ||
350 | ||
351 | /** | |
352 | * Get the digit's value, as an integer from 0..9 inclusive. | |
353 | * Note that internally this value is a decNumberUnit, but ICU configures it to be a uint8_t. | |
354 | * @param i index of digit | |
355 | * @return value of that digit | |
356 | */ | |
357 | uint8_t getDigitValue(int32_t i); | |
358 | ||
359 | ||
360 | private: | |
361 | /* | |
362 | * These data members are intentionally public and can be set directly. | |
363 | *<P> | |
364 | * The value represented is given by placing the decimal point before | |
365 | * fDigits[fDecimalAt]. If fDecimalAt is < 0, then leading zeros between | |
366 | * the decimal point and the first nonzero digit are implied. If fDecimalAt | |
367 | * is > fCount, then trailing zeros between the fDigits[fCount-1] and the | |
368 | * decimal point are implied. | |
369 | * <P> | |
370 | * Equivalently, the represented value is given by f * 10^fDecimalAt. Here | |
371 | * f is a value 0.1 <= f < 1 arrived at by placing the digits in fDigits to | |
372 | * the right of the decimal. | |
373 | * <P> | |
374 | * DigitList is normalized, so if it is non-zero, fDigits[0] is non-zero. We | |
375 | * don't allow denormalized numbers because our exponent is effectively of | |
376 | * unlimited magnitude. The fCount value contains the number of significant | |
377 | * digits present in fDigits[]. | |
378 | * <P> | |
379 | * Zero is represented by any DigitList with fCount == 0 or with each fDigits[i] | |
380 | * for all i <= fCount == '0'. | |
381 | * | |
382 | * int32_t fDecimalAt; | |
383 | * int32_t fCount; | |
384 | * UBool fIsPositive; | |
385 | * char *fDigits; | |
386 | * DecimalFormat::ERoundingMode fRoundingMode; | |
387 | */ | |
388 | ||
389 | public: | |
390 | decContext fContext; // public access to status flags. | |
391 | ||
392 | private: | |
393 | decNumber *fDecNumber; | |
394 | MaybeStackHeaderAndArray<decNumber, char, DEFAULT_DIGITS> fStorage; | |
395 | ||
396 | /* Cached double value corresponding to this decimal number. | |
397 | * This is an optimization for the formatting implementation, which may | |
398 | * ask for the double value multiple times. | |
399 | */ | |
400 | union DoubleOrInt64 { | |
401 | double fDouble; | |
402 | int64_t fInt64; | |
403 | } fUnion; | |
404 | enum EHave { | |
405 | kNone=0, | |
406 | kDouble, | |
407 | kInt64 | |
408 | } fHave; | |
409 | ||
410 | ||
411 | ||
412 | UBool shouldRoundUp(int32_t maximumDigits) const; | |
413 | ||
414 | public: | |
415 | ||
416 | using UMemory::operator new; | |
417 | using UMemory::operator delete; | |
418 | ||
419 | /** | |
420 | * Placement new for stack usage | |
421 | * @internal | |
422 | */ | |
423 | static inline void * U_EXPORT2 operator new(size_t /*size*/, void * onStack, EStackMode /*mode*/) U_NO_THROW { return onStack; } | |
424 | ||
425 | /** | |
426 | * Placement delete for stack usage | |
427 | * @internal | |
428 | */ | |
429 | static inline void U_EXPORT2 operator delete(void * /*ptr*/, void * /*onStack*/, EStackMode /*mode*/) U_NO_THROW {} | |
430 | ||
431 | private: | |
432 | inline void internalSetDouble(double d) { | |
433 | fHave = kDouble; | |
434 | fUnion.fDouble=d; | |
435 | } | |
436 | inline void internalSetInt64(int64_t d) { | |
437 | fHave = kInt64; | |
438 | fUnion.fInt64=d; | |
439 | } | |
440 | inline void internalClear() { | |
441 | fHave = kNone; | |
442 | } | |
443 | }; | |
444 | ||
445 | ||
446 | U_NAMESPACE_END | |
447 | ||
448 | #endif // #if !UCONFIG_NO_FORMATTING | |
449 | #endif // _DIGITLST | |
450 | ||
451 | //eof |