int64_t util64_fromDouble(double d) { int64_t result = 0; if (!uprv_isNaN(d)) { double mant = uprv_maxMantissa(); if (d < -mant) { d = -mant; } else if (d > mant) { d = mant; } UBool neg = d < 0; if (neg) { d = -d; } result = (int64_t)uprv_floor(d); if (neg) { result = -result; } } return result; }
/** * 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]; }