mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 02:37:36 +02:00
make detail::fp and detail::bigit constexpr
This commit is contained in:
committed by
Victor Zverovich
parent
5888de9f34
commit
c79a3841e8
@ -161,7 +161,7 @@ template <typename T> struct bits {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class fp;
|
class fp;
|
||||||
template <int SHIFT = 0> fp normalize(fp value);
|
template <int SHIFT = 0> FMT_CONSTEXPR fp normalize(fp value);
|
||||||
|
|
||||||
// Lower (upper) boundary is a value half way between a floating-point value
|
// Lower (upper) boundary is a value half way between a floating-point value
|
||||||
// and its predecessor (successor). Boundaries have the same exponent as the
|
// and its predecessor (successor). Boundaries have the same exponent as the
|
||||||
@ -194,16 +194,16 @@ class fp {
|
|||||||
static FMT_CONSTEXPR_DECL const int significand_size =
|
static FMT_CONSTEXPR_DECL const int significand_size =
|
||||||
bits<significand_type>::value;
|
bits<significand_type>::value;
|
||||||
|
|
||||||
fp() : f(0), e(0) {}
|
constexpr fp() : f(0), e(0) {}
|
||||||
fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
|
constexpr fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
|
||||||
|
|
||||||
// Constructs fp from an IEEE754 double. It is a template to prevent compile
|
// Constructs fp from an IEEE754 double. It is a template to prevent compile
|
||||||
// errors on platforms where double is not IEEE754.
|
// errors on platforms where double is not IEEE754.
|
||||||
template <typename Double> explicit fp(Double d) { assign(d); }
|
template <typename Double> explicit FMT_CONSTEXPR fp(Double d) { assign(d); }
|
||||||
|
|
||||||
// Assigns d to this and return true iff predecessor is closer than successor.
|
// Assigns d to this and return true iff predecessor is closer than successor.
|
||||||
template <typename Float, FMT_ENABLE_IF(is_supported_float<Float>::value)>
|
template <typename Float, FMT_ENABLE_IF(is_supported_float<Float>::value)>
|
||||||
bool assign(Float d) {
|
FMT_CONSTEXPR bool assign(Float d) {
|
||||||
// Assume float is in the format [sign][exponent][significand].
|
// Assume float is in the format [sign][exponent][significand].
|
||||||
using limits = std::numeric_limits<Float>;
|
using limits = std::numeric_limits<Float>;
|
||||||
const int float_significand_size = limits::digits - 1;
|
const int float_significand_size = limits::digits - 1;
|
||||||
@ -237,7 +237,7 @@ class fp {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||||
template <int SHIFT> fp normalize(fp value) {
|
template <int SHIFT> FMT_CONSTEXPR fp normalize(fp value) {
|
||||||
// Handle subnormals.
|
// Handle subnormals.
|
||||||
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
|
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
|
||||||
while ((value.f & shifted_implicit_bit) == 0) {
|
while ((value.f & shifted_implicit_bit) == 0) {
|
||||||
@ -255,7 +255,7 @@ template <int SHIFT> fp normalize(fp value) {
|
|||||||
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
|
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
|
||||||
|
|
||||||
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
|
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
|
||||||
inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
|
FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
|
||||||
#if FMT_USE_INT128
|
#if FMT_USE_INT128
|
||||||
auto product = static_cast<__uint128_t>(lhs) * rhs;
|
auto product = static_cast<__uint128_t>(lhs) * rhs;
|
||||||
auto f = static_cast<uint64_t>(product >> 64);
|
auto f = static_cast<uint64_t>(product >> 64);
|
||||||
@ -272,7 +272,9 @@ inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
|
FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
|
||||||
|
return {multiply(x.f, y.f), x.e + y.e + 64};
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
|
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
|
||||||
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
|
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
|
||||||
@ -345,14 +347,16 @@ struct accumulator {
|
|||||||
uint64_t lower;
|
uint64_t lower;
|
||||||
uint64_t upper;
|
uint64_t upper;
|
||||||
|
|
||||||
accumulator() : lower(0), upper(0) {}
|
constexpr accumulator() : lower(0), upper(0) {}
|
||||||
explicit operator uint32_t() const { return static_cast<uint32_t>(lower); }
|
constexpr explicit operator uint32_t() const {
|
||||||
|
return static_cast<uint32_t>(lower);
|
||||||
|
}
|
||||||
|
|
||||||
void operator+=(uint64_t n) {
|
FMT_CONSTEXPR void operator+=(uint64_t n) {
|
||||||
lower += n;
|
lower += n;
|
||||||
if (lower < n) ++upper;
|
if (lower < n) ++upper;
|
||||||
}
|
}
|
||||||
void operator>>=(int shift) {
|
FMT_CONSTEXPR void operator>>=(int shift) {
|
||||||
FMT_ASSERT(shift == 32, "");
|
FMT_ASSERT(shift == 32, "");
|
||||||
(void)shift;
|
(void)shift;
|
||||||
lower = (upper << 32) | (lower >> 32);
|
lower = (upper << 32) | (lower >> 32);
|
||||||
@ -370,27 +374,31 @@ class bigint {
|
|||||||
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
||||||
int exp_;
|
int exp_;
|
||||||
|
|
||||||
bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
|
FMT_CONSTEXPR20 bigit operator[](int index) const {
|
||||||
bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
|
return bigits_[to_unsigned(index)];
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR20 bigit& operator[](int index) {
|
||||||
|
return bigits_[to_unsigned(index)];
|
||||||
|
}
|
||||||
|
|
||||||
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
||||||
|
|
||||||
friend struct formatter<bigint>;
|
friend struct formatter<bigint>;
|
||||||
|
|
||||||
void subtract_bigits(int index, bigit other, bigit& borrow) {
|
FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
|
||||||
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
||||||
(*this)[index] = static_cast<bigit>(result);
|
(*this)[index] = static_cast<bigit>(result);
|
||||||
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_leading_zeros() {
|
FMT_CONSTEXPR20 void remove_leading_zeros() {
|
||||||
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
||||||
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
||||||
bigits_.resize(to_unsigned(num_bigits + 1));
|
bigits_.resize(to_unsigned(num_bigits + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes *this -= other assuming aligned bigints and *this >= other.
|
// Computes *this -= other assuming aligned bigints and *this >= other.
|
||||||
void subtract_aligned(const bigint& other) {
|
FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
|
||||||
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
|
FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
|
||||||
FMT_ASSERT(compare(*this, other) >= 0, "");
|
FMT_ASSERT(compare(*this, other) >= 0, "");
|
||||||
bigit borrow = 0;
|
bigit borrow = 0;
|
||||||
@ -401,7 +409,7 @@ class bigint {
|
|||||||
remove_leading_zeros();
|
remove_leading_zeros();
|
||||||
}
|
}
|
||||||
|
|
||||||
void multiply(uint32_t value) {
|
FMT_CONSTEXPR20 void multiply(uint32_t value) {
|
||||||
const double_bigit wide_value = value;
|
const double_bigit wide_value = value;
|
||||||
bigit carry = 0;
|
bigit carry = 0;
|
||||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||||
@ -412,7 +420,7 @@ class bigint {
|
|||||||
if (carry != 0) bigits_.push_back(carry);
|
if (carry != 0) bigits_.push_back(carry);
|
||||||
}
|
}
|
||||||
|
|
||||||
void multiply(uint64_t value) {
|
FMT_CONSTEXPR20 void multiply(uint64_t value) {
|
||||||
const bigit mask = ~bigit(0);
|
const bigit mask = ~bigit(0);
|
||||||
const double_bigit lower = value & mask;
|
const double_bigit lower = value & mask;
|
||||||
const double_bigit upper = value >> bigit_bits;
|
const double_bigit upper = value >> bigit_bits;
|
||||||
@ -430,14 +438,16 @@ class bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bigint() : exp_(0) {}
|
FMT_CONSTEXPR20 bigint() : exp_(0) {}
|
||||||
explicit bigint(uint64_t n) { assign(n); }
|
explicit bigint(uint64_t n) { assign(n); }
|
||||||
~bigint() { FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); }
|
FMT_CONSTEXPR20 ~bigint() {
|
||||||
|
FMT_ASSERT(bigits_.capacity() <= bigits_capacity, "");
|
||||||
|
}
|
||||||
|
|
||||||
bigint(const bigint&) = delete;
|
bigint(const bigint&) = delete;
|
||||||
void operator=(const bigint&) = delete;
|
void operator=(const bigint&) = delete;
|
||||||
|
|
||||||
void assign(const bigint& other) {
|
FMT_CONSTEXPR20 void assign(const bigint& other) {
|
||||||
auto size = other.bigits_.size();
|
auto size = other.bigits_.size();
|
||||||
bigits_.resize(size);
|
bigits_.resize(size);
|
||||||
auto data = other.bigits_.data();
|
auto data = other.bigits_.data();
|
||||||
@ -445,7 +455,7 @@ class bigint {
|
|||||||
exp_ = other.exp_;
|
exp_ = other.exp_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void assign(uint64_t n) {
|
FMT_CONSTEXPR20 void assign(uint64_t n) {
|
||||||
size_t num_bigits = 0;
|
size_t num_bigits = 0;
|
||||||
do {
|
do {
|
||||||
bigits_[num_bigits++] = n & ~bigit(0);
|
bigits_[num_bigits++] = n & ~bigit(0);
|
||||||
@ -455,9 +465,11 @@ class bigint {
|
|||||||
exp_ = 0;
|
exp_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
|
FMT_CONSTEXPR20 int num_bigits() const {
|
||||||
|
return static_cast<int>(bigits_.size()) + exp_;
|
||||||
|
}
|
||||||
|
|
||||||
FMT_NOINLINE bigint& operator<<=(int shift) {
|
FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
|
||||||
FMT_ASSERT(shift >= 0, "");
|
FMT_ASSERT(shift >= 0, "");
|
||||||
exp_ += shift / bigit_bits;
|
exp_ += shift / bigit_bits;
|
||||||
shift %= bigit_bits;
|
shift %= bigit_bits;
|
||||||
@ -472,13 +484,13 @@ class bigint {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Int> bigint& operator*=(Int value) {
|
template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
|
||||||
FMT_ASSERT(value > 0, "");
|
FMT_ASSERT(value > 0, "");
|
||||||
multiply(uint32_or_64_or_128_t<Int>(value));
|
multiply(uint32_or_64_or_128_t<Int>(value));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend int compare(const bigint& lhs, const bigint& rhs) {
|
friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
|
||||||
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
|
||||||
if (num_lhs_bigits != num_rhs_bigits)
|
if (num_lhs_bigits != num_rhs_bigits)
|
||||||
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
|
||||||
@ -495,8 +507,8 @@ class bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns compare(lhs1 + lhs2, rhs).
|
// Returns compare(lhs1 + lhs2, rhs).
|
||||||
friend int add_compare(const bigint& lhs1, const bigint& lhs2,
|
friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
|
||||||
const bigint& rhs) {
|
const bigint& rhs) {
|
||||||
int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
|
int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits());
|
||||||
int num_rhs_bigits = rhs.num_bigits();
|
int num_rhs_bigits = rhs.num_bigits();
|
||||||
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
||||||
@ -519,7 +531,7 @@ class bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assigns pow(10, exp) to this bigint.
|
// Assigns pow(10, exp) to this bigint.
|
||||||
void assign_pow10(int exp) {
|
FMT_CONSTEXPR20 void assign_pow10(int exp) {
|
||||||
FMT_ASSERT(exp >= 0, "");
|
FMT_ASSERT(exp >= 0, "");
|
||||||
if (exp == 0) return assign(1);
|
if (exp == 0) return assign(1);
|
||||||
// Find the top bit.
|
// Find the top bit.
|
||||||
@ -538,7 +550,7 @@ class bigint {
|
|||||||
*this <<= exp; // Multiply by pow(2, exp) by shifting.
|
*this <<= exp; // Multiply by pow(2, exp) by shifting.
|
||||||
}
|
}
|
||||||
|
|
||||||
void square() {
|
FMT_CONSTEXPR20 void square() {
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
int num_result_bigits = 2 * num_bigits;
|
int num_result_bigits = 2 * num_bigits;
|
||||||
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
||||||
@ -569,7 +581,7 @@ class bigint {
|
|||||||
|
|
||||||
// If this bigint has a bigger exponent than other, adds trailing zero to make
|
// If this bigint has a bigger exponent than other, adds trailing zero to make
|
||||||
// exponents equal. This simplifies some operations such as subtraction.
|
// exponents equal. This simplifies some operations such as subtraction.
|
||||||
void align(const bigint& other) {
|
FMT_CONSTEXPR20 void align(const bigint& other) {
|
||||||
int exp_difference = exp_ - other.exp_;
|
int exp_difference = exp_ - other.exp_;
|
||||||
if (exp_difference <= 0) return;
|
if (exp_difference <= 0) return;
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
@ -582,7 +594,7 @@ class bigint {
|
|||||||
|
|
||||||
// Divides this bignum by divisor, assigning the remainder to this and
|
// Divides this bignum by divisor, assigning the remainder to this and
|
||||||
// returning the quotient.
|
// returning the quotient.
|
||||||
int divmod_assign(const bigint& divisor) {
|
FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
|
||||||
FMT_ASSERT(this != &divisor, "");
|
FMT_ASSERT(this != &divisor, "");
|
||||||
if (compare(*this, divisor) < 0) return 0;
|
if (compare(*this, divisor) < 0) return 0;
|
||||||
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
||||||
@ -602,8 +614,9 @@ enum class round_direction { unknown, up, down };
|
|||||||
// some number v and the error, returns whether v should be rounded up, down, or
|
// some number v and the error, returns whether v should be rounded up, down, or
|
||||||
// whether the rounding direction can't be determined due to error.
|
// whether the rounding direction can't be determined due to error.
|
||||||
// error should be less than divisor / 2.
|
// error should be less than divisor / 2.
|
||||||
inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder,
|
FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
|
||||||
uint64_t error) {
|
uint64_t remainder,
|
||||||
|
uint64_t error) {
|
||||||
FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
|
FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow.
|
||||||
FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
|
FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow.
|
||||||
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
|
||||||
@ -637,8 +650,10 @@ inline uint64_t power_of_10_64(int exp) {
|
|||||||
// error: the size of the region (lower, upper) outside of which numbers
|
// error: the size of the region (lower, upper) outside of which numbers
|
||||||
// definitely do not round to value (Delta in Grisu3).
|
// definitely do not round to value (Delta in Grisu3).
|
||||||
template <typename Handler>
|
template <typename Handler>
|
||||||
FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
FMT_INLINE FMT_CONSTEXPR digits::result grisu_gen_digits(fp value,
|
||||||
Handler& handler) {
|
uint64_t error,
|
||||||
|
int& exp,
|
||||||
|
Handler& handler) {
|
||||||
const fp one(1ULL << -value.e, value.e);
|
const fp one(1ULL << -value.e, value.e);
|
||||||
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
|
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
|
||||||
// zero because it contains a product of two 64-bit numbers with MSB set (due
|
// zero because it contains a product of two 64-bit numbers with MSB set (due
|
||||||
@ -724,8 +739,8 @@ struct fixed_handler {
|
|||||||
int exp10;
|
int exp10;
|
||||||
bool fixed;
|
bool fixed;
|
||||||
|
|
||||||
digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error,
|
FMT_CONSTEXPR digits::result on_start(uint64_t divisor, uint64_t remainder,
|
||||||
int& exp) {
|
uint64_t error, int& exp) {
|
||||||
// Non-fixed formats require at least one digit and no precision adjustment.
|
// Non-fixed formats require at least one digit and no precision adjustment.
|
||||||
if (!fixed) return digits::more;
|
if (!fixed) return digits::more;
|
||||||
// Adjust fixed precision by exponent because it is relative to decimal
|
// Adjust fixed precision by exponent because it is relative to decimal
|
||||||
@ -741,8 +756,9 @@ struct fixed_handler {
|
|||||||
return digits::done;
|
return digits::done;
|
||||||
}
|
}
|
||||||
|
|
||||||
digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
|
FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
|
||||||
uint64_t error, int, bool integral) {
|
uint64_t remainder, uint64_t error, int,
|
||||||
|
bool integral) {
|
||||||
FMT_ASSERT(remainder < divisor, "");
|
FMT_ASSERT(remainder < divisor, "");
|
||||||
buf[size++] = digit;
|
buf[size++] = digit;
|
||||||
if (!integral && error >= remainder) return digits::error;
|
if (!integral && error >= remainder) return digits::error;
|
||||||
|
Reference in New Issue
Block a user