Value GranularityRounderPreferredNumbers::roundDown(Value value) { uassertNonNegativeNumber(value); if (value.coerceToDouble() == 0.0) { return value; } if (value.getType() == BSONType::NumberDecimal) { Decimal128 number = value.getDecimal(); Decimal128 multiplier = Decimal128(1); // '_baseSeries' contains doubles, so we create a vector that contains the Decimal128 // versions of the numbers in '_baseSeries' to make it easier to compare values to 'number'. vector<Decimal128> decimalSeries; for (auto&& doubleNumber : _baseSeries) { decimalSeries.push_back(Decimal128(doubleNumber)); } while (number.isLessEqual(decimalSeries.front().multiply(multiplier))) { multiplier = multiplier.divide(Decimal128(10)); } Decimal128 previousMax; while (number.isGreater(decimalSeries.back().multiply(multiplier))) { previousMax = decimalSeries.back().multiply(multiplier); multiplier = multiplier.multiply(Decimal128(10)); if (number.isLessEqual(decimalSeries.front().multiply(multiplier))) { // The number is less than or equal to the current min, so it must round down to the // previous max. For example, rounding down 0.8 in the E6 series. return Value(previousMax); } } // After scaling up or down, 'number' should now fall into the range spanned by // decimalSeries[i] * multiplier for all i in decimalSeries. invariant(number.isGreater(decimalSeries.front().multiply(multiplier)) && number.isLessEqual(decimalSeries.back().multiply(multiplier))); // Get an iterator pointing to the first element in '_baseSeries' that is greater than or // equal to 'number'. auto iterator = std::lower_bound(decimalSeries.begin(), decimalSeries.end(), number, [multiplier](Decimal128 seriesNumber, Decimal128 roundingNumber) { return seriesNumber.multiply(multiplier).isLess(roundingNumber); }); // We need to move the iterator back by one so that we round down to a number that is // strictly less than the value we are rounding. return Value(Value((*(iterator - 1)).multiply(multiplier))); } else { double number = value.coerceToDouble(); double multiplier = 1.0; while (number <= (_baseSeries.front() * multiplier)) { multiplier /= 10.0; } double previousMax; while (number > (_baseSeries.back() * multiplier)) { previousMax = _baseSeries.back() * multiplier; multiplier *= 10.0; if (number <= _baseSeries.front() * multiplier) { // The number is less than or equal to the current min, so it must round down to the // previous max. For example, rounding down 0.8 in the E6 series. return Value(previousMax); } } // After scaling up or down, 'number' should now fall into the range spanned by // _baseSeries[i] * multiplier for all i in _baseSeries. invariant(number > (_baseSeries.front() * multiplier) && number <= (_baseSeries.back() * multiplier)); // Get an iterator pointing to the first element in '_baseSeries' that is greater than or // equal to 'number'. auto iterator = std::lower_bound(_baseSeries.begin(), _baseSeries.end(), number, [multiplier](double seriesNumber, double roundingNumber) { return (seriesNumber * multiplier) < roundingNumber; }); // We need to move the iterator back by one so that we round down to a number that is // strictly less than the value we are rounding. return Value(Value(*(iterator - 1) * multiplier)); } }
Value GranularityRounderPreferredNumbers::roundUp(Value value) { uassertNonNegativeNumber(value); if (value.coerceToDouble() == 0.0) { return value; } if (value.getType() == BSONType::NumberDecimal) { Decimal128 number = value.getDecimal(); Decimal128 multiplier = Decimal128(1); // '_baseSeries' contains doubles, so we create a vector that contains the Decimal128 // versions of the numbers in '_baseSeries' to make it easier to compare values to 'number'. vector<Decimal128> decimalSeries; for (auto&& doubleNumber : _baseSeries) { decimalSeries.push_back(Decimal128(doubleNumber)); } while (number.isGreaterEqual(decimalSeries.back().multiply(multiplier))) { multiplier = multiplier.multiply(Decimal128(10)); } Decimal128 previousMin; while (number.isLess(decimalSeries.front().multiply(multiplier))) { previousMin = decimalSeries.front().multiply(multiplier); multiplier = multiplier.divide(Decimal128(10)); if (number.isGreaterEqual(decimalSeries.back().multiply(multiplier))) { // The number was between the previous min and the current max, so it must round up // to the previous min. For example, rounding up 0.8 in the E6 series. return Value(previousMin); } } // After scaling up or down, 'number' should now fall into the range spanned by // decimalSeries[i] * multiplier for all i in decimalSeries. invariant(number.isGreaterEqual(decimalSeries.front().multiply(multiplier)) && number.isLess(decimalSeries.back().multiply(multiplier))); // Get an iterator pointing to the first element in '_baseSeries' that is greater // than'number'. auto iterator = std::upper_bound(decimalSeries.begin(), decimalSeries.end(), number, [multiplier](Decimal128 roundingNumber, Decimal128 seriesNumber) { return roundingNumber.isLess(seriesNumber.multiply(multiplier)); }); return Value((*iterator).multiply(multiplier)); } else { double number = value.coerceToDouble(); double multiplier = 1.0; while (number >= (_baseSeries.back() * multiplier)) { multiplier *= 10.0; } double previousMin; while (number < (_baseSeries.front() * multiplier)) { previousMin = _baseSeries.front() * multiplier; multiplier /= 10.0; if (number >= (_baseSeries.back() * multiplier)) { // The number was between the previous min and the current max, so it must round up // to the previous min. For example, rounding up 0.8 in the E6 series. return Value(previousMin); } } // After scaling up or down, 'number' should now fall into the range spanned by // _baseSeries[i] * multiplier for all i in _baseSeries. invariant(number >= (_baseSeries.front() * multiplier) && number < (_baseSeries.back() * multiplier)); // Get an iterator pointing to the first element in '_baseSeries' that is greater // than'number'. auto iterator = std::upper_bound(_baseSeries.begin(), _baseSeries.end(), number, [multiplier](double roundingNumber, double seriesNumber) { return roundingNumber < (seriesNumber * multiplier); }); return Value(*iterator * multiplier); } }