From 208291205d664a87c79a0e3e2ec14657ab4e6870 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 2 Aug 2020 07:39:14 -0700 Subject: [PATCH] Optimize count_digits --- include/fmt/format-inl.h | 7 +++++++ include/fmt/format.h | 13 +++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index d3970d47..a4134336 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -263,6 +263,13 @@ const typename basic_data::digit_pair basic_data::digits[] = { template const char basic_data::hex_digits[] = "0123456789abcdef"; +template +const uint16_t basic_data::bsr2log10[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + #define FMT_POWERS_OF_10(factor) \ factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ (factor)*1000000, (factor)*10000000, (factor)*100000000, \ diff --git a/include/fmt/format.h b/include/fmt/format.h index 1e8382c0..444b7887 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -770,6 +770,8 @@ using uint32_or_64_or_128_t = // Static data is placed in this class template for the header-only config. template struct FMT_EXTERN_TEMPLATE_API basic_data { + // Maps the result of bsr(n) to ceil(log10(n)). + static const uint16_t bsr2log10[]; static const uint64_t powers_of_10_64[]; static const uint32_t zero_or_powers_of_10_32[]; static const uint64_t zero_or_powers_of_10_64[]; @@ -799,10 +801,9 @@ struct data : basic_data<> {}; // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case count_digits returns 1. inline int count_digits(uint64_t n) { - // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 - // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. - int t = ((FMT_BUILTIN_CLZLL(n | 1) ^ 63) + 1) * 1233 >> 12; - return t - (n < data::zero_or_powers_of_10_64[t]) + 1; + // https://github.com/fmtlib/format-benchmark/blob/master/digits10 + auto t = data::bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; + return t - (n < data::zero_or_powers_of_10_64[t - 1]); } #else // Fallback version of count_digits used when __builtin_clz is not available. @@ -859,8 +860,8 @@ template <> int count_digits<4>(detail::fallback_uintptr n); #ifdef FMT_BUILTIN_CLZ // Optional version of count_digits for better performance on 32-bit platforms. inline int count_digits(uint32_t n) { - int t = ((FMT_BUILTIN_CLZ(n | 1) ^ 31) + 1) * 1233 >> 12; - return t - (n < data::zero_or_powers_of_10_32[t]) + 1; + auto t = data::bsr2log10[FMT_BUILTIN_CLZ(n | 1) ^ 31]; + return t - (n < data::zero_or_powers_of_10_32[t - 1]); } #endif