diff options
author | Judah Schvimer <judah@mongodb.com> | 2020-03-03 11:35:22 -0500 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-03-20 13:36:53 +0000 |
commit | 8751e79bc03b5c4c679040440ac76481fd3db356 (patch) | |
tree | b51dd0aa8859537a491537c0534d6a31415baea4 /src/mongo/bson | |
parent | d302a91c30b2648a2262e4ce737e767a3db8597d (diff) | |
download | mongo-8751e79bc03b5c4c679040440ac76481fd3db356.tar.gz |
SERVER-46434 turn MemberConfig into IDL
Diffstat (limited to 'src/mongo/bson')
-rw-r--r-- | src/mongo/bson/bsonelement.cpp | 4 | ||||
-rw-r--r-- | src/mongo/bson/bsonelement.h | 55 | ||||
-rw-r--r-- | src/mongo/bson/bsonelement_test.cpp | 35 |
3 files changed, 94 insertions, 0 deletions
diff --git a/src/mongo/bson/bsonelement.cpp b/src/mongo/bson/bsonelement.cpp index 62693512426..eec51e5d6c8 100644 --- a/src/mongo/bson/bsonelement.cpp +++ b/src/mongo/bson/bsonelement.cpp @@ -65,6 +65,10 @@ using std::string; const double BSONElement::kLongLongMaxPlusOneAsDouble = scalbn(1, std::numeric_limits<long long>::digits); +const long long BSONElement::kLargestSafeLongLongAsDouble = + scalbn(1, std::numeric_limits<double>::digits); +const long long BSONElement::kSmallestSafeLongLongAsDouble = + scalbn(-1, std::numeric_limits<double>::digits); std::string BSONElement::jsonString(JsonStringFormat format, bool includeSeparator, diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h index 5ad7635ed25..c868654d65e 100644 --- a/src/mongo/bson/bsonelement.h +++ b/src/mongo/bson/bsonelement.h @@ -443,6 +443,13 @@ public: return numberDouble(); } + /** Like numberDouble() but with well-defined behavior for doubles that + * are NaNs, or too large/small to be represented as doubles. + * NaNs -> 0 + * very large decimals -> DOUBLE_MAX + * very small decimals -> DOUBLE_MIN */ + double safeNumberDouble() const; + /** Retrieve the object ID stored in the object. You must ensure the element is of type jstOID first. */ mongo::OID __oid() const { @@ -784,6 +791,14 @@ public: */ static const double kLongLongMaxPlusOneAsDouble; + /** + * Constant 'long long' representation of 2^53 (and -2^53). This is the largest (and smallest) + * 'long long' such that all 'long long's between the two can be safely represented as a double + * without losing precision. + */ + static const long long kLargestSafeLongLongAsDouble; + static const long long kSmallestSafeLongLongAsDouble; + private: template <typename Generator> BSONObj _jsonStringGenerator(const Generator& g, @@ -881,6 +896,46 @@ inline double BSONElement::numberDouble() const { } } +inline double BSONElement::safeNumberDouble() const { + switch (type()) { + case NumberDouble: { + double d = _numberDouble(); + if (std::isnan(d)) { + return 0; + } + return d; + } + case NumberInt: { + return _numberInt(); + } + case NumberLong: { + long long d = _numberLong(); + if (d > 0 && d > kLargestSafeLongLongAsDouble) { + return static_cast<double>(kLargestSafeLongLongAsDouble); + } + if (d < 0 && d < kSmallestSafeLongLongAsDouble) { + return static_cast<double>(kSmallestSafeLongLongAsDouble); + } + return d; + } + case NumberDecimal: { + Decimal128 d = _numberDecimal(); + if (d.isNaN()) { + return 0; + } + if (d.isGreater(Decimal128(std::numeric_limits<double>::max()))) { + return std::numeric_limits<double>::max(); + } + if (d.isLess(Decimal128(std::numeric_limits<double>::min()))) { + return std::numeric_limits<double>::min(); + } + return _numberDecimal().toDouble(); + } + default: + return 0; + } +} + inline int BSONElement::numberInt() const { switch (type()) { case NumberDouble: diff --git a/src/mongo/bson/bsonelement_test.cpp b/src/mongo/bson/bsonelement_test.cpp index f98ccf93894..9fd3ac54e0a 100644 --- a/src/mongo/bson/bsonelement_test.cpp +++ b/src/mongo/bson/bsonelement_test.cpp @@ -203,6 +203,41 @@ TEST(BSONElement, SafeNumberLongNegativeBound) { ASSERT_EQ(obj["negativeInfinity"].safeNumberLong(), std::numeric_limits<long long>::lowest()); } +TEST(BSONElement, SafeNumberDoublePositiveBound) { + BSONObj obj = BSON("kLargestSafeLongLongAsDouble" + << BSONElement::kLargestSafeLongLongAsDouble << "towardsZero" + << BSONElement::kLargestSafeLongLongAsDouble - 1 << "towardsInfinity" + << BSONElement::kLargestSafeLongLongAsDouble + 1 << "positiveInfinity" + << std::numeric_limits<long long>::max()); + + ASSERT_EQ(obj["kLargestSafeLongLongAsDouble"].safeNumberDouble(), + (double)BSONElement::kLargestSafeLongLongAsDouble); + ASSERT_EQ(obj["towardsZero"].safeNumberDouble(), + (double)(BSONElement::kLargestSafeLongLongAsDouble - 1)); + ASSERT_EQ(obj["towardsInfinity"].safeNumberDouble(), + (double)BSONElement::kLargestSafeLongLongAsDouble); + ASSERT_EQ(obj["positiveInfinity"].safeNumberDouble(), + (double)BSONElement::kLargestSafeLongLongAsDouble); +} + +TEST(BSONElement, SafeNumberDoubleNegativeBound) { + BSONObj obj = + BSON("kSmallestSafeLongLongAsDouble" + << BSONElement::kSmallestSafeLongLongAsDouble << "towardsZero" + << BSONElement::kSmallestSafeLongLongAsDouble + 1 << "towardsNegativeInfinity" + << BSONElement::kSmallestSafeLongLongAsDouble - 1 << "negativeInfinity" + << std::numeric_limits<long long>::min()); + + ASSERT_EQ(obj["kSmallestSafeLongLongAsDouble"].safeNumberDouble(), + (double)BSONElement::kSmallestSafeLongLongAsDouble); + ASSERT_EQ(obj["towardsZero"].safeNumberDouble(), + (double)(BSONElement::kSmallestSafeLongLongAsDouble + 1)); + ASSERT_EQ(obj["towardsNegativeInfinity"].safeNumberDouble(), + (double)BSONElement::kSmallestSafeLongLongAsDouble); + ASSERT_EQ(obj["negativeInfinity"].safeNumberDouble(), + (double)BSONElement::kSmallestSafeLongLongAsDouble); +} + TEST(BSONElementIntegerParseTest, ParseIntegerElementToNonNegativeLongRejectsNegative) { BSONObj query = BSON("" << -2LL); ASSERT_NOT_OK(query.firstElement().parseIntegerElementToNonNegativeLong()); |