const NFRule* NFRuleSet::findDoubleRule(double number) const { // if this is a fraction rule set, use findFractionRuleSetRule() if (isFractionRuleSet()) { return findFractionRuleSetRule(number); } if (uprv_isNaN(number)) { const NFRule *rule = nonNumericalRules[NAN_RULE_INDEX]; if (!rule) { rule = owner->getDefaultNaNRule(); } return rule; } // if the number is negative, return the negative number rule // (if there isn't a negative-number rule, we pretend it's a // positive number) if (number < 0) { if (nonNumericalRules[NEGATIVE_RULE_INDEX]) { return nonNumericalRules[NEGATIVE_RULE_INDEX]; } else { number = -number; } } if (uprv_isInfinite(number)) { const NFRule *rule = nonNumericalRules[INFINITY_RULE_INDEX]; if (!rule) { rule = owner->getDefaultInfinityRule(); } return rule; } // if the number isn't an integer, we use one of the fraction rules... if (number != uprv_floor(number)) { // if the number is between 0 and 1, return the proper // fraction rule if (number < 1 && nonNumericalRules[PROPER_FRACTION_RULE_INDEX]) { return nonNumericalRules[PROPER_FRACTION_RULE_INDEX]; } // otherwise, return the improper fraction rule else if (nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]) { return nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]; } } // if there's a master rule, use it to format the number if (nonNumericalRules[MASTER_RULE_INDEX]) { return nonNumericalRules[MASTER_RULE_INDEX]; } // and if we haven't yet returned a rule, use findNormalRule() // to find the applicable rule int64_t r = util64_fromDouble(number + 0.5); return findNormalRule(r); }
NumeratorSubstitution(int32_t _pos, double _denominator, const NFRuleSet* _ruleSet, const RuleBasedNumberFormat* formatter, const UnicodeString& description, UErrorCode& status) : NFSubstitution(_pos, _ruleSet, formatter, fixdesc(description), status), denominator(_denominator) { ldenominator = util64_fromDouble(denominator); withZeros = description.endsWith(LTLT, 2); }
MultiplierSubstitution(int32_t _pos, double _divisor, const NFRuleSet* _ruleSet, const RuleBasedNumberFormat* formatter, const UnicodeString& description, UErrorCode& status) : NFSubstitution(_pos, _ruleSet, formatter, description, status), divisor(_divisor) { ldivisor = util64_fromDouble(divisor); if (divisor == 0) { status = U_PARSE_ERROR; } }
NFRule * NFRuleSet::findDoubleRule(double number) const { // if this is a fraction rule set, use findFractionRuleSetRule() if (isFractionRuleSet()) { return findFractionRuleSetRule(number); } // if the number is negative, return the negative number rule // (if there isn't a negative-number rule, we pretend it's a // positive number) if (number < 0) { if (negativeNumberRule) { return negativeNumberRule; } else { number = -number; } } // if the number isn't an integer, we use one of the fraction rules... if (number != uprv_floor(number)) { // if the number is between 0 and 1, return the proper // fraction rule if (number < 1 && fractionRules[1]) { return fractionRules[1]; } // otherwise, return the improper fraction rule else if (fractionRules[0]) { return fractionRules[0]; } } // if there's a master rule, use it to format the number if (fractionRules[2]) { return fractionRules[2]; } // and if we haven't yet returned a rule, use findNormalRule() // to find the applicable rule int64_t r = util64_fromDouble(number + 0.5); return findNormalRule(r); }
void NumeratorSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t apos) const { // perform a transformation on the number being formatted that // is dependent on the type of substitution this is double numberToFormat = transformNumber(number); int64_t longNF = util64_fromDouble(numberToFormat); const NFRuleSet* aruleSet = getRuleSet(); if (withZeros && aruleSet != NULL) { // if there are leading zeros in the decimal expansion then emit them int64_t nf =longNF; int32_t len = toInsertInto.length(); while ((nf *= 10) < denominator) { toInsertInto.insert(apos + getPos(), gSpace); aruleSet->format((int64_t)0, toInsertInto, apos + getPos()); } apos += toInsertInto.length() - len; } // if the result is an integer, from here on out we work in integer // space (saving time and memory and preserving accuracy) if (numberToFormat == longNF && aruleSet != NULL) { aruleSet->format(longNF, toInsertInto, apos + getPos()); // if the result isn't an integer, then call either our rule set's // format() method or our DecimalFormat's format() method to // format the result } else { if (aruleSet != NULL) { aruleSet->format(numberToFormat, toInsertInto, apos + getPos()); } else { UErrorCode status = U_ZERO_ERROR; UnicodeString temp; getNumberFormat()->format(numberToFormat, temp, status); toInsertInto.insert(apos + getPos(), temp); } } }
/** * Performs a mathematical operation on the number, formats it using * either ruleSet or decimalFormat, and inserts the result into * toInsertInto. * @param number The number being formatted. * @param toInsertInto The string we insert the result into * @param pos The position in toInsertInto where the owning rule's * rule text begins (this value is added to this substitution's * position to determine exactly where to insert the new text) */ void NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32_t _pos) const { // perform a transformation on the number being formatted that // is dependent on the type of substitution this is double numberToFormat = transformNumber(number); // if the result is an integer, from here on out we work in integer // space (saving time and memory and preserving accuracy) if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) { ruleSet->format(util64_fromDouble(numberToFormat), toInsertInto, _pos + this->pos); // if the result isn't an integer, then call either our rule set's // format() method or our DecimalFormat's format() method to // format the result } else { if (ruleSet != NULL) { ruleSet->format(numberToFormat, toInsertInto, _pos + this->pos); } else if (numberFormat != NULL) { UnicodeString temp; numberFormat->format(numberToFormat, temp); toInsertInto.insert(_pos + this->pos, temp); } } }
UBool NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, Formattable& result) const { // try matching each rule in the rule set against the text being // parsed. Whichever one matches the most characters is the one // that determines the value we return. result.setLong(0); // dump out if there's no text to parse if (text.length() == 0) { return 0; } ParsePosition highWaterMark; ParsePosition workingPos = pos; #ifdef RBNF_DEBUG fprintf(stderr, "<nfrs> %x '", this); dumpUS(stderr, name); fprintf(stderr, "' text '"); dumpUS(stderr, text); fprintf(stderr, "'\n"); fprintf(stderr, " parse negative: %d\n", this, negativeNumberRule != 0); #endif // start by trying the negative number rule (if there is one) if (negativeNumberRule) { Formattable tempResult; #ifdef RBNF_DEBUG fprintf(stderr, " <nfrs before negative> %x ub: %g\n", negativeNumberRule, upperBound); #endif UBool success = negativeNumberRule->doParse(text, workingPos, 0, upperBound, tempResult); #ifdef RBNF_DEBUG fprintf(stderr, " <nfrs after negative> success: %d wpi: %d\n", success, workingPos.getIndex()); #endif if (success && workingPos.getIndex() > highWaterMark.getIndex()) { result = tempResult; highWaterMark = workingPos; } workingPos = pos; } #ifdef RBNF_DEBUG fprintf(stderr, "<nfrs> continue fractional with text '"); dumpUS(stderr, text); fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex()); #endif // then try each of the fraction rules { for (int i = 0; i < 3; i++) { if (fractionRules[i]) { Formattable tempResult; UBool success = fractionRules[i]->doParse(text, workingPos, 0, upperBound, tempResult); if (success && (workingPos.getIndex() > highWaterMark.getIndex())) { result = tempResult; highWaterMark = workingPos; } workingPos = pos; } } } #ifdef RBNF_DEBUG fprintf(stderr, "<nfrs> continue other with text '"); dumpUS(stderr, text); fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex()); #endif // finally, go through the regular rules one at a time. We start // at the end of the list because we want to try matching the most // sigificant rule first (this helps ensure that we parse // "five thousand three hundred six" as // "(five thousand) (three hundred) (six)" rather than // "((five thousand three) hundred) (six)"). Skip rules whose // base values are higher than the upper bound (again, this helps // limit ambiguity by making sure the rules that match a rule's // are less significant than the rule containing the substitutions)/ { int64_t ub = util64_fromDouble(upperBound); #ifdef RBNF_DEBUG { char ubstr[64]; util64_toa(ub, ubstr, 64); char ubstrhex[64]; util64_toa(ub, ubstrhex, 64, 16); fprintf(stderr, "ub: %g, i64: %s (%s)\n", upperBound, ubstr, ubstrhex); } #endif for (int32_t i = rules.size(); --i >= 0 && highWaterMark.getIndex() < text.length();) { if ((!fIsFractionRuleSet) && (rules[i]->getBaseValue() >= ub)) { continue; } Formattable tempResult; UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, tempResult); if (success && workingPos.getIndex() > highWaterMark.getIndex()) { result = tempResult; highWaterMark = workingPos; } workingPos = pos; } } #ifdef RBNF_DEBUG fprintf(stderr, "<nfrs> exit\n"); #endif // finally, update the parse postion we were passed to point to the // first character we didn't use, and return the result that // corresponds to that string of characters pos = highWaterMark; return 1; }
/** * If this rule is a fraction rule set, this function is used by * findRule() to select the most appropriate rule for formatting * the number. Basically, the base value of each rule in the rule * set is treated as the denominator of a fraction. Whichever * denominator can produce the fraction closest in value to the * number passed in is the result. If there's a tie, the earlier * one in the list wins. (If there are two rules in a row with the * same base value, the first one is used when the numerator of the * fraction would be 1, and the second rule is used the rest of the * time. * @param number The number being formatted (which will always be * a number between 0 and 1) * @return The rule to use to format this number */ NFRule* NFRuleSet::findFractionRuleSetRule(double number) const { // the obvious way to do this (multiply the value being formatted // by each rule's base value until you get an integral result) // doesn't work because of rounding error. This method is more // accurate // find the least common multiple of the rules' base values // and multiply this by the number being formatted. This is // all the precision we need, and we can do all of the rest // of the math using integer arithmetic int64_t leastCommonMultiple = rules[0]->getBaseValue(); int64_t numerator; { for (uint32_t i = 1; i < rules.size(); ++i) { leastCommonMultiple = util_lcm(leastCommonMultiple, rules[i]->getBaseValue()); } numerator = util64_fromDouble(number * (double)leastCommonMultiple + 0.5); } // for each rule, do the following... int64_t tempDifference; int64_t difference = util64_fromDouble(uprv_maxMantissa()); int32_t winner = 0; for (uint32_t i = 0; i < rules.size(); ++i) { // "numerator" is the numerator of the fraction if the // denominator is the LCD. The numerator if the rule's // base value is the denominator is "numerator" times the // base value divided bythe LCD. Here we check to see if // that's an integer, and if not, how close it is to being // an integer. tempDifference = numerator * rules[i]->getBaseValue() % leastCommonMultiple; // normalize the result of the above calculation: we want // the numerator's distance from the CLOSEST multiple // of the LCD if (leastCommonMultiple - tempDifference < tempDifference) { tempDifference = leastCommonMultiple - tempDifference; } // if this is as close as we've come, keep track of how close // that is, and the line number of the rule that did it. If // we've scored a direct hit, we don't have to look at any more // rules if (tempDifference < difference) { difference = tempDifference; winner = i; if (difference == 0) { break; } } } // if we have two successive rules that both have the winning base // value, then the first one (the one we found above) is used if // the numerator of the fraction is 1 and the second one is used if // the numerator of the fraction is anything else (this lets us // do things like "one third"/"two thirds" without haveing to define // a whole bunch of extra rule sets) if ((unsigned)(winner + 1) < rules.size() && rules[winner + 1]->getBaseValue() == rules[winner]->getBaseValue()) { double n = ((double)rules[winner]->getBaseValue()) * number; if (n < 0.5 || n >= 2) { ++winner; } } // finally, return the winning rule return rules[winner]; }