Beispiel #1
0
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;
     }
 }
Beispiel #4
0
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);
        }
    }
}
Beispiel #7
0
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;
}
Beispiel #8
0
/**
 * 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];
}