VisibleDigits & FixedPrecision::initVisibleDigits( int64_t value, VisibleDigits &digits, UErrorCode &status) const { if (U_FAILURE(status)) { return digits; } if (!fRoundingIncrement.isZero()) { // If we have round increment, use digit list. DigitList digitList; digitList.set(value); return initVisibleDigits(digitList, digits, status); } // Try fast path if (initVisibleDigits(value, 0, digits, status)) { digits.fAbsDoubleValue = fabs((double) value); digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits(); return digits; } // Oops have to use digit list DigitList digitList; digitList.set(value); return initVisibleDigits(digitList, digits, status); }
VisibleDigits & FixedPrecision::initVisibleDigits( double value, VisibleDigits &digits, UErrorCode &status) const { if (U_FAILURE(status)) { return digits; } digits.clear(); if (uprv_isNaN(value)) { digits.setNaN(); return digits; } if (uprv_isPositiveInfinity(value)) { digits.setInfinite(); return digits; } if (uprv_isNegativeInfinity(value)) { digits.setInfinite(); digits.setNegative(); return digits; } if (!fRoundingIncrement.isZero()) { // If we have round increment, use digit list. DigitList digitList; digitList.set(value); return initVisibleDigits(digitList, digits, status); } // Try to find n such that value * 10^n is an integer int32_t n = -1; double scaled; for (int32_t i = 0; i < UPRV_LENGTHOF(gPower10); ++i) { scaled = value * gPower10[i]; if (scaled > MAX_INT64_IN_DOUBLE || scaled < -MAX_INT64_IN_DOUBLE) { break; } if (scaled == floor(scaled)) { n = i; break; } } // Try fast path if (n >= 0 && initVisibleDigits(scaled, -n, digits, status)) { digits.fAbsDoubleValue = fabs(value); digits.fAbsDoubleValueSet = U_SUCCESS(status) && !digits.isOverMaxDigits(); // Adjust for negative 0 becuase when we cast to an int64, // negative 0 becomes positive 0. if (scaled == 0.0 && uprv_isNegative(scaled)) { digits.setNegative(); } return digits; } // Oops have to use digit list DigitList digitList; digitList.set(value); return initVisibleDigits(digitList, digits, status); }
// --------------------------------------- void Formattable::setDecimalNumber(StringPiece numberString, UErrorCode &status) { if (U_FAILURE(status)) { return; } dispose(); // Copy the input string and nul-terminate it. // The decNumber library requires nul-terminated input. StringPiece input // is not guaranteed nul-terminated. Too bad. // CharString automatically adds the nul. DigitList *dnum = new DigitList(); // TODO: use getInternalDigitList if (dnum == NULL) { status = U_MEMORY_ALLOCATION_ERROR; return; } dnum->set(CharString(numberString, status).toStringPiece(), status); if (U_FAILURE(status)) { delete dnum; return; // String didn't contain a decimal number. } adoptDigitList(dnum); // Note that we do not hang on to the caller's input string. // If we are asked for the string, we will regenerate one from fDecimalNum. }
/** * If in "by digits" mode, fills in the substitution one decimal digit * at a time using the rule set containing this substitution. * Otherwise, uses the superclass function. * @param number The number being formatted * @param toInsertInto The string to insert the result of formatting * the substitution into * @param pos The position of the owning rule's rule text in * toInsertInto */ void FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const { // if we're not in "byDigits" mode, just use the inherited // doSubstitution() routine if (!byDigits) { NFSubstitution::doSubstitution(number, toInsertInto, _pos); // if we're in "byDigits" mode, transform the value into an integer // by moving the decimal point eight places to the right and // pulling digits off the right one at a time, formatting each digit // as an integer using this substitution's owning rule set // (this is slower, but more accurate, than doing it from the // other end) } else { // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); // // this flag keeps us from formatting trailing zeros. It starts // // out false because we're pulling from the right, and switches // // to true the first time we encounter a non-zero digit // UBool doZeros = FALSE; // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { // int64_t digit = numberToFormat % 10; // if (digit != 0 || doZeros) { // if (doZeros && useSpaces) { // toInsertInto.insert(_pos + getPos(), gSpace); // } // doZeros = TRUE; // getRuleSet()->format(digit, toInsertInto, _pos + getPos()); // } // numberToFormat /= 10; // } DigitList dl; dl.set(number); dl.roundFixedPoint(20); // round to 20 fraction digits. dl.reduce(); // Removes any trailing zeros. UBool pad = FALSE; for (int32_t didx = dl.getCount()-1; didx>=dl.getDecimalAt(); didx--) { // Loop iterates over fraction digits, starting with the LSD. // include both real digits from the number, and zeros // to the left of the MSD but to the right of the decimal point. if (pad && useSpaces) { toInsertInto.insert(_pos + getPos(), gSpace); } else { pad = TRUE; } int64_t digit = didx>=0 ? dl.getDigit(didx) - '0' : 0; getRuleSet()->format(digit, toInsertInto, _pos + getPos()); } if (!pad) { // hack around lack of precision in digitlist. if we would end up with // "foo point" make sure we add a " zero" to the end. getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos()); } } }
VisibleDigitsWithExponent & ScientificPrecision::initVisibleDigitsWithExponent( int64_t value, VisibleDigitsWithExponent &digits, UErrorCode &status) const { if (U_FAILURE(status)) { return digits; } DigitList digitList; digitList.set(value); return initVisibleDigitsWithExponent(digitList, digits, status); }
void PluralRulesTest::checkSelect(const LocalPointer<PluralRules> &rules, UErrorCode &status, int32_t line, const char *keyword, ...) { // The varargs parameters are a const char* strings, each being a decimal number. // The formatting of the numbers as strings is significant, e.g. // the difference between "2" and "2.0" can affect which rule matches (which keyword is selected). // Note: rules parameter is a LocalPointer reference rather than a PluralRules * to avoid having // to write getAlias() at every (numerous) call site. if (U_FAILURE(status)) { errln("file %s, line %d, ICU error status: %s.", __FILE__, line, u_errorName(status)); status = U_ZERO_ERROR; return; } if (rules == NULL) { errln("file %s, line %d: rules pointer is NULL", __FILE__, line); return; } va_list ap; va_start(ap, keyword); for (;;) { const char *num = va_arg(ap, const char *); if (strcmp(num, END_MARK) == 0) { break; } // DigitList is a convenient way to parse the decimal number string and get a double. DigitList dl; dl.set(StringPiece(num), status); if (U_FAILURE(status)) { errln("file %s, line %d, ICU error status: %s.", __FILE__, line, u_errorName(status)); status = U_ZERO_ERROR; continue; } double numDbl = dl.getDouble(); const char *decimalPoint = strchr(num, '.'); int fractionDigitCount = decimalPoint == NULL ? 0 : (num + strlen(num) - 1) - decimalPoint; int fractionDigits = fractionDigitCount == 0 ? 0 : atoi(decimalPoint + 1); FixedDecimal ni(numDbl, fractionDigitCount, fractionDigits); UnicodeString actualKeyword = rules->select(ni); if (actualKeyword != UnicodeString(keyword)) { errln("file %s, line %d, select(%s) returned incorrect keyword. Expected %s, got %s", __FILE__, line, num, keyword, US(actualKeyword).cstr()); } } va_end(ap); }
/** * If in "by digits" mode, fills in the substitution one decimal digit * at a time using the rule set containing this substitution. * Otherwise, uses the superclass function. * @param number The number being formatted * @param toInsertInto The string to insert the result of formatting * the substitution into * @param pos The position of the owning rule's rule text in * toInsertInto */ void FractionalPartSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const { // if we're not in "byDigits" mode, just use the inherited // doSubstitution() routine if (!byDigits) { NFSubstitution::doSubstitution(number, toInsertInto, _pos); // if we're in "byDigits" mode, transform the value into an integer // by moving the decimal point eight places to the right and // pulling digits off the right one at a time, formatting each digit // as an integer using this substitution's owning rule set // (this is slower, but more accurate, than doing it from the // other end) } else { // int32_t numberToFormat = (int32_t)uprv_round(transformNumber(number) * uprv_pow(10, kMaxDecimalDigits)); // // this flag keeps us from formatting trailing zeros. It starts // // out false because we're pulling from the right, and switches // // to true the first time we encounter a non-zero digit // UBool doZeros = FALSE; // for (int32_t i = 0; i < kMaxDecimalDigits; i++) { // int64_t digit = numberToFormat % 10; // if (digit != 0 || doZeros) { // if (doZeros && useSpaces) { // toInsertInto.insert(_pos + getPos(), gSpace); // } // doZeros = TRUE; // getRuleSet()->format(digit, toInsertInto, _pos + getPos()); // } // numberToFormat /= 10; // } DigitList dl; dl.set(number, 20, TRUE); UBool pad = FALSE; while (dl.fCount > (dl.fDecimalAt <= 0 ? 0 : dl.fDecimalAt)) { if (pad && useSpaces) { toInsertInto.insert(_pos + getPos(), gSpace); } else { pad = TRUE; } getRuleSet()->format((int64_t)(dl.fDigits[--dl.fCount] - '0'), toInsertInto, _pos + getPos()); } while (dl.fDecimalAt < 0) { if (pad && useSpaces) { toInsertInto.insert(_pos + getPos(), gSpace); } else { pad = TRUE; } getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos()); ++dl.fDecimalAt; } if (!pad) { // hack around lack of precision in digitlist. if we would end up with // "foo point" make sure we add a " zero" to the end. getRuleSet()->format((int64_t)0, toInsertInto, _pos + getPos()); } } }