diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index ad113ca7..a2f47689 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -473,14 +473,14 @@ class bigint { // A bigint is stored as an array of bigits (big digits), with bigit at index // 0 being the least significant one. using bigit = uint32_t; + using double_bigit = uint64_t; basic_memory_buffer bigits_; static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; friend struct formatter; - public: - explicit bigint(uint64_t n) { + void assign(uint64_t n) { int num_bigits = bits::value / bigit_bits; bigits_.resize(num_bigits); for (int i = 0; i < num_bigits; ++i) { @@ -489,9 +489,37 @@ class bigint { } } + public: + bigint() {} + explicit bigint(uint64_t n) { assign(n); } + + // Assigns pow(10, exp) to this bigint. + void assign_pow10(int exp) { + assert(exp >= 0); + if (exp == 0) return assign(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); + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + bigint(const bigint&) = delete; void operator=(const bigint&) = delete; + void square() { + // TODO + } + bigint& operator<<=(int shift) { assert(shift >= 0 && shift < bigit_bits); bigit carry = 0; @@ -503,6 +531,23 @@ class bigint { if (carry != 0) bigits_.push_back(carry); return *this; } + + bigint& operator*=(uint32_t value) { + assert(value > 0); + // Verify that the computation doesn't overflow. + constexpr double_bigit max32 = (std::numeric_limits::max)(); + constexpr double_bigit max64 = (std::numeric_limits::max)(); + static_assert(max32 * max32 <= max64 - max32, ""); + bigit carry = 0; + const double_bigit wide_value = value; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * wide_value + carry; + bigits_[i] = static_cast(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + return *this; + } }; enum round_direction { unknown, up, down }; @@ -725,8 +770,9 @@ template struct grisu_shortest_handler { } }; -FMT_API void fallback_format(const fp& value) { +FMT_API void fallback_format(const fp& value, int exp10) { (void)value; // TODO + (void)exp10; } template & buf, int precision, result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler); size = handler.size; if (result == digits::error) { - fallback_format(fp_value); + fallback_format(fp_value, exp - cached_exp10); return false; } } else { diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index afcc86f8..37e5d93c 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -29,6 +29,7 @@ static_assert(!std::is_copy_constructible::value, ""); static_assert(!std::is_copy_assignable::value, ""); TEST(BigIntTest, Construct) { + EXPECT_EQ("", fmt::format("{}", bigint())); EXPECT_EQ("42", fmt::format("{}", bigint(0x42))); EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0))); } @@ -43,6 +44,20 @@ TEST(BigIntTest, ShiftLeft) { EXPECT_EQ("108000000", fmt::format("{}", n)); } +TEST(BigIntTest, Multiply) { + bigint n(0x42); + n *= 1; + EXPECT_EQ("42", fmt::format("{}", n)); + n *= 2; + EXPECT_EQ("84", fmt::format("{}", n)); + n *= 0x12345678; + EXPECT_EQ("962fc95e0", fmt::format("{}", n)); + auto max = (std::numeric_limits::max)(); + bigint bigmax(max); + bigmax *= max; + EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax)); +} + template void test_construct_from_double() { fmt::print("warning: double is not IEC559, skipping FP tests\n"); }