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 452