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); }
// --------------------------------------- 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. }
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); }
/** * Currently, getDouble() depends on strtod() to do its conversion. * * WARNING!! * This is an extremely costly function. ~1/2 of the conversion time * can be linked to this function. */ double DigitList::getDouble() const { { Mutex mutex; if (fHave == kDouble) { return fUnion.fDouble; } } double tDouble = 0.0; if (isZero()) { tDouble = 0.0; if (decNumberIsNegative(fDecNumber)) { tDouble /= -1; } } else if (isInfinite()) { if (std::numeric_limits<double>::has_infinity) { tDouble = std::numeric_limits<double>::infinity(); } else { tDouble = std::numeric_limits<double>::max(); } if (!isPositive()) { tDouble = -tDouble; //this was incorrectly "-fDouble" originally. } } else { MaybeStackArray<char, MAX_DBL_DIGITS+18> s; // Note: 14 is a magic constant from the decNumber library documentation, // the max number of extra characters beyond the number of digits // needed to represent the number in string form. Add a few more // for the additional digits we retain. // Round down to appx. double precision, if the number is longer than that. // Copy the number first, so that we don't modify the original. if (getCount() > MAX_DBL_DIGITS + 3) { DigitList numToConvert(*this); numToConvert.reduce(); // Removes any trailing zeros, so that digit count is good. numToConvert.round(MAX_DBL_DIGITS+3); uprv_decNumberToString(numToConvert.fDecNumber, s.getAlias()); // TODO: how many extra digits should be included for an accurate conversion? } else { uprv_decNumberToString(this->fDecNumber, s.getAlias()); } U_ASSERT(uprv_strlen(&s[0]) < MAX_DBL_DIGITS+18); char *end = NULL; tDouble = decimalStrToDouble(s.getAlias(), &end); } { Mutex mutex; DigitList *nonConstThis = const_cast<DigitList *>(this); nonConstThis->internalSetDouble(tDouble); } return tDouble; }
void DigitList::mult(const DigitList &other, UErrorCode &status) { fContext.status = 0; int32_t requiredDigits = this->digits() + other.digits(); if (requiredDigits > fContext.digits) { reduce(); // Remove any trailing zeros int32_t requiredDigits = this->digits() + other.digits(); ensureCapacity(requiredDigits, status); } uprv_decNumberMultiply(fDecNumber, fDecNumber, other.fDecNumber, &fContext); fHaveDouble = FALSE; }
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); }
/* * Multiply * The number will be expanded if need be to retain full precision. * In practice, for formatting, multiply is by 10, 100 or 1000, so more digits * will not be required for this use. */ void DigitList::mult(const DigitList &other, UErrorCode &status) { if (U_FAILURE(status)) { return; } fContext.status = 0; int32_t requiredDigits = this->digits() + other.digits(); if (requiredDigits > fContext.digits) { reduce(); // Remove any trailing zeros int32_t requiredDigits = this->digits() + other.digits(); ensureCapacity(requiredDigits, status); } uprv_decNumberMultiply(fDecNumber, fDecNumber, other.fDecNumber, &fContext); internalClear(); }
UBool FixedPrecision::handleNonNumeric(DigitList &value, VisibleDigits &digits) { if (value.isNaN()) { digits.setNaN(); return TRUE; } if (value.isInfinite()) { digits.setInfinite(); if (!value.isPositive()) { digits.setNegative(); } return TRUE; } return FALSE; }
/** * 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()); } } }
void DecimalFormatPatternParser::applyPatternWithoutExpandAffix( const UnicodeString& pattern, DecimalFormatPattern& out, UParseError& parseError, UErrorCode& status) { if (U_FAILURE(status)) { return; } out = DecimalFormatPattern(); // Clear error struct parseError.offset = -1; parseError.preContext[0] = parseError.postContext[0] = (UChar)0; // TODO: Travis Keep: This won't always work. UChar nineDigit = (UChar)(fZeroDigit + 9); int32_t digitLen = fDigit.length(); int32_t groupSepLen = fGroupingSeparator.length(); int32_t decimalSepLen = fDecimalSeparator.length(); int32_t pos = 0; int32_t patLen = pattern.length(); // Part 0 is the positive pattern. Part 1, if present, is the negative // pattern. for (int32_t part=0; part<2 && pos<patLen; ++part) { // The subpart ranges from 0 to 4: 0=pattern proper, 1=prefix, // 2=suffix, 3=prefix in quote, 4=suffix in quote. Subpart 0 is // between the prefix and suffix, and consists of pattern // characters. In the prefix and suffix, percent, perMill, and // currency symbols are recognized and translated. int32_t subpart = 1, sub0Start = 0, sub0Limit = 0, sub2Limit = 0; // It's important that we don't change any fields of this object // prematurely. We set the following variables for the multiplier, // grouping, etc., and then only change the actual object fields if // everything parses correctly. This also lets us register // the data from part 0 and ignore the part 1, except for the // prefix and suffix. UnicodeString prefix; UnicodeString suffix; int32_t decimalPos = -1; int32_t multiplier = 1; int32_t digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0, sigDigitCount = 0; int8_t groupingCount = -1; int8_t groupingCount2 = -1; int32_t padPos = -1; UChar32 padChar = 0; int32_t roundingPos = -1; DigitList roundingInc; int8_t expDigits = -1; UBool expSignAlways = FALSE; // The affix is either the prefix or the suffix. UnicodeString* affix = &prefix; int32_t start = pos; UBool isPartDone = FALSE; UChar32 ch; for (; !isPartDone && pos < patLen; ) { // Todo: account for surrogate pairs ch = pattern.char32At(pos); switch (subpart) { case 0: // Pattern proper subpart (between prefix & suffix) // Process the digits, decimal, and grouping characters. We // record five pieces of information. We expect the digits // to occur in the pattern ####00.00####, and we record the // number of left digits, zero (central) digits, and right // digits. The position of the last grouping character is // recorded (should be somewhere within the first two blocks // of characters), as is the position of the decimal point, // if any (should be in the zero digits). If there is no // decimal point, then there should be no right digits. if (pattern.compare(pos, digitLen, fDigit) == 0) { if (zeroDigitCount > 0 || sigDigitCount > 0) { ++digitRightCount; } else { ++digitLeftCount; } if (groupingCount >= 0 && decimalPos < 0) { ++groupingCount; } pos += digitLen; } else if ((ch >= fZeroDigit && ch <= nineDigit) || ch == fSigDigit) { if (digitRightCount > 0) { // Unexpected '0' debug("Unexpected '0'") status = U_UNEXPECTED_TOKEN; syntaxError(pattern,pos,parseError); return; } if (ch == fSigDigit) { ++sigDigitCount; } else { if (ch != fZeroDigit && roundingPos < 0) { roundingPos = digitLeftCount + zeroDigitCount; } if (roundingPos >= 0) { roundingInc.append((char)(ch - fZeroDigit + '0')); } ++zeroDigitCount; } if (groupingCount >= 0 && decimalPos < 0) { ++groupingCount; } pos += U16_LENGTH(ch); } else if (pattern.compare(pos, groupSepLen, fGroupingSeparator) == 0) { if (decimalPos >= 0) { // Grouping separator after decimal debug("Grouping separator after decimal") status = U_UNEXPECTED_TOKEN; syntaxError(pattern,pos,parseError); return; } groupingCount2 = groupingCount; groupingCount = 0; pos += groupSepLen; } else if (pattern.compare(pos, decimalSepLen, fDecimalSeparator) == 0) { if (decimalPos >= 0) { // Multiple decimal separators debug("Multiple decimal separators") status = U_MULTIPLE_DECIMAL_SEPARATORS; syntaxError(pattern,pos,parseError); return; } // Intentionally incorporate the digitRightCount, // even though it is illegal for this to be > 0 // at this point. We check pattern syntax below. decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; pos += decimalSepLen; } else { if (pattern.compare(pos, fExponent.length(), fExponent) == 0) { if (expDigits >= 0) { // Multiple exponential symbols debug("Multiple exponential symbols") status = U_MULTIPLE_EXPONENTIAL_SYMBOLS; syntaxError(pattern,pos,parseError); return; } if (groupingCount >= 0) { // Grouping separator in exponential pattern debug("Grouping separator in exponential pattern") status = U_MALFORMED_EXPONENTIAL_PATTERN; syntaxError(pattern,pos,parseError); return; } pos += fExponent.length(); // Check for positive prefix if (pos < patLen && pattern.compare(pos, fPlus.length(), fPlus) == 0) { expSignAlways = TRUE; pos += fPlus.length(); } // Use lookahead to parse out the exponential part of the // pattern, then jump into suffix subpart. expDigits = 0; while (pos < patLen && pattern.char32At(pos) == fZeroDigit) { ++expDigits; pos += U16_LENGTH(fZeroDigit); } // 1. Require at least one mantissa pattern digit // 2. Disallow "#+ @" in mantissa // 3. Require at least one exponent pattern digit if (((digitLeftCount + zeroDigitCount) < 1 && (sigDigitCount + digitRightCount) < 1) || (sigDigitCount > 0 && digitLeftCount > 0) || expDigits < 1) { // Malformed exponential pattern debug("Malformed exponential pattern") status = U_MALFORMED_EXPONENTIAL_PATTERN; syntaxError(pattern,pos,parseError); return; } } // Transition to suffix subpart subpart = 2; // suffix subpart affix = &suffix; sub0Limit = pos; continue; } break; case 1: // Prefix subpart case 2: // Suffix subpart // Process the prefix / suffix characters // Process unquoted characters seen in prefix or suffix // subpart. // Several syntax characters implicitly begins the // next subpart if we are in the prefix; otherwise // they are illegal if unquoted. if (!pattern.compare(pos, digitLen, fDigit) || !pattern.compare(pos, groupSepLen, fGroupingSeparator) || !pattern.compare(pos, decimalSepLen, fDecimalSeparator) || (ch >= fZeroDigit && ch <= nineDigit) || ch == fSigDigit) { if (subpart == 1) { // prefix subpart subpart = 0; // pattern proper subpart sub0Start = pos; // Reprocess this character continue; } else { status = U_UNQUOTED_SPECIAL; syntaxError(pattern,pos,parseError); return; } } else if (ch == kCurrencySign) { affix->append(kQuote); // Encode currency // Use lookahead to determine if the currency sign is // doubled or not. U_ASSERT(U16_LENGTH(kCurrencySign) == 1); if ((pos+1) < pattern.length() && pattern[pos+1] == kCurrencySign) { affix->append(kCurrencySign); ++pos; // Skip over the doubled character if ((pos+1) < pattern.length() && pattern[pos+1] == kCurrencySign) { affix->append(kCurrencySign); ++pos; // Skip over the doubled character out.fCurrencySignCount = fgCurrencySignCountInPluralFormat; } else { out.fCurrencySignCount = fgCurrencySignCountInISOFormat; } } else { out.fCurrencySignCount = fgCurrencySignCountInSymbolFormat; } // Fall through to append(ch) } else if (ch == kQuote) { // A quote outside quotes indicates either the opening // quote or two quotes, which is a quote literal. That is, // we have the first quote in 'do' or o''clock. U_ASSERT(U16_LENGTH(kQuote) == 1); ++pos; if (pos < pattern.length() && pattern[pos] == kQuote) { affix->append(kQuote); // Encode quote // Fall through to append(ch) } else { subpart += 2; // open quote continue; } } else if (pattern.compare(pos, fSeparator.length(), fSeparator) == 0) { // Don't allow separators in the prefix, and don't allow // separators in the second pattern (part == 1). if (subpart == 1 || part == 1) { // Unexpected separator debug("Unexpected separator") status = U_UNEXPECTED_TOKEN; syntaxError(pattern,pos,parseError); return; } sub2Limit = pos; isPartDone = TRUE; // Go to next part pos += fSeparator.length(); break; } else if (pattern.compare(pos, fPercent.length(), fPercent) == 0) { // Next handle characters which are appended directly. if (multiplier != 1) { // Too many percent/perMill characters debug("Too many percent characters") status = U_MULTIPLE_PERCENT_SYMBOLS; syntaxError(pattern,pos,parseError); return; } affix->append(kQuote); // Encode percent/perMill affix->append(kPatternPercent); // Use unlocalized pattern char multiplier = 100; pos += fPercent.length(); break; } else if (pattern.compare(pos, fPerMill.length(), fPerMill) == 0) { // Next handle characters which are appended directly. if (multiplier != 1) { // Too many percent/perMill characters debug("Too many perMill characters") status = U_MULTIPLE_PERMILL_SYMBOLS; syntaxError(pattern,pos,parseError); return; } affix->append(kQuote); // Encode percent/perMill affix->append(kPatternPerMill); // Use unlocalized pattern char multiplier = 1000; pos += fPerMill.length(); break; } else if (pattern.compare(pos, fPadEscape.length(), fPadEscape) == 0) { if (padPos >= 0 || // Multiple pad specifiers (pos+1) == pattern.length()) { // Nothing after padEscape debug("Multiple pad specifiers") status = U_MULTIPLE_PAD_SPECIFIERS; syntaxError(pattern,pos,parseError); return; } padPos = pos; pos += fPadEscape.length(); padChar = pattern.char32At(pos); pos += U16_LENGTH(padChar); break; } else if (pattern.compare(pos, fMinus.length(), fMinus) == 0) { affix->append(kQuote); // Encode minus affix->append(kPatternMinus); pos += fMinus.length(); break; } else if (pattern.compare(pos, fPlus.length(), fPlus) == 0) { affix->append(kQuote); // Encode plus affix->append(kPatternPlus); pos += fPlus.length(); break; } // Unquoted, non-special characters fall through to here, as // well as other code which needs to append something to the // affix. affix->append(ch); pos += U16_LENGTH(ch); break; case 3: // Prefix subpart, in quote case 4: // Suffix subpart, in quote // A quote within quotes indicates either the closing // quote or two quotes, which is a quote literal. That is, // we have the second quote in 'do' or 'don''t'. if (ch == kQuote) { ++pos; if (pos < pattern.length() && pattern[pos] == kQuote) { affix->append(kQuote); // Encode quote // Fall through to append(ch) } else { subpart -= 2; // close quote continue; } } affix->append(ch); pos += U16_LENGTH(ch); break; } } if (sub0Limit == 0) { sub0Limit = pattern.length(); } if (sub2Limit == 0) { sub2Limit = pattern.length(); } /* Handle patterns with no '0' pattern character. These patterns * are legal, but must be recodified to make sense. "##.###" -> * "#0.###". ".###" -> ".0##". * * We allow patterns of the form "####" to produce a zeroDigitCount * of zero (got that?); although this seems like it might make it * possible for format() to produce empty strings, format() checks * for this condition and outputs a zero digit in this situation. * Having a zeroDigitCount of zero yields a minimum integer digits * of zero, which allows proper round-trip patterns. We don't want * "#" to become "#0" when toPattern() is called (even though that's * what it really is, semantically). */ if (zeroDigitCount == 0 && sigDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) { // Handle "###.###" and "###." and ".###" int n = decimalPos; if (n == 0) ++n; // Handle ".###" digitRightCount = digitLeftCount - n; digitLeftCount = n - 1; zeroDigitCount = 1; } // Do syntax checking on the digits, decimal points, and quotes. if ((decimalPos < 0 && digitRightCount > 0 && sigDigitCount == 0) || (decimalPos >= 0 && (sigDigitCount > 0 || decimalPos < digitLeftCount || decimalPos > (digitLeftCount + zeroDigitCount))) || groupingCount == 0 || groupingCount2 == 0 || (sigDigitCount > 0 && zeroDigitCount > 0) || subpart > 2) { // subpart > 2 == unmatched quote debug("Syntax error") status = U_PATTERN_SYNTAX_ERROR; syntaxError(pattern,pos,parseError); return; } // Make sure pad is at legal position before or after affix. if (padPos >= 0) { if (padPos == start) { padPos = DecimalFormatPattern::kPadBeforePrefix; } else if (padPos+2 == sub0Start) { padPos = DecimalFormatPattern::kPadAfterPrefix; } else if (padPos == sub0Limit) { padPos = DecimalFormatPattern::kPadBeforeSuffix; } else if (padPos+2 == sub2Limit) { padPos = DecimalFormatPattern::kPadAfterSuffix; } else { // Illegal pad position debug("Illegal pad position") status = U_ILLEGAL_PAD_POSITION; syntaxError(pattern,pos,parseError); return; } } if (part == 0) { out.fPosPatternsBogus = FALSE; out.fPosPrefixPattern = prefix; out.fPosSuffixPattern = suffix; out.fNegPatternsBogus = TRUE; out.fNegPrefixPattern.remove(); out.fNegSuffixPattern.remove(); out.fUseExponentialNotation = (expDigits >= 0); if (out.fUseExponentialNotation) { out.fMinExponentDigits = expDigits; } out.fExponentSignAlwaysShown = expSignAlways; int32_t digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount; // The effectiveDecimalPos is the position the decimal is at or // would be at if there is no decimal. Note that if // decimalPos<0, then digitTotalCount == digitLeftCount + // zeroDigitCount. int32_t effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount; UBool isSigDig = (sigDigitCount > 0); out.fUseSignificantDigits = isSigDig; if (isSigDig) { out.fMinimumSignificantDigits = sigDigitCount; out.fMaximumSignificantDigits = sigDigitCount + digitRightCount; } else { int32_t minInt = effectiveDecimalPos - digitLeftCount; out.fMinimumIntegerDigits = minInt; out.fMaximumIntegerDigits = out.fUseExponentialNotation ? digitLeftCount + out.fMinimumIntegerDigits : gDefaultMaxIntegerDigits; out.fMaximumFractionDigits = decimalPos >= 0 ? (digitTotalCount - decimalPos) : 0; out.fMinimumFractionDigits = decimalPos >= 0 ? (digitLeftCount + zeroDigitCount - decimalPos) : 0; } out.fGroupingUsed = groupingCount > 0; out.fGroupingSize = (groupingCount > 0) ? groupingCount : 0; out.fGroupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount) ? groupingCount2 : 0; out.fMultiplier = multiplier; out.fDecimalSeparatorAlwaysShown = decimalPos == 0 || decimalPos == digitTotalCount; if (padPos >= 0) { out.fPadPosition = (DecimalFormatPattern::EPadPosition) padPos; // To compute the format width, first set up sub0Limit - // sub0Start. Add in prefix/suffix length later. // fFormatWidth = prefix.length() + suffix.length() + // sub0Limit - sub0Start; out.fFormatWidth = sub0Limit - sub0Start; out.fPad = padChar; } else { out.fFormatWidth = 0; } if (roundingPos >= 0) { out.fRoundingIncrementUsed = TRUE; roundingInc.setDecimalAt(effectiveDecimalPos - roundingPos); out.fRoundingIncrement = roundingInc; } else { out.fRoundingIncrementUsed = FALSE; } } else { out.fNegPatternsBogus = FALSE; out.fNegPrefixPattern = prefix; out.fNegSuffixPattern = suffix; } } if (pattern.length() == 0) { out.fNegPatternsBogus = TRUE; out.fNegPrefixPattern.remove(); out.fNegSuffixPattern.remove(); out.fPosPatternsBogus = FALSE; out.fPosPrefixPattern.remove(); out.fPosSuffixPattern.remove(); out.fMinimumIntegerDigits = 0; out.fMaximumIntegerDigits = kDoubleIntegerDigits; out.fMinimumFractionDigits = 0; out.fMaximumFractionDigits = kDoubleFractionDigits; out.fUseExponentialNotation = FALSE; out.fCurrencySignCount = fgCurrencySignCountZero; out.fGroupingUsed = FALSE; out.fGroupingSize = 0; out.fGroupingSize2 = 0; out.fMultiplier = 1; out.fDecimalSeparatorAlwaysShown = FALSE; out.fFormatWidth = 0; out.fRoundingIncrementUsed = FALSE; } // If there was no negative pattern, or if the negative pattern is // identical to the positive pattern, then prepend the minus sign to the // positive pattern to form the negative pattern. if (out.fNegPatternsBogus || (out.fNegPrefixPattern == out.fPosPrefixPattern && out.fNegSuffixPattern == out.fPosSuffixPattern)) { out.fNegPatternsBogus = FALSE; out.fNegSuffixPattern = out.fPosSuffixPattern; out.fNegPrefixPattern.remove(); out.fNegPrefixPattern.append(kQuote).append(kPatternMinus) .append(out.fPosPrefixPattern); } // TODO: Deprecate/Remove out.fNegSuffixPattern and 3 other fields. AffixPattern::parseAffixString( out.fNegSuffixPattern, out.fNegSuffixAffix, status); AffixPattern::parseAffixString( out.fPosSuffixPattern, out.fPosSuffixAffix, status); AffixPattern::parseAffixString( out.fNegPrefixPattern, out.fNegPrefixAffix, status); AffixPattern::parseAffixString( out.fPosPrefixPattern, out.fPosPrefixAffix, status); }
/** * Currently, getDouble() depends on strtod() to do its conversion. * * WARNING!! * This is an extremely costly function. ~1/2 of the conversion time * can be linked to this function. */ double DigitList::getDouble() const { static char gDecimal = 0; char decimalSeparator; { Mutex mutex; if (fHave == kDouble) { return fUnion.fDouble; } else if(fHave == kInt64) { return (double)fUnion.fInt64; } decimalSeparator = gDecimal; } if (decimalSeparator == 0) { // We need to know the decimal separator character that will be used with strtod(). // Depends on the C runtime global locale. // Most commonly is '.' // TODO: caching could fail if the global locale is changed on the fly. char rep[MAX_DIGITS]; sprintf(rep, "%+1.1f", 1.0); decimalSeparator = rep[2]; } double tDouble = 0.0; if (isZero()) { tDouble = 0.0; if (decNumberIsNegative(fDecNumber)) { tDouble /= -1; } } else if (isInfinite()) { if (std::numeric_limits<double>::has_infinity) { tDouble = std::numeric_limits<double>::infinity(); } else { tDouble = std::numeric_limits<double>::max(); } if (!isPositive()) { tDouble = -tDouble; //this was incorrectly "-fDouble" originally. } } else { MaybeStackArray<char, MAX_DBL_DIGITS+18> s; // Note: 14 is a magic constant from the decNumber library documentation, // the max number of extra characters beyond the number of digits // needed to represent the number in string form. Add a few more // for the additional digits we retain. // Round down to appx. double precision, if the number is longer than that. // Copy the number first, so that we don't modify the original. if (getCount() > MAX_DBL_DIGITS + 3) { DigitList numToConvert(*this); numToConvert.reduce(); // Removes any trailing zeros, so that digit count is good. numToConvert.round(MAX_DBL_DIGITS+3); uprv_decNumberToString(numToConvert.fDecNumber, s); // TODO: how many extra digits should be included for an accurate conversion? } else { uprv_decNumberToString(this->fDecNumber, s); } U_ASSERT(uprv_strlen(&s[0]) < MAX_DBL_DIGITS+18); if (decimalSeparator != '.') { char *decimalPt = strchr(s, '.'); if (decimalPt != NULL) { *decimalPt = decimalSeparator; } } char *end = NULL; tDouble = uprv_strtod(s, &end); } { Mutex mutex; DigitList *nonConstThis = const_cast<DigitList *>(this); nonConstThis->internalSetDouble(tDouble); gDecimal = decimalSeparator; } return tDouble; }
int32_t ScientificPrecision::toScientific(DigitList &value) const { return value.toScientific( fMantissa.fMin.getIntDigitCount(), getMultiplier()); }
UBool FractionalPartSubstitution::doParse(const UnicodeString& text, ParsePosition& parsePosition, double baseValue, double /*upperBound*/, UBool lenientParse, Formattable& resVal) const { // if we're not in byDigits mode, we can just use the inherited // doParse() if (!byDigits) { return NFSubstitution::doParse(text, parsePosition, baseValue, 0, lenientParse, resVal); // if we ARE in byDigits mode, parse the text one digit at a time // using this substitution's owning rule set (we do this by setting // upperBound to 10 when calling doParse() ) until we reach // nonmatching text } else { UnicodeString workText(text); ParsePosition workPos(1); double result = 0; int32_t digit; // double p10 = 0.1; DigitList dl; NumberFormat* fmt = NULL; while (workText.length() > 0 && workPos.getIndex() != 0) { workPos.setIndex(0); Formattable temp; getRuleSet()->parse(workText, workPos, 10, temp); UErrorCode status = U_ZERO_ERROR; digit = temp.getLong(status); // digit = temp.getType() == Formattable::kLong ? // temp.getLong() : // (int32_t)temp.getDouble(); if (lenientParse && workPos.getIndex() == 0) { if (!fmt) { status = U_ZERO_ERROR; fmt = NumberFormat::createInstance(status); if (U_FAILURE(status)) { delete fmt; fmt = NULL; } } if (fmt) { fmt->parse(workText, temp, workPos); digit = temp.getLong(status); } } if (workPos.getIndex() != 0) { dl.append((char)('0' + digit)); // result += digit * p10; // p10 /= 10; parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex()); workText.removeBetween(0, workPos.getIndex()); while (workText.length() > 0 && workText.charAt(0) == gSpace) { workText.removeBetween(0, 1); parsePosition.setIndex(parsePosition.getIndex() + 1); } } } delete fmt; result = dl.fCount == 0 ? 0 : dl.getDouble(); result = composeRuleValue(result, baseValue); resVal.setDouble(result); return TRUE; } }
/** * 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()); } } }