diff --git a/src/utils/math.hpp b/src/utils/math.hpp new file mode 100644 index 000000000..c686fdaff --- /dev/null +++ b/src/utils/math.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <cstdint> +#include <experimental/type_traits> + +#include <glog/logging.h> + +namespace utils { + +static_assert( + std::experimental::is_same_v<uint64_t, unsigned long>, + "utils::Log requires uint64_t to be implemented as unsigned long."); + +/// This function computes the log2 function on integer types. It is faster than +/// the cmath `log2` function because it doesn't use floating point values for +/// calculation. +inline uint64_t Log2(uint64_t val) { + // The `clz` function is undefined when the passed value is 0 and the value of + // `log` is `-inf` so we special case it here. + if (val == 0) return 0; + // clzl = count leading zeros from long + // ^ ^ ^ ^ + int ret = __builtin_clzl(val); + return 64UL - static_cast<uint64_t>(ret) - 1UL; +} +} // namespace utils diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 0c1654d64..c603b6f4d 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -323,6 +323,9 @@ target_link_libraries(${test_prefix}utils_exceptions mg-utils) add_unit_test(utils_executor.cpp) target_link_libraries(${test_prefix}utils_executor mg-utils) +add_unit_test(utils_math.cpp) +target_link_libraries(${test_prefix}utils_math mg-utils) + add_unit_test(utils_on_scope_exit.cpp) target_link_libraries(${test_prefix}utils_on_scope_exit mg-utils) diff --git a/tests/unit/utils_math.cpp b/tests/unit/utils_math.cpp new file mode 100644 index 000000000..7e74cf3ae --- /dev/null +++ b/tests/unit/utils_math.cpp @@ -0,0 +1,12 @@ +#include <cmath> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "utils/math.hpp" + +TEST(UtilsMath, Log2) { + for (uint64_t i = 1; i < 1000000; ++i) { + ASSERT_EQ(utils::Log2(i), static_cast<uint64_t>(log2(i))); + } +}