From 0d51657278f88d2fb17b3ca65bfe72c406e3f888 Mon Sep 17 00:00:00 2001
From: Vladislav Shchapov <vladislav@shchapov.ru>
Date: Wed, 30 Nov 2022 22:32:32 +0500
Subject: [PATCH] Add countl_zero function

Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
---
 include/fmt/format.h     | 12 +++++++++++-
 test/format-impl-test.cc | 13 +++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/include/fmt/format.h b/include/fmt/format.h
index 414d11a370527..c60e78a0fbc6f 100644
--- a/include/fmt/format.h
+++ b/include/fmt/format.h
@@ -472,6 +472,16 @@ inline auto bit_cast(const From& from) -> To {
   return result;
 }
 
+FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int {
+#ifdef FMT_BUILTIN_CLZ
+  if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n);
+#endif
+  int lz = 0;
+  constexpr uint32_t msb_mask = 1u << (num_bits<uint32_t>() - 1);
+  for (; (n & msb_mask) == 0; n <<= 1) lz++;
+  return lz;
+}
+
 FMT_INLINE void assume(bool condition) {
   (void)condition;
 #if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
@@ -3149,7 +3159,7 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
   const auto leading_mask = carrier_uint(0xF) << leading_shift;
   const auto leading_xdigit =
       static_cast<uint32_t>((f.f & leading_mask) >> leading_shift);
-  if (leading_xdigit > 1) f.e -= (32 - FMT_BUILTIN_CLZ(leading_xdigit) - 1);
+  if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1);
 
   int print_xdigits = num_xdigits - 1;
   if (precision >= 0 && print_xdigits > precision) {
diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc
index 46486a6f4a8d0..fb109b56db661 100644
--- a/test/format-impl-test.cc
+++ b/test/format-impl-test.cc
@@ -353,6 +353,19 @@ TEST(format_impl_test, count_digits) {
   test_count_digits<uint64_t>();
 }
 
+// Tests fmt::detail::countl_zero for unsigned type UInt.
+template <typename UInt> void test_countl_zero() {
+  constexpr auto num_bits = fmt::detail::num_bits<UInt>();
+  UInt n = 1u;
+  for (int i = 1; i < num_bits - 1; i++) {
+    n <<= 1;
+    EXPECT_EQ(fmt::detail::countl_zero(n - 1), num_bits - i);
+    EXPECT_EQ(fmt::detail::countl_zero(n), num_bits - i - 1);
+  }
+}
+
+TEST(format_impl_test, countl_zero) { test_countl_zero<uint32_t>(); }
+
 #if FMT_USE_FLOAT128
 TEST(format_impl_test, write_float128) {
   auto s = std::string();