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 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 !defined(U_DARWIN) 65 template 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 */ 122 class U_I18N_API DigitList : public UMemory { // Declare external to make compiler happy 123 public: 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 */ isNaN(void)282 UBool isNaN(void) const {return decNumberIsNaN(fDecNumber);}; 283 isInfinite()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 */ setToZero()293 void setToZero() {uprv_decNumberZero(fDecNumber);}; 294 295 /** get the number of digits in the decimal number */ digits()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 isPositive(void)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 346 private: 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 375 private: 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 394 U_NAMESPACE_END 395 396 #endif // #if !UCONFIG_NO_FORMATTING 397 #endif // _DIGITLST 398 399 //eof 400