summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGeert Bosch <geert@mongodb.com>2016-03-31 15:06:35 -0400
committerGeert Bosch <geert@mongodb.com>2016-04-12 16:15:29 -0400
commitf924b3fac16ac35ad000be0c6a4f1e1cf9d2c85c (patch)
tree0569fff95357469c51b615dd2b696da0f1484a48 /src
parent09b99e5c6659e60c992288e6b391bd2cb9e409c9 (diff)
downloadmongo-f924b3fac16ac35ad000be0c6a4f1e1cf9d2c85c.tar.gz
SERVER-19703 Add a few new Decimal128 methods/constructors for usage by KeyString
Make conversions explicit and allow control over precision in conversion from double. Update uses to the new interface.
Diffstat (limited to 'src')
-rw-r--r--src/mongo/bson/bsonelement.cpp6
-rw-r--r--src/mongo/bson/bsonelement.h2
-rw-r--r--src/mongo/bson/mutable/document.cpp4
-rw-r--r--src/mongo/db/pipeline/value.cpp4
-rw-r--r--src/mongo/platform/decimal128.cpp49
-rw-r--r--src/mongo/platform/decimal128.h118
-rw-r--r--src/mongo/platform/decimal128_dummy.cpp6
-rw-r--r--src/mongo/platform/decimal128_test.cpp44
-rw-r--r--src/mongo/scripting/mozjs/valuewriter.cpp3
-rw-r--r--src/mongo/util/safe_num.cpp12
10 files changed, 196 insertions, 52 deletions
diff --git a/src/mongo/bson/bsonelement.cpp b/src/mongo/bson/bsonelement.cpp
index 64a2d35d8ed..7c934fb8c81 100644
--- a/src/mongo/bson/bsonelement.cpp
+++ b/src/mongo/bson/bsonelement.cpp
@@ -31,8 +31,8 @@
#include "mongo/bson/bsonelement.h"
-#include <cmath>
#include <boost/functional/hash.hpp>
+#include <cmath>
#include "mongo/base/compare_numbers.h"
#include "mongo/base/data_cursor.h"
@@ -1051,7 +1051,9 @@ size_t BSONElement::Hasher::operator()(const BSONElement& elem) const {
case mongo::NumberDecimal: {
const Decimal128 dcml = elem.numberDecimal();
- if (dcml.toAbs().isGreater(Decimal128(std::numeric_limits<double>::max())) &&
+ 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
diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h
index b0b2f05aa1c..5391e622b0e 100644
--- a/src/mongo/bson/bsonelement.h
+++ b/src/mongo/bson/bsonelement.h
@@ -711,7 +711,7 @@ inline Decimal128 BSONElement::numberDecimal() const {
case NumberDecimal:
return _numberDecimal();
default:
- return 0;
+ return Decimal128::kNormalizedZero;
}
}
diff --git a/src/mongo/bson/mutable/document.cpp b/src/mongo/bson/mutable/document.cpp
index 85cfa23ce53..612baf1a285 100644
--- a/src/mongo/bson/mutable/document.cpp
+++ b/src/mongo/bson/mutable/document.cpp
@@ -1900,7 +1900,7 @@ Status Element::setValueSafeNum(const SafeNum value) {
case mongo::NumberDouble:
return setValueDouble(value._value.doubleVal);
case mongo::NumberDecimal:
- return setValueDecimal(value._value.decimalVal);
+ return setValueDecimal(Decimal128(value._value.decimalVal));
default:
return Status(ErrorCodes::UnsupportedFormat,
"Don't know how to handle unexpected SafeNum type");
@@ -2564,7 +2564,7 @@ Element Document::makeElementSafeNum(StringData fieldName, SafeNum value) {
case mongo::NumberDouble:
return makeElementDouble(fieldName, value._value.doubleVal);
case mongo::NumberDecimal:
- return makeElementDecimal(fieldName, value._value.decimalVal);
+ return makeElementDecimal(fieldName, Decimal128(value._value.decimalVal));
default:
// Return an invalid element to indicate that we failed.
return end();
diff --git a/src/mongo/db/pipeline/value.cpp b/src/mongo/db/pipeline/value.cpp
index 704f922755b..cc35067b9da 100644
--- a/src/mongo/db/pipeline/value.cpp
+++ b/src/mongo/db/pipeline/value.cpp
@@ -822,7 +822,9 @@ void Value::hash_combine(size_t& seed) const {
case mongo::NumberDecimal: {
const Decimal128 dcml = getDecimal();
- if (dcml.toAbs().isGreater(Decimal128(std::numeric_limits<double>::max())) &&
+ 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
diff --git a/src/mongo/platform/decimal128.cpp b/src/mongo/platform/decimal128.cpp
index 8ec4cc924b1..ddaa381b367 100644
--- a/src/mongo/platform/decimal128.cpp
+++ b/src/mongo/platform/decimal128.cpp
@@ -30,10 +30,10 @@
#include <cmath>
#include <cstdlib>
+#include <iostream>
#include <memory>
#include <string>
#include <utility>
-#include <iostream>
// The Intel C library typedefs wchar_t, but it is a distinct fundamental type
// in C++, so we #define _WCHAR_T here to prevent the library from trying to typedef.
#define _WCHAR_T
@@ -202,13 +202,16 @@ Decimal128::Decimal128(std::int64_t int64Value)
* In the worst case, proven by the above error analysis, we only need to
* requantize once to yield exactly 15 decimal digits of precision.
*/
-Decimal128::Decimal128(double doubleValue, RoundingMode roundMode) {
+Decimal128::Decimal128(double doubleValue,
+ RoundingPrecision roundPrecision,
+ RoundingMode roundMode) {
BID_UINT128 convertedDoubleValue;
std::uint32_t throwAwayFlag = 0;
convertedDoubleValue = 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)) {
+ if (doubleValue == 0.0 || std::isinf(doubleValue) || std::isnan(doubleValue) ||
+ roundPrecision == kRoundTo34Digits) {
_value = libraryTypeToValue(convertedDoubleValue);
return;
}
@@ -629,37 +632,33 @@ const std::uint64_t t17hi32 = t17 >> 32;
// t17hi32*t17hi32 + 2*t17hi32*t17lo32 + t17lo32*t17lo32 where the 2nd term
// is shifted right by 32 and the 3rd term by 64 (which effectively drops the 3rd term)
const std::uint64_t t34hi64 = t17hi32 * t17hi32 + (((t17hi32 * t17lo32) >> 31));
-
-// Get the max exponent for a decimal128 (including the bias)
-const std::uint64_t maxBiasedExp = 6143 + 6144;
-// Get the binary representation of the negative sign bit
-const std::uint64_t negativeSignBit = 1ull << 63;
+static_assert(t34hi64 == 0x1ed09bead87c0, "");
+static_assert(t34lo64 == 0x378d8e63ffffffff, "");
} // namespace
-// The low bits of the largest positive number are all 9s (t34lo64) and
-// the highest are t32hi64 added to the max exponent shifted over 49.
-// The exponent is placed at 49 because 64 bits - 1 sign bit - 14 exponent bits = 49
-const Decimal128 Decimal128::kLargestPositive(Decimal128::Value({t34lo64,
- (maxBiasedExp << 49) + t34hi64}));
-// The smallest positive decimal is 1 with the largest negative exponent of 0 (biased -6176)
-const Decimal128 Decimal128::kSmallestPositive(Decimal128::Value({1ull, 0ull}));
+// (t34hi64 << 64) + t34lo64 == 1e34 - 1
+const Decimal128 Decimal128::kLargestPositive(0, Decimal128::kMaxBiasedExponent, t34hi64, t34lo64);
+// The smallest positive decimal is 1 with the largest negative exponent of 0 (biased)
+const Decimal128 Decimal128::kSmallestPositive(0, 0, 0, 1);
// Add a sign bit to the largest and smallest positive to get their corresponding negatives
-const Decimal128 Decimal128::kLargestNegative(
- Decimal128::Value({t34lo64, (maxBiasedExp << 49) + t34hi64 + negativeSignBit}));
-const Decimal128 Decimal128::kSmallestNegative(Decimal128::Value({1ull, 0ull + negativeSignBit}));
-// Get the reprsentation of 0 with the largest negative exponent
+const Decimal128 Decimal128::kLargestNegative(1, Decimal128::kMaxBiasedExponent, t34hi64, t34lo64);
+const Decimal128 Decimal128::kSmallestNegative(1, 0, 0, 1);
+
+// Get the representation of 0 (0E0).
+const Decimal128 Decimal128::kNormalizedZero(Decimal128::Value(
+ {0, static_cast<uint64_t>(Decimal128::kExponentBias) << Decimal128::kExponentFieldPos}));
+
+// Get the representation of 0 with the most negative exponent
const Decimal128 Decimal128::kLargestNegativeExponentZero(Decimal128::Value({0ull, 0ull}));
// Shift the format of the combination bits to the right position to get Inf and NaN
-// +Inf = 0111 1000 ... ... = 0x78 ... ...
-// +NaN = 0111 1100 ... ... = 0x7c ... ...
+// +Inf = 0111 1000 ... ... = 0x78 ... ..., -Inf = 1111 1000 ... ... = 0xf8 ... ...
+// +NaN = 0111 1100 ... ... = 0x7c ... ..., -NaN = 1111 1100 ... ... = 0xfc ... ...
const Decimal128 Decimal128::kPositiveInfinity(Decimal128::Value({0ull, 0x78ull << 56}));
-const Decimal128 Decimal128::kNegativeInfinity(
- Decimal128::Value({0ull, (0x78ull << 56) + negativeSignBit}));
+const Decimal128 Decimal128::kNegativeInfinity(Decimal128::Value({0ull, 0xf8ull << 56}));
const Decimal128 Decimal128::kPositiveNaN(Decimal128::Value({0ull, 0x7cull << 56}));
-const Decimal128 Decimal128::kNegativeNaN(Decimal128::Value({0ull,
- (0x7cull << 56) + negativeSignBit}));
+const Decimal128 Decimal128::kNegativeNaN(Decimal128::Value({0ull, 0xfcull << 56}));
std::ostream& operator<<(std::ostream& stream, const Decimal128& value) {
return stream << value.toString();
diff --git a/src/mongo/platform/decimal128.h b/src/mongo/platform/decimal128.h
index e9112190bbb..7e6d72a0459 100644
--- a/src/mongo/platform/decimal128.h
+++ b/src/mongo/platform/decimal128.h
@@ -35,6 +35,8 @@
#include "mongo/config.h"
+#include "mongo/util/assert_util.h"
+
namespace mongo {
/**
@@ -69,6 +71,7 @@ public:
static const Decimal128 kLargestNegative;
static const Decimal128 kSmallestNegative;
+ static const Decimal128 kNormalizedZero; // zero with exponent 0
static const Decimal128 kLargestNegativeExponentZero;
static const Decimal128 kPositiveInfinity;
@@ -76,6 +79,11 @@ public:
static const Decimal128 kPositiveNaN;
static const Decimal128 kNegativeNaN;
+ static const uint32_t kMaxBiasedExponent = 6143 + 6144;
+ // Biased exponent of a Decimal128 with least significant digit in the units place
+ static const int32_t kExponentBias = 6143 + 33;
+ static const uint32_t kInfinityExponent = kMaxBiasedExponent + 1; // internal convention only
+
/**
* This struct holds the raw data for IEEE 754-2008 data types
*/
@@ -93,6 +101,12 @@ public:
};
/**
+ * Indicates if constructing a Decimal128 from a double should round the double to 15 digits
+ * (so the conversion will correctly round-trip decimals), or round to the full 34 digits.
+ */
+ enum RoundingPrecision { kRoundTo15Digits = 0, kRoundTo34Digits = 1 };
+
+ /**
* The signaling flag enum determines the signaling nature of a decimal operation.
* The values of these flags are defined in the Intel RDFP math library.
*
@@ -118,28 +132,47 @@ public:
return ((signalingFlags & f) != 0u);
}
- Decimal128() = default;
+ /**
+ * Construct a 0E0 valued Decimal128.
+ */
+ Decimal128() : _value(kNormalizedZero._value) {}
/**
* This constructor takes in a raw decimal128 type, which consists of two
* uint64_t's. This class performs an endian check on the system to ensure
* that the Value.high64 represents the higher 64 bits.
*/
- Decimal128(Decimal128::Value dec128Value) : _value(dec128Value) {}
+ explicit Decimal128(Decimal128::Value dec128Value) : _value(dec128Value) {}
- Decimal128(std::int32_t int32Value);
- Decimal128(std::int64_t int64Value);
+ /**
+ * Constructs a Decimal128 from parts, dealing with proper encoding of the combination field.
+ * Assumes that the value will be inside the valid range of finite values. (No NaN/Inf, etc.)
+ */
+ Decimal128(uint64_t sign, uint64_t exponent, uint64_t coefficientHigh, uint64_t coefficientLow)
+ : _value(
+ Value{coefficientLow,
+ (sign << kSignFieldPos) | (exponent << kExponentFieldPos) | coefficientHigh}) {
+ dassert(coefficientHigh < 0x1ed09bead87c0 ||
+ (coefficientHigh == 0x1ed09bead87c0 && coefficientLow == 0x378d8e63ffffffff));
+ dassert(exponent == getBiasedExponent());
+ }
+
+ explicit Decimal128(std::int32_t int32Value);
+ explicit Decimal128(std::int64_t int64Value);
/**
- * This constructor takes a double and constructs a Decimal128 object
- * given a roundMode with a fixed precision of 15. Doubles can only
- * properly represent a decimal precision of 15-17 digits.
+ * This constructor takes a double and constructs a Decimal128 object given a roundMode, either
+ * to full precision, or with a fixed precision of 15 decimal digits. When a double is used to
+ * store a decimal floating point number, it is only correct up to 15 digits after converting
+ * back to decimal, so the 15 digit rounding is used for mixed-mode operations.
* The general idea is to quantize the direct double->dec128 conversion
* with a quantum of 1E(-15 +/- base10 exponent equivalent of the double).
* To do this, we find the smallest (abs value) base 10 exponent greater
* than the double's base 2 exp and shift the quantizer's exp accordingly.
*/
- Decimal128(double doubleValue, RoundingMode roundMode = kRoundTiesToEven);
+ explicit Decimal128(double doubleValue,
+ RoundingPrecision roundPrecision = kRoundTo15Digits,
+ RoundingMode roundMode = kRoundTiesToEven);
/**
* This constructor takes a string and constructs a Decimal128 object from it.
@@ -152,7 +185,7 @@ public:
* "200E9999999999" --> +Inf
* "-200E9999999999" --> -Inf
*/
- Decimal128(std::string stringValue, RoundingMode roundMode = kRoundTiesToEven);
+ explicit Decimal128(std::string stringValue, RoundingMode roundMode = kRoundTiesToEven);
/**
* This function gets the inner Value struct storing a Decimal128 value.
@@ -160,11 +193,51 @@ public:
Value getValue() const;
/**
- * This function returns the decimal absolute value of the caller.
+ * Extracts the biased exponent from the combination field.
+ */
+ uint32_t getBiasedExponent() const {
+ const uint64_t combo = _getCombinationField();
+ if (combo < kCombinationNonCanonical)
+ return combo >> 3;
+
+ return combo >= kCombinationInfinity
+ ? kMaxBiasedExponent + 1 // NaN or Inf
+ : (combo >> 1) & ((1 << 14) - 1); // non-canonical representation
+ }
+
+ /**
+ * Returns the high 49 bits of the 113-bit binary encoded coefficient. Returns 0 for
+ * non-canonical or non-finite numbers.
+ */
+ uint64_t getCoefficientHigh() const {
+ return _getCombinationField() < kCombinationNonCanonical
+ ? _value.high64 & kCanonicalCoefficientHighFieldMask
+ : 0;
+ }
+
+ /**
+ * Returns the low 64 bits of the 113-bit binary encoded coefficient. Returns 0 for
+ * non-canonical or non-finite numbers.
+ */
+ uint64_t getCoefficientLow() const {
+ return _getCombinationField() < kCombinationNonCanonical ? _value.low64 : 0;
+ }
+
+ /**
+ * Returns the absolute value of this.
*/
Decimal128 toAbs() const;
/**
+ * Returns `this` with inverted sign bit
+ */
+ Decimal128 negate() const {
+ Value negated = {_value.low64, _value.high64 ^ (1ULL << 63)};
+ return Decimal128(negated);
+ }
+
+
+ /**
* This set of functions converts a Decimal128 to a certain integer type with a
* given rounding mode.
*
@@ -302,8 +375,31 @@ public:
bool isLess(const Decimal128& other) const;
bool isLessEqual(const Decimal128& other) const;
+ /**
+ * Returns true iff 'this' and 'other' are bitwise identical. Note that this returns false
+ * even for values that may convert to identical strings, such as different NaNs or
+ * non-canonical representations that represent bit-patterns never generated by any conforming
+ * implementation, but should be treated as 0. Mostly for testing.
+ */
+ bool isBinaryEqual(const Decimal128& other) const {
+ return _value.high64 == other._value.high64 && _value.low64 == other._value.low64;
+ }
+
private:
+ static const uint8_t kSignFieldPos = 64 - 1;
+ static const uint8_t kCombinationFieldPos = kSignFieldPos - 17;
+ static const uint64_t kCombinationFieldMask = (1 << 17) - 1;
+ static const uint64_t kExponentFieldPos = kCombinationFieldPos + 3;
+ static const uint64_t kCoefficientContinuationFieldMask = (1ull << kCombinationFieldPos) - 1;
+ static const uint64_t kCombinationNonCanonical = 3 << 15;
+ static const uint64_t kCombinationInfinity = 0x1e << 12;
+ static const uint64_t kCombinationNaN = 0x1f << 12;
+ static const uint64_t kCanonicalCoefficientHighFieldMask = (1ull << 49) - 1;
+
+ uint64_t _getCombinationField() const {
+ return (_value.high64 >> kCombinationFieldPos) & kCombinationFieldMask;
+ }
+
Value _value;
};
-
} // namespace mongo
diff --git a/src/mongo/platform/decimal128_dummy.cpp b/src/mongo/platform/decimal128_dummy.cpp
index d2db7d5892a..59a4a64e78b 100644
--- a/src/mongo/platform/decimal128_dummy.cpp
+++ b/src/mongo/platform/decimal128_dummy.cpp
@@ -41,7 +41,9 @@ Decimal128::Decimal128(int64_t int64Value) {
invariant(false);
}
-Decimal128::Decimal128(double doubleValue, RoundingMode roundMode) {
+Decimal128::Decimal128(double doubleValue,
+ RoundingPrecision roundPrecision,
+ RoundingMode roundMode) {
invariant(false);
}
@@ -207,4 +209,6 @@ const Decimal128 Decimal128::kNegativeInfinity = Decimal128();
const Decimal128 Decimal128::kPositiveNaN = Decimal128();
const Decimal128 Decimal128::kNegativeNaN = Decimal128();
+const Decimal128 Decimal128::kNormalizedZero = {};
+
} // namespace mongo
diff --git a/src/mongo/platform/decimal128_test.cpp b/src/mongo/platform/decimal128_test.cpp
index a90eca6ee7c..296d6490514 100644
--- a/src/mongo/platform/decimal128_test.cpp
+++ b/src/mongo/platform/decimal128_test.cpp
@@ -39,6 +39,11 @@
namespace mongo {
// Tests for Decimal128 constructors
+TEST(Decimal128Test, TestDefaultConstructor) {
+ Decimal128 d;
+ ASSERT_TRUE(d.isBinaryEqual(Decimal128(0)));
+}
+
TEST(Decimal128Test, TestInt32ConstructorZero) {
int32_t intZero = 0;
Decimal128 d(intZero);
@@ -185,13 +190,15 @@ TEST(Decimal128Test, TestDoubleConstructorNeg) {
TEST(Decimal128Test, TestDoubleConstructorMaxRoundDown) {
double doubleMax = DBL_MAX;
- Decimal128 d(doubleMax, Decimal128::RoundingMode::kRoundTowardNegative);
+ Decimal128 d(
+ doubleMax, Decimal128::kRoundTo15Digits, Decimal128::RoundingMode::kRoundTowardNegative);
ASSERT_EQUALS(d.toString(), "1.79769313486231E+308");
}
TEST(Decimal128Test, TestDoubleConstructorMaxRoundUp) {
double doubleMax = DBL_MAX;
- Decimal128 d(doubleMax, Decimal128::RoundingMode::kRoundTowardPositive);
+ Decimal128 d(
+ doubleMax, Decimal128::kRoundTo15Digits, Decimal128::RoundingMode::kRoundTowardPositive);
ASSERT_EQUALS(d.toString(), "1.79769313486232E+308");
}
@@ -269,6 +276,39 @@ TEST(Decimal128Test, TestStringConstructorNaN) {
ASSERT_EQUALS(val.low64, lowBytes);
}
+TEST(Decimal128Test, TestNonCanonicalDecimal) {
+ // It is possible to encode a significand with more than 34 decimal digits.
+ // Conforming implementations should not generate these, but they must be treated as zero
+ // when encountered. However, the exponent and sign still matter.
+
+ // 0x6c10000000000000 0000000000000000 = non-canonical 0, all ignored bits clear
+ Decimal128 nonCanonical0E0(Decimal128::Value{0, 0x6c10000000000000ull});
+ std::string zeroE0 = nonCanonical0E0.toString();
+ ASSERT_EQUALS(zeroE0, "0");
+
+ // 0xec100000deadbeef 0123456789abcdef = non-canonical -0, random stuff in ignored bits
+ Decimal128 nonCanonicalM0E0(Decimal128::Value{0x0123456789abcdefull, 0xec100000deadbeefull});
+ std::string minusZeroE0 = nonCanonicalM0E0.toString();
+ ASSERT_EQUALS(minusZeroE0, "-0");
+
+ // 0x6c11fffffffffffff ffffffffffffffff = non-canonical 0.000, all ignored bits set
+ Decimal128 nonCanonical0E3(Decimal128::Value{0xffffffffffffffffull, 0x6c11ffffffffffffull});
+ std::string zeroE3 = nonCanonical0E3.toString();
+ ASSERT_EQUALS(zeroE3, "0E+3");
+
+ // Check extraction functions, they should treat this as the corresponding zero as well.
+ ASSERT_EQUALS(nonCanonical0E3.getBiasedExponent(), Decimal128("0E+3").getBiasedExponent());
+ ASSERT_EQUALS(nonCanonical0E3.getCoefficientHigh(), 0u);
+ ASSERT_EQUALS(nonCanonical0E3.getCoefficientLow(), 0u);
+
+ // Check doing some arithmetic opations and number conversions
+ const double minusZeroDouble = nonCanonicalM0E0.toDouble();
+ ASSERT_EQUALS(minusZeroDouble, 0.0);
+ ASSERT_EQUALS(-1.0, std::copysign(1.0, minusZeroDouble));
+ ASSERT_TRUE(nonCanonical0E3.add(Decimal128(1)).isEqual(Decimal128(1)));
+ ASSERT_TRUE(Decimal128(1).divide(nonCanonicalM0E0).isEqual(Decimal128::kNegativeInfinity));
+}
+
// Tests for absolute value function
TEST(Decimal128Test, TestAbsValuePos) {
Decimal128 d(25);
diff --git a/src/mongo/scripting/mozjs/valuewriter.cpp b/src/mongo/scripting/mozjs/valuewriter.cpp
index 8b811f1afc1..44465ef86d5 100644
--- a/src/mongo/scripting/mozjs/valuewriter.cpp
+++ b/src/mongo/scripting/mozjs/valuewriter.cpp
@@ -33,6 +33,7 @@
#include <js/Conversions.h>
#include "mongo/base/error_codes.h"
+#include "mongo/platform/decimal128.h"
#include "mongo/scripting/mozjs/exception.h"
#include "mongo/scripting/mozjs/implscope.h"
#include "mongo/scripting/mozjs/jsstringwrapper.h"
@@ -164,7 +165,7 @@ int64_t ValueWriter::toInt64() {
Decimal128 ValueWriter::toDecimal128() {
if (_value.isNumber()) {
- return Decimal128(toNumber());
+ return Decimal128(toNumber(), Decimal128::kRoundTo15Digits);
}
if (getScope(_context)->getProto<NumberIntInfo>().instanceOf(_value))
diff --git a/src/mongo/util/safe_num.cpp b/src/mongo/util/safe_num.cpp
index cf8af43377c..61c0d4856a4 100644
--- a/src/mongo/util/safe_num.cpp
+++ b/src/mongo/util/safe_num.cpp
@@ -150,7 +150,7 @@ bool SafeNum::isIdentical(const SafeNum& rhs) const {
case NumberDouble:
return _value.doubleVal == rhs._value.doubleVal;
case NumberDecimal:
- return Decimal128(_value.decimalVal).isEqual(rhs._value.decimalVal);
+ return Decimal128(_value.decimalVal).isEqual(Decimal128(rhs._value.decimalVal));
case EOO:
// EOO doesn't match anything, including itself.
default:
@@ -187,15 +187,15 @@ double SafeNum::getDouble(const SafeNum& snum) {
Decimal128 SafeNum::getDecimal(const SafeNum& snum) {
switch (snum._type) {
case NumberInt:
- return snum._value.int32Val;
+ return Decimal128(snum._value.int32Val);
case NumberLong:
- return snum._value.int64Val;
+ return Decimal128(snum._value.int64Val);
case NumberDouble:
- return snum._value.doubleVal;
+ return Decimal128(snum._value.doubleVal, Decimal128::kRoundTo15Digits);
case NumberDecimal:
- return snum._value.decimalVal;
+ return Decimal128(snum._value.decimalVal);
default:
- return 0.0;
+ return Decimal128::kNormalizedZero;
}
}