mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Add optional enforcement of valid integer range to Number
This commit is contained in:
@@ -13,16 +13,47 @@ class Number;
|
||||
std::string
|
||||
to_string(Number const& amount);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool
|
||||
isPowerOfTen(T value)
|
||||
{
|
||||
while (value >= 10 && value % 10 == 0)
|
||||
value /= 10;
|
||||
return value == 1;
|
||||
}
|
||||
|
||||
class Number
|
||||
{
|
||||
public:
|
||||
/** Describes whether and how to enforce this number as an integer.
|
||||
*
|
||||
* - none: No enforcement. The value may vary freely. This is the default.
|
||||
* - weak: If the absolute value is greater than maxIntValue, valid() will
|
||||
* return false.
|
||||
* - strong: Assignment operations will throw if the absolute value is above
|
||||
* maxIntValue.
|
||||
*/
|
||||
enum EnforceInteger { none, weak, strong };
|
||||
|
||||
private:
|
||||
using rep = std::int64_t;
|
||||
rep mantissa_{0};
|
||||
int exponent_{std::numeric_limits<int>::lowest()};
|
||||
|
||||
// The enforcement setting is not serialized, and does not affect the
|
||||
// ledger. If not "none", the value is checked to be within the valid
|
||||
// integer range. With "strong", the checks will be made as automatic as
|
||||
// possible.
|
||||
EnforceInteger enforceInteger_ = none;
|
||||
|
||||
public:
|
||||
// The range for the mantissa when normalized
|
||||
constexpr static std::int64_t minMantissa = 1'000'000'000'000'000LL;
|
||||
constexpr static std::int64_t maxMantissa = 9'999'999'999'999'999LL;
|
||||
constexpr static rep minMantissa = 1'000'000'000'000'000LL;
|
||||
static_assert(isPowerOfTen(minMantissa));
|
||||
constexpr static rep maxMantissa = minMantissa * 10 - 1;
|
||||
static_assert(maxMantissa == 9'999'999'999'999'999LL);
|
||||
|
||||
constexpr static rep maxIntValue = minMantissa / 10;
|
||||
|
||||
// The range for the exponent when normalized
|
||||
constexpr static int minExponent = -32768;
|
||||
@@ -35,15 +66,33 @@ public:
|
||||
|
||||
explicit constexpr Number() = default;
|
||||
|
||||
Number(rep mantissa);
|
||||
explicit Number(rep mantissa, int exponent);
|
||||
Number(rep mantissa, EnforceInteger enforce = none);
|
||||
explicit Number(rep mantissa, int exponent, EnforceInteger enforce = none);
|
||||
explicit constexpr Number(rep mantissa, int exponent, unchecked) noexcept;
|
||||
constexpr Number(Number const& other) = default;
|
||||
constexpr Number(Number&& other) = default;
|
||||
|
||||
~Number() = default;
|
||||
|
||||
constexpr Number&
|
||||
operator=(Number const& other);
|
||||
constexpr Number&
|
||||
operator=(Number&& other);
|
||||
|
||||
constexpr rep
|
||||
mantissa() const noexcept;
|
||||
constexpr int
|
||||
exponent() const noexcept;
|
||||
|
||||
void
|
||||
setIntegerEnforcement(EnforceInteger enforce);
|
||||
|
||||
EnforceInteger
|
||||
integerEnforcement() const noexcept;
|
||||
|
||||
bool
|
||||
valid() const noexcept;
|
||||
|
||||
constexpr Number
|
||||
operator+() const noexcept;
|
||||
constexpr Number
|
||||
@@ -184,6 +233,9 @@ public:
|
||||
private:
|
||||
static thread_local rounding_mode mode_;
|
||||
|
||||
void
|
||||
checkInteger(char const* what) const;
|
||||
|
||||
void
|
||||
normalize();
|
||||
constexpr bool
|
||||
@@ -197,16 +249,52 @@ inline constexpr Number::Number(rep mantissa, int exponent, unchecked) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
inline Number::Number(rep mantissa, int exponent)
|
||||
: mantissa_{mantissa}, exponent_{exponent}
|
||||
inline Number::Number(rep mantissa, int exponent, EnforceInteger enforce)
|
||||
: mantissa_{mantissa}, exponent_{exponent}, enforceInteger_(enforce)
|
||||
{
|
||||
normalize();
|
||||
|
||||
checkInteger("Number::Number integer overflow");
|
||||
}
|
||||
|
||||
inline Number::Number(rep mantissa) : Number{mantissa, 0}
|
||||
inline Number::Number(rep mantissa, EnforceInteger enforce)
|
||||
: Number{mantissa, 0, enforce}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Number&
|
||||
Number::operator=(Number const& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
mantissa_ = other.mantissa_;
|
||||
exponent_ = other.exponent_;
|
||||
enforceInteger_ = std::max(enforceInteger_, other.enforceInteger_);
|
||||
|
||||
checkInteger("Number::operator= integer overflow");
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr Number&
|
||||
Number::operator=(Number&& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
// std::move doesn't really do anything for these types, but
|
||||
// this is future-proof in case the types ever change
|
||||
mantissa_ = std::move(other.mantissa_);
|
||||
exponent_ = std::move(other.exponent_);
|
||||
if (other.enforceInteger_ > enforceInteger_)
|
||||
enforceInteger_ = std::move(other.enforceInteger_);
|
||||
|
||||
checkInteger("Number::operator= integer overflow");
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline constexpr Number::rep
|
||||
Number::mantissa() const noexcept
|
||||
{
|
||||
@@ -219,6 +307,20 @@ Number::exponent() const noexcept
|
||||
return exponent_;
|
||||
}
|
||||
|
||||
inline void
|
||||
Number::setIntegerEnforcement(EnforceInteger enforce)
|
||||
{
|
||||
enforceInteger_ = enforce;
|
||||
|
||||
checkInteger("Number::setIntegerEnforcement integer overflow");
|
||||
}
|
||||
|
||||
inline Number::EnforceInteger
|
||||
Number::integerEnforcement() const noexcept
|
||||
{
|
||||
return enforceInteger_;
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
Number::operator+() const noexcept
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user