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);
}
示例#7
0
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.");
}
示例#8
0
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)));
}
示例#14
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()));
}