From 418a5691eae02493b4296035d19db8968e4b1103 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 21 Nov 2023 19:39:18 +0100 Subject: [PATCH] Library: relocate integer-log2 and make it constexpr This highly optimised function was introduced about one year ago for handling of denomals with rational values (fractions), as an interim solution until we'll switch to C++20. Since this function uses an unrolled loop and basically just does a logarithmic search for the highest set bit, it can just be declared constexpr. Moreover, it is now relocated into one of the basic utility headers Remark: the primary "competitor" is the ilogb(double), which can exploit hardware acceleration. For 64bit integers, the ilog2() is only marginally faster according to my own repeated invocation benchmarks. --- src/lib/rational.hpp | 49 ++---------------------------------------- src/lib/util-quant.hpp | 47 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/lib/rational.hpp b/src/lib/rational.hpp index 909c76539..0c94b7d3b 100644 --- a/src/lib/rational.hpp +++ b/src/lib/rational.hpp @@ -68,13 +68,13 @@ #define LIB_RATIONAL_H +#include "lib/util-quant.hpp" + #include #include #include #include -#include "lib/util-quant.hpp" - namespace util { @@ -87,51 +87,6 @@ namespace util { using uint = unsigned int; - /** - * Integral binary logarithm (disregarding fractional part) - * @return index of the largest bit set in `num`; -1 for `num==0` - * @todo C++20 will provide `std::bit_width(i)` — run a microbenchmark! - * @remark The implementation uses an unrolled loop to break down the given number - * in a logarithmic search, subtracting away the larger powers of 2 first. - * Explained 10/2021 by user «[ToddLehman]» in this [stackoverflow]. - * @note Microbenchmarks indicate that this function and `std::ilogb(double)` perform - * in the same order of magnitude (which is surprising). This function gets - * slightly faster for smaller data types. The naive bitshift-count implementation - * is always significantly slower (8 times for int64_t, 1.6 times for int8_t) - * @see Rational_test::verify_intLog2() - * @see ZoomWindow_test - * - * [ToddLehman]: https://stackoverflow.com/users/267551/todd-lehman - * [stackoverflow]: https://stackoverflow.com/a/24748637 "How to do an integer log2()" - */ - template - inline int - ilog2 (I num) - { - if (num <= 0) - return -1; - const I MAX_POW = sizeof(I)*CHAR_BIT - 1; - int logB{0}; - auto remove_power = [&](I pow) - { - if (pow > MAX_POW) return; - if (num >= I{1} << pow) - { - logB += pow; - num >>= pow; - } - }; - remove_power(32); - remove_power(16); - remove_power (8); - remove_power (4); - remove_power (2); - remove_power (1); - - return logB; - } - - inline bool can_represent_Product (int64_t a, int64_t b) { diff --git a/src/lib/util-quant.hpp b/src/lib/util-quant.hpp index 1f5d94cd0..7da68ffd7 100644 --- a/src/lib/util-quant.hpp +++ b/src/lib/util-quant.hpp @@ -30,6 +30,7 @@ #define LIB_UTIL_QUANT_H #include +#include #include #include @@ -155,5 +156,51 @@ namespace util { } + + /** + * Integral binary logarithm (disregarding fractional part) + * @return index of the largest bit set in `num`; -1 for `num==0` + * @todo C++20 will provide `std::bit_width(i)` — run a microbenchmark! + * @remark The implementation uses an unrolled loop to break down the given number + * in a logarithmic search, subtracting away the larger powers of 2 first. + * Explained 10/2021 by user «[ToddLehman]» in this [stackoverflow]. + * @note Microbenchmarks indicate that this function and `std::ilogb(double)` perform + * in the same order of magnitude (which is surprising). This function gets + * slightly faster for smaller data types. The naive bitshift-count implementation + * is always significantly slower (8 times for int64_t, 1.6 times for int8_t) + * @see Rational_test::verify_intLog2() + * @see ZoomWindow_test + * + * [ToddLehman]: https://stackoverflow.com/users/267551/todd-lehman + * [stackoverflow]: https://stackoverflow.com/a/24748637 "How to do an integer log2()" + */ + template + inline constexpr int + ilog2 (I num) + { + if (num <= 0) + return -1; + const I MAX_POW = sizeof(I)*CHAR_BIT - 1; + int logB{0}; + auto remove_power = [&](I pow) constexpr + { + if (pow > MAX_POW) return; + if (num >= I{1} << pow) + { + logB += pow; + num >>= pow; + } + }; + remove_power(32); + remove_power(16); + remove_power (8); + remove_power (4); + remove_power (2); + remove_power (1); + + return logB; + } + + } // namespace util #endif /*UTIL_QUANT_H*/