summaryrefslogtreecommitdiff
path: root/src/mongo/bson
diff options
context:
space:
mode:
authorJudah Schvimer <judah@mongodb.com>2020-03-03 11:35:22 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-03-20 13:36:53 +0000
commit8751e79bc03b5c4c679040440ac76481fd3db356 (patch)
treeb51dd0aa8859537a491537c0534d6a31415baea4 /src/mongo/bson
parentd302a91c30b2648a2262e4ce737e767a3db8597d (diff)
downloadmongo-8751e79bc03b5c4c679040440ac76481fd3db356.tar.gz
SERVER-46434 turn MemberConfig into IDL
Diffstat (limited to 'src/mongo/bson')
-rw-r--r--src/mongo/bson/bsonelement.cpp4
-rw-r--r--src/mongo/bson/bsonelement.h55
-rw-r--r--src/mongo/bson/bsonelement_test.cpp35
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());