diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/bson/util/bsoncolumn_test.cpp | 24 | ||||
-rw-r--r-- | src/mongo/bson/util/simple8b_type_util.cpp | 13 |
2 files changed, 34 insertions, 3 deletions
diff --git a/src/mongo/bson/util/bsoncolumn_test.cpp b/src/mongo/bson/util/bsoncolumn_test.cpp index 386d824a8cc..215b4bc1eac 100644 --- a/src/mongo/bson/util/bsoncolumn_test.cpp +++ b/src/mongo/bson/util/bsoncolumn_test.cpp @@ -1188,6 +1188,30 @@ TEST_F(BSONColumnTest, DoubleIntegerOverflow) { verifyDecompression(binData, {e1, e2}); } +TEST_F(BSONColumnTest, DoubleZerosSignDifference) { + BSONColumnBuilder cb("test"_sd); + + // 0.0 compares equal to -0.0 when compared as double. Make sure we can handle this case without + // data loss. + auto d1 = createElementDouble(0.0); + auto d2 = createElementDouble(-0.0); + cb.append(d1); + cb.append(d2); + + // These numbers are encoded as a large integer that does not fit in Simple8b so the result is + // two uncompressed literals. + ASSERT_EQ(deltaDoubleMemory(d2, d1), 0xFFFFFFFFFFFFFFFF); + + BufBuilder expected; + appendLiteral(expected, d1); + appendLiteral(expected, d2); + appendEOO(expected); + + auto binData = cb.finalize(); + verifyBinary(binData, expected); + verifyDecompression(binData, {d1, d2}); +} + TEST_F(BSONColumnTest, Decimal128Base) { BSONColumnBuilder cb("test"_sd); diff --git a/src/mongo/bson/util/simple8b_type_util.cpp b/src/mongo/bson/util/simple8b_type_util.cpp index b009c723f0b..1ec42cdf8bf 100644 --- a/src/mongo/bson/util/simple8b_type_util.cpp +++ b/src/mongo/bson/util/simple8b_type_util.cpp @@ -144,10 +144,14 @@ boost::optional<uint8_t> Simple8bTypeUtil::calculateDecimalShiftMultiplier(doubl } boost::optional<int64_t> Simple8bTypeUtil::encodeDouble(double val, uint8_t scaleIndex) { - if (scaleIndex == kMemoryAsInteger) { + auto bitCastToInt64 = [](double value) { int64_t ret; - memcpy(&ret, &val, sizeof(ret)); + memcpy(&ret, &value, sizeof(ret)); return ret; + }; + + if (scaleIndex == kMemoryAsInteger) { + return bitCastToInt64(val); } // Checks for both overflow and handles NaNs @@ -166,7 +170,10 @@ boost::optional<int64_t> Simple8bTypeUtil::encodeDouble(double val, uint8_t scal // We use encodeInt64 to handle negative floats by taking the signed bit and placing it at the // lsb position int64_t valueToBeEncoded = std::llround(valTimesMultiplier); - if (valueToBeEncoded / scaleMultiplier != val) { + + // We need to check that we can get the exact bit pattern back. 0.0 and -0.0 compares as equal + // when comparing as doubles but they have different bit patterns. + if (bitCastToInt64(valueToBeEncoded / scaleMultiplier) != bitCastToInt64(val)) { return boost::none; } // Delta encoding. Gap should never induce overflow |