diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 9a99b7a1..be5248da 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -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 ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void multiply(UInt value) { + const UInt lower = static_cast(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(carry); carry = - bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); + upper * bigits_[i] + (result >> bigit_bits) + (carry >> bigit_bits); bigits_[i] = static_cast(result); } while (carry != 0) { - bigits_.push_back(carry & mask); + bigits_.push_back(static_cast(carry)); carry >>= bigit_bits; } } + template ::value || + std::is_same::value)> + FMT_CONSTEXPR20 void assign(UInt n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = static_cast(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 FMT_CONSTEXPR20 void operator=(Int n) { + FMT_ASSERT(n > 0, ""); + assign(uint64_or_128_t(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 { - 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 + 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) { diff --git a/include/fmt/format.h b/include/fmt/format.h index ac0e5aab..5b5d0b92 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -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 using uint32_or_64_or_128_t = conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, uint32_t, - conditional_t() <= 64, uint64_t, uint128_opt>>; + conditional_t() <= 64, uint64_t, uint128_t>>; template -using uint64_or_128_t = - conditional_t() <= 64, uint64_t, uint128_opt>; +using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; #define FMT_POWERS_OF_10(factor) \ factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index e718150a..0276391b 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -24,9 +24,9 @@ static_assert(!std::is_copy_constructible::value, ""); static_assert(!std::is_copy_assignable::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()); bigmax *= max_value(); - EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax)); - bigmax.assign(max_value()); - bigmax *= max_value(); - EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax)); + EXPECT_EQ(fmt::to_string(bigmax), "fffffffe00000001"); + + const auto max64 = max_value(); + 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()); 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 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(); 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 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() / 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 -#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