Decimal128 Decimal128::logarithm(const Decimal128& other, RoundingMode roundMode) const { std::uint32_t throwAwayFlag = 0; if (other.isEqual(Decimal128(2))) { BID_UINT128 current = decimal128ToLibraryType(_value); current = bid128_log2(current, roundMode, &throwAwayFlag); return Decimal128{libraryTypeToValue(current)}; } if (other.isEqual(Decimal128(10))) { BID_UINT128 current = decimal128ToLibraryType(_value); current = bid128_log10(current, roundMode, &throwAwayFlag); return Decimal128{libraryTypeToValue(current)}; } return logarithm(other, &throwAwayFlag); }
Decimal128 Decimal128::power(const Decimal128& other, std::uint32_t* signalingFlags, RoundingMode roundMode) const { BID_UINT128 base = decimal128ToLibraryType(_value); BID_UINT128 exp = decimal128ToLibraryType(other.getValue()); BID_UINT128 result; if (this->isEqual(Decimal128(10))) result = bid128_exp10(exp, roundMode, signalingFlags); else if (this->isEqual(Decimal128(2))) result = bid128_exp2(exp, roundMode, signalingFlags); else result = bid128_pow(base, exp, roundMode, signalingFlags); return Decimal128{libraryTypeToValue(result)}.add(kLargestNegativeExponentZero); }
TEST(Decimal128Test, TestDecimal128SubtractSignaling) { Decimal128 d = Decimal128::kLargestNegative; uint32_t sigFlags = Decimal128::SignalingFlag::kNoFlag; Decimal128 res = d.subtract(Decimal128(1), &sigFlags); ASSERT_TRUE(res.isEqual(Decimal128::kLargestNegative)); ASSERT_TRUE(Decimal128::hasFlag(sigFlags, Decimal128::SignalingFlag::kInexact)); }
TEST(Decimal128Test, TestDecimal128DivideSignaling) { Decimal128 d("2"); uint32_t sigFlags = Decimal128::SignalingFlag::kNoFlag; Decimal128 res = d.divide(Decimal128(0), &sigFlags); ASSERT_TRUE(res.isEqual(Decimal128::kPositiveInfinity)); ASSERT_TRUE(Decimal128::hasFlag(sigFlags, Decimal128::SignalingFlag::kDivideByZero)); }
TEST(ExclusionProjectionExecutionTest, ShouldCoerceNumericsToBools) { ParsedExclusionProjection exclusion; exclusion.parse(BSON("a" << Value(0) << "b" << Value(0LL) << "c" << Value(0.0) << "d" << Value(Decimal128(0)))); auto result = exclusion.applyProjection(Document{{"_id", "ID"}, {"a", 1}, {"b", 2}, {"c", 3}}); auto expectedResult = Document{{"_id", "ID"}}; ASSERT_EQ(result, expectedResult); }
/** * Quantize a doubleValue argument to a Decimal128 with exactly 15 digits * of precision. * * To highlight the motivation for this function, consider doubleValue = 0.1. * The quantity 0.1 does not have an exact respresentation as a double. * The actual value stored in the 64-bit type is 0.1000000000000000055511... * * Although imprecise, the double type does guarantee a minimum of 15 digits * of decimal precision. When casting the double to a decimal type, we choose * to only appreciate the double's first 15 digits and round accordingly. * * To perform this operation, doubleValue is converted to a decimal and then quantized * with the appropriate quantum (Q) to yield exactly 15 digits of precision. * For example, * doubleValue = 0.1 * dec128 = Decimal128(doubleValue) <== 0.1000000000000000055511151231257827 * Q = 1E-15 * dec128.quantize(Q) * ==> 0.100000000000000 * * The value to quantize dec128 on (Q) is related to the base 10 exponent of the rounded * doubleValue, * Q = 10 ** (floor(log10(doubleValue rounded to 15 decimal digits)) - 14) * * * =============================================================================== * * Convert a double's base 2 exponent to base 10 using integer arithmetic. * * Given doubleValue with exponent base2Exp, we would like to find base10Exp such that: * (1) 10**base10Exp > |doubleValue rounded to 15 decimal digits| * (2) 10**(base10Exp-1) <= |doubleValue rounded to 15 decimal digits| * * Given a double precision number of the form 2**E, we can compute base10Exp such that these * conditions hold for 2**E. However, because the absolute value of doubleValue maybe up to a * factor of two higher, the required base10Exp may be 1 higher. Exactly knowing in which case we * are would require knowing how the double value will round, so just try with the lowest * possible base10Exp, and retry if we need to increase the exponent by 1. It is important to first * try the lower exponent, as the other way around might unnecessarily lose a significant digit, * as in 0.9999999999999994 (15 nines) -> 1.00000000000000 (14 zeros) instead of 0.999999999999999 * (15 nines). * * +-------------+-------------------+----------------------+---------------------------+ * | doubleValue | base2Exp | computed base10Exp | Q | * +-------------+-------------------+----------------------+---------------------------+ * | 100000 | 16 | 4 | 10**(5 - 14) <= Retry | * | 500000 | 18 | 5 | 10**(5 - 14) | * | 999999 | 19 | 5 | 10**(5 - 14) | * | .00001 | -17 | -6 | 10**(5 - 14) <= Retry | * | .00005 | -15 | -5 | 10**(5 - 14) | * | .00009 | -14 | -5 | 10**(5 - 14) | * +-------------+-------------------+----------------------+---------------------------+ */ Decimal128::Decimal128(double doubleValue, RoundingPrecision roundPrecision, RoundingMode roundMode) { std::uint32_t throwAwayFlag = 0; Decimal128 convertedDoubleValue( libraryTypeToValue(binary64_to_bid128(doubleValue, roundMode, &throwAwayFlag))); // If the original number was zero, infinity, or NaN, there's no need to quantize if (doubleValue == 0.0 || std::isinf(doubleValue) || std::isnan(doubleValue) || roundPrecision == kRoundTo34Digits) { *this = convertedDoubleValue; return; } // Get the base2 exponent from doubleValue. int base2Exp; frexp(doubleValue, &base2Exp); // As frexp normalizes doubleValue between 0.5 and 1.0 rather than 1.0 and 2.0, adjust. base2Exp--; // We will use base10Exp = base2Exp * 30103 / (100*1000) as lowerbound (using integer division). // // This formula is derived from the following, with base2Exp the binary exponent of doubleValue: // (1) 10**(base2Exp * log10(2)) == 2**base2Exp // (2) 0.30103 closely approximates log10(2) // // Exhaustive testing using Python shows : // { base2Exp * 30103 / (100 * 1000) == math.floor(math.log10(2**base2Exp)) // for base2Exp in xrange(-1074, 1023) } == { True } int base10Exp = (base2Exp * 30103) / (100 * 1000); // As integer division truncates, rather than rounds down (as in Python), adjust accordingly. if (base2Exp < 0) base10Exp--; Decimal128 Q(0, base10Exp - 14 + Decimal128::kExponentBias, 0, 1); *this = convertedDoubleValue.quantize(Q, roundMode); // Check if the quantization was done correctly: _value stores exactly 15 // decimal digits of precision (15 digits can fit into the low 64 bits of the decimal) uint64_t kSmallest15DigitInt = 1E14; // A 1 with 14 zeros uint64_t kLargest15DigitInt = 1E15 - 1; // 15 nines if (getCoefficientLow() > kLargest15DigitInt) { // If we didn't precisely get 15 digits of precision, the original base 10 exponent // guess was 1 off, so quantize once more with base10Exp + 1 Q = Decimal128(0, base10Exp - 13 + Decimal128::kExponentBias, 0, 1); *this = convertedDoubleValue.quantize(Q, roundMode); } // The decimal must have exactly 15 digits of precision invariant(getCoefficientHigh() == 0); invariant(getCoefficientLow() >= kSmallest15DigitInt); invariant(getCoefficientLow() <= kLargest15DigitInt); }
Decimal128 ValueWriter::toDecimal128() { if (_value.isNumber()) { return Decimal128(toNumber()); } if (getScope(_context)->getProto<NumberIntInfo>().instanceOf(_value)) return Decimal128(NumberIntInfo::ToNumberInt(_context, _value)); if (getScope(_context)->getProto<NumberLongInfo>().instanceOf(_value)) return Decimal128(static_cast<int64_t>(NumberLongInfo::ToNumberLong(_context, _value))); if (getScope(_context)->getProto<NumberDecimalInfo>().instanceOf(_value)) return NumberDecimalInfo::ToNumberDecimal(_context, _value); if (_value.isString()) { return Decimal128(toString()); } uasserted(ErrorCodes::BadValue, str::stream() << "Unable to write Decimal128 value."); }
namespace mongo { Decimal128::Decimal128(int32_t int32Value) { MONGO_UNREACHABLE; } Decimal128::Decimal128(int64_t int64Value) { MONGO_UNREACHABLE; } Decimal128::Decimal128(double doubleValue, RoundingPrecision roundPrecision, RoundingMode roundMode) { MONGO_UNREACHABLE; } Decimal128::Decimal128(std::string stringValue, RoundingMode roundMode) { MONGO_UNREACHABLE; } Decimal128::Value Decimal128::getValue() const { MONGO_UNREACHABLE; } Decimal128 Decimal128::toAbs() const { MONGO_UNREACHABLE; } int32_t Decimal128::toInt(RoundingMode roundMode) const { MONGO_UNREACHABLE; } int32_t Decimal128::toInt(uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } int64_t Decimal128::toLong(RoundingMode roundMode) const { MONGO_UNREACHABLE; } int64_t Decimal128::toLong(uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } int32_t Decimal128::toIntExact(RoundingMode roundMode) const { MONGO_UNREACHABLE; } int32_t Decimal128::toIntExact(uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } int64_t Decimal128::toLongExact(RoundingMode roundMode) const { MONGO_UNREACHABLE; } int64_t Decimal128::toLongExact(uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } double Decimal128::toDouble(RoundingMode roundMode) const { MONGO_UNREACHABLE; } double Decimal128::toDouble(uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } std::string Decimal128::toString() const { MONGO_UNREACHABLE; } bool Decimal128::isZero() const { MONGO_UNREACHABLE; } bool Decimal128::isNaN() const { MONGO_UNREACHABLE; } bool Decimal128::isInfinite() const { MONGO_UNREACHABLE; } bool Decimal128::isNegative() const { MONGO_UNREACHABLE; } Decimal128 Decimal128::add(const Decimal128& other, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::add(const Decimal128& other, uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::subtract(const Decimal128& other, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::subtract(const Decimal128& other, uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::multiply(const Decimal128& other, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::multiply(const Decimal128& other, uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::divide(const Decimal128& other, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::divide(const Decimal128& other, uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::quantize(const Decimal128& other, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::quantize(const Decimal128& reference, uint32_t* signalingFlags, RoundingMode roundMode) const { MONGO_UNREACHABLE; } Decimal128 Decimal128::normalize() const { MONGO_UNREACHABLE; } bool Decimal128::isEqual(const Decimal128& other) const { MONGO_UNREACHABLE; } bool Decimal128::isNotEqual(const Decimal128& other) const { MONGO_UNREACHABLE; } bool Decimal128::isGreater(const Decimal128& other) const { MONGO_UNREACHABLE; } bool Decimal128::isGreaterEqual(const Decimal128& other) const { MONGO_UNREACHABLE; } bool Decimal128::isLess(const Decimal128& other) const { MONGO_UNREACHABLE; } bool Decimal128::isLessEqual(const Decimal128& other) const { MONGO_UNREACHABLE; } const Decimal128 Decimal128::kLargestPositive = Decimal128(); const Decimal128 Decimal128::kSmallestPositive = Decimal128(); const Decimal128 Decimal128::kLargestNegative = Decimal128(); const Decimal128 Decimal128::kSmallestNegative = Decimal128(); const Decimal128 Decimal128::kLargestNegativeExponentZero = Decimal128(); const Decimal128 Decimal128::kPositiveInfinity = Decimal128(); const Decimal128 Decimal128::kNegativeInfinity = Decimal128(); const Decimal128 Decimal128::kPositiveNaN = Decimal128(); const Decimal128 Decimal128::kNegativeNaN = Decimal128(); const Decimal128 Decimal128::kNormalizedZero = {}; } // namespace mongo
void BSONComparatorInterfaceBase<T>::hashCombineBSONElement( size_t& hash, BSONElement elemToHash, bool considerFieldName, const StringData::ComparatorInterface* stringComparator) { boost::hash_combine(hash, elemToHash.canonicalType()); const StringData fieldName = elemToHash.fieldNameStringData(); if (considerFieldName && !fieldName.empty()) { SimpleStringDataComparator::kInstance.hash_combine(hash, fieldName); } switch (elemToHash.type()) { // Order of types is the same as in compareElementValues(). case mongo::EOO: case mongo::Undefined: case mongo::jstNULL: case mongo::MaxKey: case mongo::MinKey: // These are valueless types break; case mongo::Bool: boost::hash_combine(hash, elemToHash.boolean()); break; case mongo::bsonTimestamp: boost::hash_combine(hash, elemToHash.timestamp().asULL()); break; case mongo::Date: boost::hash_combine(hash, elemToHash.date().asInt64()); break; case mongo::NumberDecimal: { const Decimal128 dcml = elemToHash.numberDecimal(); if (dcml.toAbs().isGreater(Decimal128(std::numeric_limits<double>::max(), Decimal128::kRoundTo34Digits, Decimal128::kRoundTowardZero)) && !dcml.isInfinite() && !dcml.isNaN()) { // Normalize our decimal to force equivalent decimals // in the same cohort to hash to the same value Decimal128 dcmlNorm(dcml.normalize()); boost::hash_combine(hash, dcmlNorm.getValue().low64); boost::hash_combine(hash, dcmlNorm.getValue().high64); break; } // Else, fall through and convert the decimal to a double and hash. // At this point the decimal fits into the range of doubles, is infinity, or is NaN, // which doubles have a cheaper representation for. } case mongo::NumberDouble: case mongo::NumberLong: case mongo::NumberInt: { // This converts all numbers to doubles, which ignores the low-order bits of // NumberLongs > 2**53 and precise decimal numbers without double representations, // but that is ok since the hash will still be the same for equal numbers and is // still likely to be different for different numbers. (Note: this issue only // applies for decimals when they are outside of the valid double range. See // the above case.) // SERVER-16851 const double dbl = elemToHash.numberDouble(); if (std::isnan(dbl)) { boost::hash_combine(hash, std::numeric_limits<double>::quiet_NaN()); } else { boost::hash_combine(hash, dbl); } break; } case mongo::jstOID: elemToHash.__oid().hash_combine(hash); break; case mongo::String: { if (stringComparator) { stringComparator->hash_combine(hash, elemToHash.valueStringData()); } else { SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.valueStringData()); } break; } case mongo::Code: case mongo::Symbol: SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.valueStringData()); break; case mongo::Object: case mongo::Array: hashCombineBSONObj(hash, elemToHash.embeddedObject(), true, // considerFieldName stringComparator); break; case mongo::DBRef: case mongo::BinData: // All bytes of the value are required to be identical. SimpleStringDataComparator::kInstance.hash_combine( hash, StringData(elemToHash.value(), elemToHash.valuesize())); break; case mongo::RegEx: SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.regex()); SimpleStringDataComparator::kInstance.hash_combine(hash, elemToHash.regexFlags()); break; case mongo::CodeWScope: { SimpleStringDataComparator::kInstance.hash_combine( hash, StringData(elemToHash.codeWScopeCode(), elemToHash.codeWScopeCodeLen())); hashCombineBSONObj(hash, elemToHash.codeWScopeObject(), true, // considerFieldName &SimpleStringDataComparator::kInstance); break; } } }
Decimal128 Decimal128::toAbs() const { BID_UINT128 dec128 = decimal128ToLibraryType(_value); dec128 = bid128_abs(dec128); return Decimal128(libraryTypeToValue(dec128)); }
Decimal128::Decimal128(std::string stringValue, RoundingMode roundMode) { std::uint32_t throwAwayFlag = 0; *this = Decimal128(stringValue, &throwAwayFlag, roundMode); }
TEST(Decimal128Test, TestAbsValueNeg) { Decimal128 d(-25); Decimal128 dAbs = d.toAbs(); ASSERT_TRUE(dAbs.isEqual(Decimal128(25))); }
TEST(Decimal128Test, TestDoubleConstructorZero) { double doubleZero = 0; Decimal128 d(doubleZero); ASSERT_TRUE(d.isEqual(Decimal128(0))); }
Decimal128 NumberDecimalInfo::ToNumberDecimal(JSContext* cx, JS::HandleObject thisv) { auto x = static_cast<Decimal128*>(JS_GetPrivate(thisv)); return x ? *x : Decimal128(0); }
TEST(MatchExpressionParserTest, ParseIntegerElementToLongRejectsLargestDecimal) { BSONObj query = BSON("" << Decimal128(Decimal128::kLargestPositive)); ASSERT_NOT_OK(MatchExpressionParser::parseIntegerElementToLong(query.firstElement())); }
TEST(MatchExpressionParserTest, ParseIntegerElementToLongRejectsNonIntegralDecimal) { BSONObj query = BSON("" << Decimal128("2.5")); ASSERT_NOT_OK(MatchExpressionParser::parseIntegerElementToLong(query.firstElement())); }