0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-22 04:59:34 +01:00

Revert "SERVER-24374 Make Decimal128 integer ctors constexpr"

This reverts commit e039271638.
This commit is contained in:
Billy Donahue 2019-06-16 14:43:09 -04:00
parent e039271638
commit 58df54d9c7
5 changed files with 125 additions and 146 deletions

View File

@ -147,12 +147,12 @@ env.Library(
'$BUILD_DIR/third_party/shim_allocator',
'$BUILD_DIR/third_party/shim_boost',
'$BUILD_DIR/third_party/shim_fmt',
'$BUILD_DIR/third_party/shim_intel_decimal128',
'$BUILD_DIR/third_party/shim_pcrecpp',
'boost_assert_shim',
'util/quick_exit',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/third_party/shim_intel_decimal128',
'util/debugger',
],
)

View File

@ -187,6 +187,15 @@ BID_UINT128 decimal128ToLibraryType(Decimal128::Value value) {
}
} // namespace
Decimal128::Decimal128(std::int32_t int32Value)
: _value(libraryTypeToValue(bid128_from_int32(int32Value))) {}
Decimal128::Decimal128(std::int64_t int64Value)
: _value(libraryTypeToValue(bid128_from_int64(int64Value))) {}
Decimal128::Decimal128(std::uint64_t uint64Value)
: _value(libraryTypeToValue(bid128_from_uint64(uint64Value))) {}
/**
* Quantize a doubleValue argument to a Decimal128 with exactly 15 digits
* of precision.
@ -283,8 +292,8 @@ Decimal128::Decimal128(double doubleValue,
// 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)
std::uint64_t kSmallest15DigitInt = 1E14; // A 1 with 14 zeros
std::uint64_t kLargest15DigitInt = 1E15 - 1; // 15 nines
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
@ -315,6 +324,10 @@ Decimal128::Decimal128(std::string stringValue,
_value = libraryTypeToValue(dec128);
}
Decimal128::Value Decimal128::getValue() const {
return _value;
}
Decimal128 Decimal128::toAbs() const {
BID_UINT128 dec128 = decimal128ToLibraryType(_value);
dec128 = bid128_abs(dec128);
@ -448,12 +461,12 @@ std::int32_t Decimal128::toInt(std::uint32_t* signalingFlags, RoundingMode round
}
}
std::int64_t Decimal128::toLong(RoundingMode roundMode) const {
int64_t Decimal128::toLong(RoundingMode roundMode) const {
std::uint32_t throwAwayFlag = 0;
return toLong(&throwAwayFlag, roundMode);
}
std::int64_t Decimal128::toLong(std::uint32_t* signalingFlags, RoundingMode roundMode) const {
int64_t Decimal128::toLong(std::uint32_t* signalingFlags, RoundingMode roundMode) const {
BID_UINT128 dec128 = decimal128ToLibraryType(_value);
switch (roundMode) {
case kRoundTiesToEven:
@ -900,10 +913,10 @@ Decimal128 Decimal128::quantize(const Decimal128& reference,
auto normalizedThis = this->normalize();
auto normalizedReferenceExponent =
static_cast<std::int32_t>(reference.normalize().getBiasedExponent());
static_cast<int32_t>(reference.normalize().getBiasedExponent());
if (normalizedReferenceExponent != 0 &&
(static_cast<std::int32_t>(normalizedThis.getBiasedExponent()) -
normalizedReferenceExponent) > 33) {
(static_cast<int32_t>(normalizedThis.getBiasedExponent()) - normalizedReferenceExponent) >
33) {
return normalizedThis;
}
return nonNormalizingQuantize(reference, signalingFlags, roundMode);
@ -976,7 +989,7 @@ const std::uint64_t t17 = 100ull * 1000 * 1000 * 1000 * 1000 * 1000;
// Computed by running the calculations in Python, and verified with static_assert.
const std::uint64_t t34lo64 = 4003012203950112767ULL;
#if defined(__GNUC__)
MONGO_STATIC_ASSERT(t34lo64 == t17 * t17 - 1);
static_assert(t34lo64 == t17 * t17 - 1, "precomputed constant is wrong");
#endif
// Mod t17 by 2^32 to get the low 32 bits of t17's binary representation
const std::uint64_t t17lo32 = t17 % (1ull << 32);
@ -1001,7 +1014,7 @@ const Decimal128 Decimal128::kSmallestNegative(1, 0, 0, 1);
// Get the representation of 0 (0E0).
const Decimal128 Decimal128::kNormalizedZero(Decimal128::Value(
{0, static_cast<std::uint64_t>(Decimal128::kExponentBias) << Decimal128::kExponentFieldPos}));
{0, static_cast<uint64_t>(Decimal128::kExponentBias) << Decimal128::kExponentFieldPos}));
// Shift the format of the combination bits to the right position to get Inf and NaN
// +Inf = 0111 1000 ... ... = 0x78 ... ..., -Inf = 1111 1000 ... ... = 0xf8 ... ...

View File

@ -33,13 +33,11 @@
#include <cstdint>
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>
#include "mongo/config.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/if_constexpr.h"
namespace mongo {
@ -77,11 +75,10 @@ public:
static const Decimal128 kPiOver180;
static const Decimal128 k180OverPi;
static constexpr std::uint32_t kMaxBiasedExponent = 6143 + 6144;
static const uint32_t kMaxBiasedExponent = 6143 + 6144;
// Biased exponent of a Decimal128 with least significant digit in the units place
static constexpr std::int32_t kExponentBias = 6143 + 33;
static constexpr std::uint32_t kInfinityExponent =
kMaxBiasedExponent + 1; // internal convention only
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
@ -127,17 +124,17 @@ public:
kInexact = 0x20,
};
constexpr static bool hasFlag(std::uint32_t signalingFlags, SignalingFlag f) {
static bool hasFlag(std::uint32_t signalingFlags, SignalingFlag f) {
return ((signalingFlags & f) != 0u);
}
/**
* Returns true if a valid Decimal can be constructed from the given arguments.
*/
constexpr static bool isValid(std::uint64_t sign,
std::uint64_t exponent,
std::uint64_t coefficientHigh,
std::uint64_t coefficientLow) {
static bool isValid(uint64_t sign,
uint64_t exponent,
uint64_t coefficientHigh,
uint64_t coefficientLow) {
if (coefficientHigh >= 0x1ed09bead87c0 &&
(coefficientHigh != 0x1ed09bead87c0 || coefficientLow != 0x378d8e63ffffffff)) {
return false;
@ -152,10 +149,7 @@ public:
/**
* Construct a 0E0 valued Decimal128.
*/
constexpr Decimal128()
: _value{0,
static_cast<std::uint64_t>(Decimal128::kExponentBias)
<< Decimal128::kExponentFieldPos} {}
Decimal128() : _value(kNormalizedZero._value) {}
/**
* This constructor takes in a raw decimal128 type, which consists of two
@ -168,15 +162,16 @@ public:
* 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.)
*/
constexpr Decimal128(std::uint64_t sign,
std::uint64_t exponent,
std::uint64_t coefficientHigh,
std::uint64_t coefficientLow)
: _value(_valueFromParts(sign, exponent, coefficientHigh, coefficientLow)) {}
Decimal128(uint64_t sign, uint64_t exponent, uint64_t coefficientHigh, uint64_t coefficientLow)
: _value(
Value{coefficientLow,
(sign << kSignFieldPos) | (exponent << kExponentFieldPos) | coefficientHigh}) {
dassert(isValid(sign, exponent, coefficientHigh, coefficientLow));
}
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
constexpr explicit Decimal128(T v)
: Decimal128(v < 0 ? 1 : 0, kExponentBias, 0, _makeCoefficientLow(v)) {}
explicit Decimal128(std::int32_t int32Value);
explicit Decimal128(std::int64_t int64Value);
explicit Decimal128(std::uint64_t uint64Value);
/**
* This constructor takes a double and constructs a Decimal128 object given a roundMode, either
@ -212,15 +207,13 @@ public:
/**
* This function gets the inner Value struct storing a Decimal128 value.
*/
constexpr Value getValue() const {
return _value;
}
Value getValue() const;
/**
* Extracts the biased exponent from the combination field.
*/
constexpr std::uint32_t getBiasedExponent() const {
const std::uint64_t combo = _getCombinationField();
uint32_t getBiasedExponent() const {
const uint64_t combo = _getCombinationField();
if (combo < kCombinationNonCanonical)
return combo >> 3;
@ -233,7 +226,7 @@ public:
* Returns the high 49 bits of the 113-bit binary encoded coefficient. Returns 0 for
* non-canonical or non-finite numbers.
*/
constexpr std::uint64_t getCoefficientHigh() const {
uint64_t getCoefficientHigh() const {
return _getCombinationField() < kCombinationNonCanonical
? _value.high64 & kCanonicalCoefficientHighFieldMask
: 0;
@ -243,7 +236,7 @@ public:
* Returns the low 64 bits of the 113-bit binary encoded coefficient. Returns 0 for
* non-canonical or non-finite numbers.
*/
constexpr std::uint64_t getCoefficientLow() const {
uint64_t getCoefficientLow() const {
return _getCombinationField() < kCombinationNonCanonical ? _value.low64 : 0;
}
@ -526,16 +519,15 @@ public:
}
private:
constexpr static std::uint8_t kSignFieldPos = 64 - 1;
constexpr static std::uint8_t kCombinationFieldPos = kSignFieldPos - 17;
constexpr static std::uint64_t kCombinationFieldMask = (1 << 17) - 1;
constexpr static std::uint64_t kExponentFieldPos = kCombinationFieldPos + 3;
constexpr static std::uint64_t kCoefficientContinuationFieldMask =
(1ull << kCombinationFieldPos) - 1;
constexpr static std::uint64_t kCombinationNonCanonical = 3 << 15;
constexpr static std::uint64_t kCombinationInfinity = 0x1e << 12;
constexpr static std::uint64_t kCombinationNaN = 0x1f << 12;
constexpr static std::uint64_t kCanonicalCoefficientHighFieldMask = (1ull << 49) - 1;
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;
std::string _convertToScientificNotation(StringData coefficient, int adjustedExponent) const;
std::string _convertToStandardDecimalNotation(StringData coefficient, int exponent) const;
@ -550,35 +542,10 @@ private:
std::uint32_t* signalingFlags,
RoundingMode roundMode = kRoundTiesToEven) const;
constexpr std::uint64_t _getCombinationField() const {
uint64_t _getCombinationField() const {
return (_value.high64 >> kCombinationFieldPos) & kCombinationFieldMask;
}
constexpr static Value _valueFromParts(std::uint64_t sign,
std::uint64_t exponent,
std::uint64_t coefficientHigh,
std::uint64_t coefficientLow) {
// For constexpr's sake the invariant must be compiled only if !isValid().
if (!isValid(sign, exponent, coefficientHigh, coefficientLow)) {
invariant(false, "invalid arguments to Decimal128()");
}
return Value{coefficientLow,
(sign << kSignFieldPos) | (exponent << kExponentFieldPos) | coefficientHigh};
}
// Constructs the absolute value of v as a uint64_t in a constexpr-permitted way.
template <typename T>
constexpr std::uint64_t _makeCoefficientLow(T i) {
// if constexpr to avoid MSVC warnings.
IF_CONSTEXPR(std::is_signed_v<T>) {
return i < 0 ? static_cast<std::uint64_t>(-i) : static_cast<std::uint64_t>(i);
}
else {
return static_cast<std::uint64_t>(i);
}
}
Value _value;
};
} // namespace mongo

View File

@ -31,7 +31,6 @@
#include <array>
#include <cmath>
#include <fmt/format.h>
#include <memory>
#include <string>
#include <utility>
@ -41,68 +40,80 @@
namespace mongo {
using namespace fmt::literals;
// Tests for Decimal128 constructors
TEST(Decimal128Test, TestDefaultConstructor) {
Decimal128 d;
ASSERT_TRUE(d.isBinaryEqual(Decimal128(0)));
}
template <typename T>
using Lim = std::numeric_limits<T>;
TEST(Decimal128Test, TestInt32ConstructorZero) {
int32_t intZero = 0;
Decimal128 d(intZero);
Decimal128::Value val = d.getValue();
// 0x3040000000000000 0000000000000000 = +0E+0
uint64_t highBytes = 0x3040000000000000ull;
uint64_t lowBytes = 0x0000000000000000ull;
ASSERT_EQUALS(val.high64, highBytes);
ASSERT_EQUALS(val.low64, lowBytes);
}
TEST(Decimal128Test, TestConstructor) {
// High bits of a positive Decimal128 with exponent 1.
constexpr uint64_t posHigh = 0x3040000000000000;
// High bits of a negative Decimal128 with exponent 1.
constexpr uint64_t negHigh = 0xb040000000000000;
TEST(Decimal128Test, TestInt32ConstructorMax) {
int32_t intMax = std::numeric_limits<int32_t>::max();
Decimal128 d(intMax);
Decimal128::Value val = d.getValue();
// 0x3040000000000000 000000007fffffff = +2147483647E+0
uint64_t highBytes = 0x3040000000000000ull;
uint64_t lowBytes = 0x000000007fffffffull;
ASSERT_EQUALS(val.high64, highBytes);
ASSERT_EQUALS(val.low64, lowBytes);
}
#define TEST_CTOR(N, HIGH64, LOW64) \
ASSERT_EQ(Decimal128{N}.getValue().high64, static_cast<uint64_t>(HIGH64)); \
ASSERT_EQ(Decimal128{N}.getValue().low64, static_cast<uint64_t>(LOW64));
TEST(Decimal128Test, TestInt32ConstructorMin) {
int32_t intMin = std::numeric_limits<int32_t>::lowest();
Decimal128 d(intMin);
Decimal128::Value val = d.getValue();
// 0xb040000000000000 000000007fffffff = -2147483648E+0
uint64_t highBytes = 0xb040000000000000ull;
uint64_t lowBytes = 0x0000000080000000ull;
ASSERT_EQUALS(val.high64, highBytes);
ASSERT_EQUALS(val.low64, lowBytes);
}
TEST_CTOR(int8_t{0}, posHigh, 0); // +0E+0
TEST_CTOR(Lim<int8_t>::lowest(), negHigh, uint64_t{1} << 7);
TEST_CTOR(Lim<int8_t>::max(), posHigh, (uint64_t{1} << 7) - 1);
TEST_CTOR(int8_t{5}, posHigh, 0x5);
TEST(Decimal128Test, TestInt64ConstructorZero) {
int64_t longZero = 0;
Decimal128 d(longZero);
Decimal128::Value val = d.getValue();
// 0x3040000000000000 0000000000000000 = +0E+0
uint64_t highBytes = 0x3040000000000000ull;
uint64_t lowBytes = 0x0000000000000000ull;
ASSERT_EQUALS(val.high64, highBytes);
ASSERT_EQUALS(val.low64, lowBytes);
}
TEST_CTOR(uint8_t{0}, posHigh, 0);
TEST_CTOR(Lim<uint8_t>::max(), posHigh, (uint64_t{1} << 8) - 1);
TEST_CTOR(uint8_t{5}, posHigh, 0x5);
TEST(Decimal128Test, TestInt64ConstructorMax) {
int64_t longMax = std::numeric_limits<long long>::max();
Decimal128 d(longMax);
Decimal128::Value val = d.getValue();
// 0x3040000000000000 7fffffffffffffff = +9223372036854775807E+0
uint64_t highBytes = 0x3040000000000000ull;
uint64_t lowBytes = 0x7fffffffffffffffull;
ASSERT_EQUALS(val.high64, highBytes);
ASSERT_EQUALS(val.low64, lowBytes);
}
TEST_CTOR(int16_t{0}, posHigh, 0);
TEST_CTOR(Lim<int16_t>::lowest(), negHigh, uint64_t{1} << 15);
TEST_CTOR(Lim<int16_t>::max(), posHigh, (uint64_t{1} << 15) - 1);
TEST_CTOR(int16_t{5}, posHigh, 0x5);
TEST_CTOR(uint16_t{0}, posHigh, 0);
TEST_CTOR(Lim<uint16_t>::max(), posHigh, (uint64_t{1} << 16) - 1);
TEST_CTOR(uint16_t{5}, posHigh, 0x5);
TEST_CTOR(int32_t{0}, posHigh, 0);
TEST_CTOR(Lim<int32_t>::lowest(), negHigh, uint64_t{1} << 31);
TEST_CTOR(Lim<int32_t>::max(), posHigh, (uint64_t{1} << 31) - 1);
TEST_CTOR(int32_t{5}, posHigh, 0x5);
TEST_CTOR(uint32_t{0}, posHigh, 0);
TEST_CTOR(Lim<uint32_t>::max(), posHigh, (uint64_t{1} << 32) - 1);
TEST_CTOR(uint32_t{5}, posHigh, 0x5);
TEST_CTOR(int64_t{0}, posHigh, 0);
TEST_CTOR(Lim<int64_t>::lowest(), negHigh, uint64_t{1} << 63);
TEST_CTOR(Lim<int64_t>::max(), posHigh, (uint64_t{1} << 63) - 1);
TEST_CTOR(int64_t{5}, posHigh, 0x5);
TEST_CTOR(uint64_t{0}, posHigh, 0);
TEST_CTOR(Lim<uint64_t>::max(), posHigh, Lim<uint64_t>::max());
TEST_CTOR(uint64_t{5}, posHigh, 0x5);
#undef TEST_CTOR
TEST(Decimal128Test, TestInt64ConstructorMin) {
int64_t longMin = std::numeric_limits<long long>::lowest();
Decimal128 d(longMin);
Decimal128::Value val = d.getValue();
// 0xb040000000000000 8000000000000000 = -9223372036854775808E+0
uint64_t highBytes = 0xb040000000000000;
uint64_t lowBytes = 0x8000000000000000;
ASSERT_EQUALS(val.high64, highBytes);
ASSERT_EQUALS(val.low64, lowBytes);
}
TEST(Decimal128Test, TestPartsConstructor) {
constexpr Decimal128 expected(10);
Decimal128 expected(10);
Decimal128 val(0LL, Decimal128::kExponentBias, 0LL, 10LL);
ASSERT_EQUALS(val.getValue().low64, expected.getValue().low64);
ASSERT_EQUALS(val.getValue().low64, expected.getValue().low64);
@ -287,19 +298,17 @@ TEST(Decimal128Test, TestNonCanonicalDecimal) {
// when encountered. However, the exponent and sign still matter.
// 0x6c10000000000000 0000000000000000 = non-canonical 0, all ignored bits clear
constexpr Decimal128 nonCanonical0E0(Decimal128::Value{0, 0x6c10000000000000ull});
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
constexpr Decimal128 nonCanonicalM0E0(
Decimal128::Value{0x0123456789abcdefull, 0xec100000deadbeefull});
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
constexpr Decimal128 nonCanonical0E3(
Decimal128::Value{0xffffffffffffffffull, 0x6c11ffffffffffffull});
Decimal128 nonCanonical0E3(Decimal128::Value{0xffffffffffffffffull, 0x6c11ffffffffffffull});
std::string zeroE3 = nonCanonical0E3.toString();
ASSERT_EQUALS(zeroE3, "0E+3");
@ -318,13 +327,13 @@ TEST(Decimal128Test, TestNonCanonicalDecimal) {
// Tests for absolute value function
TEST(Decimal128Test, TestAbsValuePos) {
constexpr Decimal128 d(25);
Decimal128 d(25);
Decimal128 dAbs = d.toAbs();
ASSERT_TRUE(dAbs.isEqual(d));
}
TEST(Decimal128Test, TestAbsValueNeg) {
constexpr Decimal128 d(-25);
Decimal128 d(-25);
Decimal128 dAbs = d.toAbs();
ASSERT_TRUE(dAbs.isEqual(Decimal128(25)));
}
@ -949,8 +958,8 @@ TEST(Decimal128Test, TestDecimal128DivideSignaling) {
// Test Decimal128 special comparisons
TEST(Decimal128Test, TestDecimal128IsZero) {
constexpr Decimal128 d1(0);
constexpr Decimal128 d2(500);
Decimal128 d1(0);
Decimal128 d2(500);
ASSERT_TRUE(d1.isZero());
ASSERT_FALSE(d2.isZero());
}

View File

@ -30,7 +30,6 @@
#include "mongo/platform/basic.h"
#include <cmath>
#include <fmt/format.h>
#include <limits>
#include <type_traits>
@ -44,8 +43,6 @@ namespace mongo {
namespace {
using namespace fmt::literals;
// Char values
const signed char kCharMax = std::numeric_limits<signed char>::max();
const int kCharMaxAsInt = kCharMax;
@ -357,15 +354,8 @@ void integerToDecimal128() {
v.emplace_back(-5);
}
for (const Integer n : v) {
auto d = representAs<Decimal128>(n);
ASSERT(d);
if (!d->isEqual(Decimal128(std::to_string(n)))) {
FAIL(
"Failed expectation, representAs<Decimal128>({}) == Decimal128({}),"
" but !Decimal128({}).isEqual(Decimal128(std::to_string({}))"_format(
n, d->toString(), d->toString(), n));
}
for (const auto& n : v) {
ASSERT(representAs<Decimal128>(n)->isEqual(Decimal128(std::to_string(n))));
}
}