Remove test-only code from the headers and move equal2 to format-inl.h

Its only user is vformat_to, which needs only the char overload.
This commit is contained in:
Victor Zverovich 2026-11-07 21:00:48 -08:00
parent 32777676cf
commit 41f2838233
3 changed files with 36 additions and 103 deletions

View File

@ -188,11 +188,6 @@ FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args)
namespace detail {
template <typename F>
inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
return x.f == y.f && x.e == y.e;
}
// Compilers should be able to optimize this into the ror instruction.
FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
r &= 31;
@ -1397,32 +1392,6 @@ small_divisor_case_label:
} // namespace dragonbox
} // namespace detail
template <> struct formatter<detail::bigint> {
FMT_CONSTEXPR auto parse(format_parse_context& ctx)
-> format_parse_context::iterator {
return ctx.begin();
}
auto format(const detail::bigint& n, format_context& ctx) const
-> format_context::iterator {
auto out = ctx.out();
bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1u];
if (first) {
out = fmt::format_to(out, FMT_STRING("{:x}"), value);
first = false;
continue;
}
out = fmt::format_to(out, FMT_STRING("{:08x}"), value);
}
if (n.exp_ > 0)
out = fmt::format_to(out, FMT_STRING("p{}"),
n.exp_ * detail::bigint::bigit_bits);
return out;
}
};
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
for_each_codepoint(s, [this](uint32_t cp, string_view) {
if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8"));
@ -1464,6 +1433,11 @@ FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
namespace detail {
// Compares two characters for equality.
inline auto equal2(const char* lhs, const char* rhs) -> bool {
return memcmp(lhs, rhs, 2) == 0;
}
FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
locale_ref loc) {
auto out = appender(buf);

View File

@ -1389,14 +1389,6 @@ extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
FMT_END_EXPORT
#endif // FMT_HEADER_ONLY
// Compares two characters for equality.
template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
}
inline auto equal2(const char* lhs, const char* rhs) -> bool {
return memcmp(lhs, rhs, 2) == 0;
}
// Writes a two-digit value to out.
template <typename Char>
FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) {
@ -1842,11 +1834,7 @@ template <typename F> struct basic_fp {
F f;
int e;
static constexpr int num_significand_bits =
static_cast<int>(sizeof(F) * num_bits<unsigned char>());
constexpr basic_fp() : f(0), e(0) {}
constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
// Constructs fp from an IEEE754 floating-point number.
template <typename Float> FMT_CONSTEXPR basic_fp(Float n) { assign(n); }
@ -1884,48 +1872,6 @@ template <typename F> struct basic_fp {
}
};
using fp = basic_fp<ullong>;
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT = 0, typename F>
FMT_CONSTEXPR auto normalize(basic_fp<F> value) -> basic_fp<F> {
// Handle subnormals.
const auto implicit_bit = F(1) << num_significand_bits<double>();
const auto shifted_implicit_bit = implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1;
--value.e;
}
// Subtract 1 to account for hidden bit.
const auto offset = basic_fp<F>::num_significand_bits -
num_significand_bits<double>() - SHIFT - 1;
value.f <<= offset;
value.e -= offset;
return value;
}
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
FMT_CONSTEXPR inline auto multiply(uint64_t lhs, uint64_t rhs) -> uint64_t {
#if FMT_USE_INT128
auto product = static_cast<uint128_t>(lhs) * rhs;
auto f = static_cast<uint64_t>(product >> 64);
return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
#else
// Multiply 32-bit parts of significands.
uint64_t mask = (1ULL << 32) - 1;
uint64_t a = lhs >> 32, b = lhs & mask;
uint64_t c = rhs >> 32, d = rhs & mask;
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
// Compute mid 64-bit of result and round.
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
#endif
}
FMT_CONSTEXPR inline auto operator*(fp x, fp y) -> fp {
return {multiply(x.f, y.f), x.e + y.e + 64};
}
template <typename T, bool doublish = num_bits<T>() == num_bits<double>()>
using convert_float_result =
conditional_t<std::is_same<T, float>::value || doublish, double, T>;

View File

@ -15,9 +15,36 @@
#include "gmock/gmock.h"
using fmt::detail::bigint;
using fmt::detail::fp;
using fmt::detail::max_value;
FMT_BEGIN_NAMESPACE
template <> struct formatter<detail::bigint> {
FMT_CONSTEXPR auto parse(format_parse_context& ctx)
-> format_parse_context::iterator {
return ctx.begin();
}
auto format(const detail::bigint& n, format_context& ctx) const
-> format_context::iterator {
auto out = ctx.out();
bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1u];
if (first) {
out = fmt::format_to(out, FMT_STRING("{:x}"), value);
first = false;
continue;
}
out = fmt::format_to(out, FMT_STRING("{:08x}"), value);
}
if (n.exp_ > 0)
out = fmt::format_to(out, FMT_STRING("p{}"),
n.exp_ * detail::bigint::bigit_bits);
return out;
}
};
FMT_END_NAMESPACE
static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, "");
@ -171,29 +198,15 @@ template <bool is_iec559> void run_double_tests() {
template <> void run_double_tests<true>() {
// Construct from double.
EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
auto v = fmt::detail::basic_fp<uint64_t>(1.23);
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -52);
}
TEST(fp_test, double_tests) {
run_double_tests<std::numeric_limits<double>::is_iec559>();
}
TEST(fp_test, normalize) {
const auto v = fp(0xbeef, 42);
auto normalized = normalize(v);
EXPECT_EQ(normalized.f, 0xbeef000000000000);
EXPECT_EQ(normalized.e, -6);
}
TEST(fp_test, multiply) {
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
EXPECT_EQ(v.f, 123u * 56u);
EXPECT_EQ(v.e, 4 + 7 + 64);
v = fp(123ULL << 32, 4) * fp(567ULL << 31, 8);
EXPECT_EQ(v.f, (123 * 567 + 1u) / 2);
EXPECT_EQ(v.e, 4 + 8 + 64);
}
TEST(fp_test, dragonbox_max_k) {
using fmt::detail::dragonbox::floor_log10_pow2;
using float_info = fmt::detail::dragonbox::float_info<float>;