mirror of
https://github.com/fmtlib/fmt.git
synced 2025-02-03 20:54:08 +00:00
Add 128-bit operations to bigint
This commit is contained in:
parent
ef54f9aa38
commit
b4dc7a1d34
@ -368,23 +368,36 @@ class bigint {
|
||||
if (carry != 0) bigits_.push_back(carry);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void multiply(uint64_t value) {
|
||||
const bigit mask = ~bigit(0);
|
||||
const double_bigit lower = value & mask;
|
||||
const double_bigit upper = value >> bigit_bits;
|
||||
double_bigit carry = 0;
|
||||
template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
|
||||
std::is_same<UInt, uint128_t>::value)>
|
||||
FMT_CONSTEXPR20 void multiply(UInt value) {
|
||||
const UInt lower = static_cast<bigit>(value);
|
||||
const UInt upper = value >> bigit_bits;
|
||||
UInt carry = 0;
|
||||
for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
|
||||
double_bigit result = bigits_[i] * lower + (carry & mask);
|
||||
UInt result = lower * bigits_[i] + static_cast<bigit>(carry);
|
||||
carry =
|
||||
bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits);
|
||||
upper * bigits_[i] + (result >> bigit_bits) + (carry >> bigit_bits);
|
||||
bigits_[i] = static_cast<bigit>(result);
|
||||
}
|
||||
while (carry != 0) {
|
||||
bigits_.push_back(carry & mask);
|
||||
bigits_.push_back(static_cast<bigit>(carry));
|
||||
carry >>= bigit_bits;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
|
||||
std::is_same<UInt, uint128_t>::value)>
|
||||
FMT_CONSTEXPR20 void assign(UInt n) {
|
||||
size_t num_bigits = 0;
|
||||
do {
|
||||
bigits_[num_bigits++] = static_cast<bigit>(n);
|
||||
n >>= bigit_bits;
|
||||
} while (n != 0);
|
||||
bigits_.resize(num_bigits);
|
||||
exp_ = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR20 bigint() : exp_(0) {}
|
||||
explicit bigint(uint64_t n) { assign(n); }
|
||||
@ -400,14 +413,9 @@ class bigint {
|
||||
exp_ = other.exp_;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 void assign(uint64_t n) {
|
||||
size_t num_bigits = 0;
|
||||
do {
|
||||
bigits_[num_bigits++] = n & ~bigit(0);
|
||||
n >>= bigit_bits;
|
||||
} while (n != 0);
|
||||
bigits_.resize(num_bigits);
|
||||
exp_ = 0;
|
||||
template <typename Int> FMT_CONSTEXPR20 void operator=(Int n) {
|
||||
FMT_ASSERT(n > 0, "");
|
||||
assign(uint64_or_128_t<Int>(n));
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR20 int num_bigits() const {
|
||||
@ -478,14 +486,14 @@ class bigint {
|
||||
// Assigns pow(10, exp) to this bigint.
|
||||
FMT_CONSTEXPR20 void assign_pow10(int exp) {
|
||||
FMT_ASSERT(exp >= 0, "");
|
||||
if (exp == 0) return assign(1);
|
||||
if (exp == 0) return *this = 1;
|
||||
// Find the top bit.
|
||||
int bitmask = 1;
|
||||
while (exp >= bitmask) bitmask <<= 1;
|
||||
bitmask >>= 1;
|
||||
// pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
|
||||
// repeated squaring and multiplication.
|
||||
assign(5);
|
||||
*this = 5;
|
||||
bitmask >>= 1;
|
||||
while (bitmask != 0) {
|
||||
square();
|
||||
@ -2061,12 +2069,12 @@ FMT_CONSTEXPR20 inline void format_dragon(fp value, unsigned flags,
|
||||
bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0;
|
||||
int shift = is_predecessor_closer ? 2 : 1;
|
||||
if (value.e >= 0) {
|
||||
numerator.assign(value.f);
|
||||
numerator = value.f;
|
||||
numerator <<= value.e + shift;
|
||||
lower.assign(1);
|
||||
lower = 1;
|
||||
lower <<= value.e;
|
||||
if (is_predecessor_closer) {
|
||||
upper_store.assign(1);
|
||||
upper_store = 1;
|
||||
upper_store <<= value.e + 1;
|
||||
upper = &upper_store;
|
||||
}
|
||||
@ -2082,16 +2090,16 @@ FMT_CONSTEXPR20 inline void format_dragon(fp value, unsigned flags,
|
||||
}
|
||||
numerator *= value.f;
|
||||
numerator <<= shift;
|
||||
denominator.assign(1);
|
||||
denominator = 1;
|
||||
denominator <<= shift - value.e;
|
||||
} else {
|
||||
numerator.assign(value.f);
|
||||
numerator = value.f;
|
||||
numerator <<= shift;
|
||||
denominator.assign_pow10(exp10);
|
||||
denominator <<= shift - value.e;
|
||||
lower.assign(1);
|
||||
lower = 1;
|
||||
if (is_predecessor_closer) {
|
||||
upper_store.assign(1ULL << 1);
|
||||
upper_store = 1ULL << 1;
|
||||
upper = &upper_store;
|
||||
}
|
||||
}
|
||||
@ -2278,13 +2286,14 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision,
|
||||
} // namespace detail
|
||||
|
||||
template <> struct formatter<detail::bigint> {
|
||||
FMT_CONSTEXPR format_parse_context::iterator parse(
|
||||
format_parse_context& ctx) {
|
||||
FMT_CONSTEXPR auto parse(format_parse_context& ctx)
|
||||
-> format_parse_context::iterator {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
format_context::iterator format(const detail::bigint& n,
|
||||
format_context& ctx) {
|
||||
template <typename FormatContext>
|
||||
auto format(const detail::bigint& n, FormatContext& ctx) const ->
|
||||
typename FormatContext::iterator {
|
||||
auto out = ctx.out();
|
||||
bool first = true;
|
||||
for (auto i = n.bigits_.size(); i > 0; --i) {
|
||||
|
@ -339,6 +339,10 @@ class uint128_fallback {
|
||||
const uint128_fallback& rhs) -> bool {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
friend auto operator>(const uint128_fallback& lhs,
|
||||
const uint128_fallback& rhs) -> bool {
|
||||
return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
|
||||
}
|
||||
friend auto operator|(const uint128_fallback& lhs,
|
||||
const uint128_fallback& rhs) -> uint128_fallback {
|
||||
return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
|
||||
@ -347,6 +351,16 @@ class uint128_fallback {
|
||||
const uint128_fallback& rhs) -> uint128_fallback {
|
||||
return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
|
||||
}
|
||||
friend auto operator+(const uint128_fallback& lhs,
|
||||
const uint128_fallback& rhs) -> uint128_fallback {
|
||||
auto result = uint128_fallback(lhs);
|
||||
result += rhs;
|
||||
return result;
|
||||
}
|
||||
friend auto operator*(const uint128_fallback&, uint32_t) -> uint128_fallback {
|
||||
FMT_ASSERT(false, "");
|
||||
return {};
|
||||
}
|
||||
friend auto operator-(const uint128_fallback& lhs, uint64_t rhs)
|
||||
-> uint128_fallback {
|
||||
FMT_ASSERT(lhs.lo_ >= rhs, "");
|
||||
@ -973,10 +987,9 @@ template <typename T>
|
||||
using uint32_or_64_or_128_t =
|
||||
conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
|
||||
uint32_t,
|
||||
conditional_t<num_bits<T>() <= 64, uint64_t, uint128_opt>>;
|
||||
conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
|
||||
template <typename T>
|
||||
using uint64_or_128_t =
|
||||
conditional_t<num_bits<T>() <= 64, uint64_t, uint128_opt>;
|
||||
using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
|
||||
|
||||
#define FMT_POWERS_OF_10(factor) \
|
||||
factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
|
||||
|
@ -24,9 +24,9 @@ static_assert(!std::is_copy_constructible<bigint>::value, "");
|
||||
static_assert(!std::is_copy_assignable<bigint>::value, "");
|
||||
|
||||
TEST(bigint_test, construct) {
|
||||
EXPECT_EQ("", fmt::format("{}", bigint()));
|
||||
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
|
||||
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
|
||||
EXPECT_EQ(fmt::to_string(bigint()), "");
|
||||
EXPECT_EQ(fmt::to_string(bigint(0x42)), "42");
|
||||
EXPECT_EQ(fmt::to_string(bigint(0x123456789abcedf0)), "123456789abcedf0");
|
||||
}
|
||||
|
||||
TEST(bigint_test, compare) {
|
||||
@ -72,46 +72,56 @@ TEST(bigint_test, add_compare) {
|
||||
TEST(bigint_test, shift_left) {
|
||||
bigint n(0x42);
|
||||
n <<= 0;
|
||||
EXPECT_EQ("42", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "42");
|
||||
n <<= 1;
|
||||
EXPECT_EQ("84", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "84");
|
||||
n <<= 25;
|
||||
EXPECT_EQ("108000000", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "108000000");
|
||||
}
|
||||
|
||||
TEST(bigint_test, multiply) {
|
||||
bigint n(0x42);
|
||||
EXPECT_THROW(n *= 0, assertion_failure);
|
||||
n *= 1;
|
||||
EXPECT_EQ("42", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "42");
|
||||
|
||||
n *= 2;
|
||||
EXPECT_EQ("84", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "84");
|
||||
n *= 0x12345678;
|
||||
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "962fc95e0");
|
||||
|
||||
bigint bigmax(max_value<uint32_t>());
|
||||
bigmax *= max_value<uint32_t>();
|
||||
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
|
||||
bigmax.assign(max_value<uint64_t>());
|
||||
bigmax *= max_value<uint64_t>();
|
||||
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
|
||||
EXPECT_EQ(fmt::to_string(bigmax), "fffffffe00000001");
|
||||
|
||||
const auto max64 = max_value<uint64_t>();
|
||||
bigmax = max64;
|
||||
bigmax *= max64;
|
||||
EXPECT_EQ(fmt::to_string(bigmax), "fffffffffffffffe0000000000000001");
|
||||
|
||||
const auto max128 = (fmt::detail::uint128_t(max64) << 64) | max64;
|
||||
bigmax = max128;
|
||||
// bigmax *= max128;
|
||||
// EXPECT_EQ(fmt::to_string(bigmax),
|
||||
// "fffffffffffffffffffffffffffffffe00000000000000000000000000000001");
|
||||
}
|
||||
|
||||
TEST(bigint_test, square) {
|
||||
bigint n0(0);
|
||||
n0.square();
|
||||
EXPECT_EQ("0", fmt::format("{}", n0));
|
||||
EXPECT_EQ(fmt::to_string(n0), "0");
|
||||
bigint n1(0x100);
|
||||
n1.square();
|
||||
EXPECT_EQ("10000", fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1), "10000");
|
||||
bigint n2(0xfffffffff);
|
||||
n2.square();
|
||||
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2));
|
||||
EXPECT_EQ(fmt::to_string(n2), "ffffffffe000000001");
|
||||
bigint n3(max_value<uint64_t>());
|
||||
n3.square();
|
||||
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3));
|
||||
EXPECT_EQ(fmt::to_string(n3), "fffffffffffffffe0000000000000001");
|
||||
bigint n4;
|
||||
n4.assign_pow10(10);
|
||||
EXPECT_EQ("2540be400", fmt::format("{}", n4));
|
||||
EXPECT_EQ(fmt::to_string(n4), "2540be400");
|
||||
}
|
||||
|
||||
TEST(bigint_test, divmod_assign_zero_divisor) {
|
||||
@ -133,8 +143,8 @@ TEST(bigint_test, divmod_assign_unaligned) {
|
||||
n2.assign_pow10(100);
|
||||
int result = n1.divmod_assign(n2);
|
||||
EXPECT_EQ(result, 9406);
|
||||
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96",
|
||||
fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1),
|
||||
"10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96");
|
||||
}
|
||||
|
||||
TEST(bigint_test, divmod_assign) {
|
||||
@ -142,19 +152,19 @@ TEST(bigint_test, divmod_assign) {
|
||||
bigint n1(100);
|
||||
int result = n1.divmod_assign(bigint(10));
|
||||
EXPECT_EQ(result, 10);
|
||||
EXPECT_EQ("0", fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1), "0");
|
||||
// pow(10, 100) / (42 << 320):
|
||||
n1.assign_pow10(100);
|
||||
result = n1.divmod_assign(bigint(42) <<= 320);
|
||||
EXPECT_EQ(result, 111);
|
||||
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96",
|
||||
fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1),
|
||||
"13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96");
|
||||
// 42 / 100:
|
||||
bigint n2(42);
|
||||
n1.assign_pow10(2);
|
||||
result = n2.divmod_assign(n1);
|
||||
EXPECT_EQ(result, 0);
|
||||
EXPECT_EQ("2a", fmt::format("{}", n2));
|
||||
EXPECT_EQ(fmt::to_string(n2), "2a");
|
||||
}
|
||||
|
||||
template <bool is_iec559> void run_double_tests() {
|
||||
@ -173,8 +183,8 @@ TEST(fp_test, double_tests) {
|
||||
TEST(fp_test, normalize) {
|
||||
const auto v = fp(0xbeef, 42);
|
||||
auto normalized = normalize(v);
|
||||
EXPECT_EQ(0xbeef000000000000, normalized.f);
|
||||
EXPECT_EQ(-6, normalized.e);
|
||||
EXPECT_EQ(normalized.f, 0xbeef000000000000);
|
||||
EXPECT_EQ(normalized.e, -6);
|
||||
}
|
||||
|
||||
TEST(fp_test, multiply) {
|
||||
@ -200,8 +210,8 @@ TEST(fp_test, get_cached_power) {
|
||||
cache <<= power.e;
|
||||
exact.align(cache);
|
||||
cache.align(exact);
|
||||
auto exact_str = fmt::format("{}", exact);
|
||||
auto cache_str = fmt::format("{}", cache);
|
||||
auto exact_str = fmt::to_string(exact);
|
||||
auto cache_str = fmt::to_string(cache);
|
||||
EXPECT_EQ(exact_str.size(), cache_str.size());
|
||||
EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15));
|
||||
int diff = cache_str[15] - exact_str[15];
|
||||
@ -212,11 +222,11 @@ TEST(fp_test, get_cached_power) {
|
||||
} else {
|
||||
cache.assign_pow10(-dec_exp);
|
||||
cache *= power.f + 1; // Inexact check.
|
||||
exact.assign(1);
|
||||
exact = 1;
|
||||
exact <<= -power.e;
|
||||
exact.align(cache);
|
||||
auto exact_str = fmt::format("{}", exact);
|
||||
auto cache_str = fmt::format("{}", cache);
|
||||
auto exact_str = fmt::to_string(exact);
|
||||
auto cache_str = fmt::to_string(cache);
|
||||
EXPECT_EQ(exact_str.size(), cache_str.size());
|
||||
EXPECT_EQ(exact_str.substr(0, 16), cache_str.substr(0, 16));
|
||||
}
|
||||
@ -242,25 +252,25 @@ TEST(fp_test, dragonbox_max_k) {
|
||||
TEST(fp_test, get_round_direction) {
|
||||
using fmt::detail::get_round_direction;
|
||||
using fmt::detail::round_direction;
|
||||
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0));
|
||||
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10));
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(100, 60, 10));
|
||||
EXPECT_EQ(get_round_direction(100, 50, 0), round_direction::down);
|
||||
EXPECT_EQ(get_round_direction(100, 51, 0), round_direction::up);
|
||||
EXPECT_EQ(get_round_direction(100, 40, 10), round_direction::down);
|
||||
EXPECT_EQ(get_round_direction(100, 60, 10), round_direction::up);
|
||||
for (size_t i = 41; i < 60; ++i)
|
||||
EXPECT_EQ(round_direction::unknown, get_round_direction(100, i, 10));
|
||||
EXPECT_EQ(get_round_direction(100, i, 10), round_direction::unknown);
|
||||
uint64_t max = max_value<uint64_t>();
|
||||
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure);
|
||||
EXPECT_THROW(get_round_direction(100, 0, 100), assertion_failure);
|
||||
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
|
||||
// Check that remainder + error doesn't overflow.
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 2));
|
||||
EXPECT_EQ(get_round_direction(max, max - 1, 2), round_direction::up);
|
||||
// Check that 2 * (remainder + error) doesn't overflow.
|
||||
EXPECT_EQ(round_direction::unknown,
|
||||
get_round_direction(max, max / 2 + 1, max / 2));
|
||||
EXPECT_EQ(get_round_direction(max, max / 2 + 1, max / 2),
|
||||
round_direction::unknown);
|
||||
// Check that remainder - error doesn't overflow.
|
||||
EXPECT_EQ(round_direction::unknown, get_round_direction(100, 40, 41));
|
||||
EXPECT_EQ(get_round_direction(100, 40, 41), round_direction::unknown);
|
||||
// Check that 2 * (remainder - error) doesn't overflow.
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 1));
|
||||
EXPECT_EQ(get_round_direction(max, max - 1, 1), round_direction::up);
|
||||
}
|
||||
|
||||
TEST(fp_test, fixed_handler) {
|
||||
@ -283,20 +293,20 @@ TEST(fp_test, fixed_handler) {
|
||||
}
|
||||
|
||||
TEST(fp_test, grisu_format_compiles_with_on_ieee_double) {
|
||||
fmt::memory_buffer buf;
|
||||
auto buf = fmt::memory_buffer();
|
||||
format_float(0.42, -1, fmt::detail::float_specs(), buf);
|
||||
}
|
||||
|
||||
TEST(format_impl_test, format_error_code) {
|
||||
std::string msg = "error 42", sep = ": ";
|
||||
{
|
||||
fmt::memory_buffer buffer;
|
||||
auto buffer = fmt::memory_buffer();
|
||||
format_to(fmt::appender(buffer), "garbage");
|
||||
fmt::detail::format_error_code(buffer, 42, "test");
|
||||
EXPECT_EQ("test: " + msg, to_string(buffer));
|
||||
EXPECT_EQ(to_string(buffer), "test: " + msg);
|
||||
}
|
||||
{
|
||||
fmt::memory_buffer buffer;
|
||||
auto buffer = fmt::memory_buffer();
|
||||
auto prefix =
|
||||
std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
|
||||
fmt::detail::format_error_code(buffer, 42, prefix);
|
||||
@ -317,7 +327,7 @@ TEST(format_impl_test, format_error_code) {
|
||||
// Test with a message that doesn't fit into the buffer.
|
||||
prefix += 'x';
|
||||
fmt::detail::format_error_code(buffer, codes[i], prefix);
|
||||
EXPECT_EQ(msg, to_string(buffer));
|
||||
EXPECT_EQ(to_string(buffer), msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,8 +343,8 @@ template <typename Int> void test_count_digits() {
|
||||
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
|
||||
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
|
||||
n *= 10;
|
||||
EXPECT_EQ(i, fmt::detail::count_digits(n - 1));
|
||||
EXPECT_EQ(i + 1, fmt::detail::count_digits(n));
|
||||
EXPECT_EQ(fmt::detail::count_digits(n - 1), i);
|
||||
EXPECT_EQ(fmt::detail::count_digits(n), i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,11 +355,9 @@ TEST(format_impl_test, count_digits) {
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
TEST(format_impl_test, write_console_signature) {
|
||||
decltype(WriteConsoleW)* p = fmt::detail::WriteConsoleW;
|
||||
decltype(::WriteConsoleW)* p = fmt::detail::WriteConsoleW;
|
||||
(void)p;
|
||||
}
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user