From d0d81e470ec07745efffbb81becfb4bfca89bb5b Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Mon, 13 May 2024 18:57:38 +0100 Subject: [PATCH] AK: Fix off by one error in integral `ceil_log2()` Previously, certain values of `ceil_log2(x)` would be 1 smaller than `ceil(log2(x))`. --- AK/IntegralMath.h | 6 ++---- Tests/AK/TestIntegerMath.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/AK/IntegralMath.h b/AK/IntegralMath.h index c1051fed843..ebd279768fa 100644 --- a/AK/IntegralMath.h +++ b/AK/IntegralMath.h @@ -27,12 +27,10 @@ constexpr T log2(T x) template constexpr T ceil_log2(T x) { - if (!x) + if (x <= 1) return 0; - T log = AK::log2(x); - log += (x & ((((T)1) << (log - 1)) - 1)) != 0; - return log; + return AK::log2(x - 1) + 1; } template diff --git a/Tests/AK/TestIntegerMath.cpp b/Tests/AK/TestIntegerMath.cpp index 3e1d742ce5e..192543a494a 100644 --- a/Tests/AK/TestIntegerMath.cpp +++ b/Tests/AK/TestIntegerMath.cpp @@ -64,3 +64,37 @@ TEST_CASE(exp2) EXPECT_EQ(AK::exp2(62), 4611686018427387904); EXPECT_EQ(AK::exp2(63), 9223372036854775808ull); } + +TEST_CASE(ceil_log2) +{ + EXPECT_EQ(AK::ceil_log2(0), 0ull); + EXPECT_EQ(AK::ceil_log2(1), 0ull); + EXPECT_EQ(AK::ceil_log2(2), 1); + EXPECT_EQ(AK::ceil_log2(3), 2); + EXPECT_EQ(AK::ceil_log2(6), 3); + EXPECT_EQ(AK::ceil_log2(96), 7); + EXPECT_EQ(AK::ceil_log2(127), 7); + EXPECT_EQ(AK::ceil_log2(128), 7); + EXPECT_EQ(AK::ceil_log2(255), 8); + EXPECT_EQ(AK::ceil_log2(256), 8); + EXPECT_EQ(AK::ceil_log2(257), 9); + EXPECT_EQ(AK::ceil_log2(384), 9); + EXPECT_EQ(AK::ceil_log2(24576), 15); + EXPECT_EQ(AK::ceil_log2(32767), 15); + EXPECT_EQ(AK::ceil_log2(32768), 15); + EXPECT_EQ(AK::ceil_log2(32769), 16); + EXPECT_EQ(AK::ceil_log2(98304), 17); + EXPECT_EQ(AK::ceil_log2(1610612736), 31); + EXPECT_EQ(AK::ceil_log2(2147483647), 31); + EXPECT_EQ(AK::ceil_log2(2147483648), 31u); + EXPECT_EQ(AK::ceil_log2(2147483649), 32u); + EXPECT_EQ(AK::ceil_log2(3221225472), 32u); + EXPECT_EQ(AK::ceil_log2(4294967295), 32u); + EXPECT_EQ(AK::ceil_log2(4294967296), 32); + EXPECT_EQ(AK::ceil_log2(4294967297), 33); + EXPECT_EQ(AK::ceil_log2(9223372036854775807), 63ll); + EXPECT_EQ(AK::ceil_log2(9223372036854775808ull), 63ull); + EXPECT_EQ(AK::ceil_log2(9223372036854775809ull), 64ull); + EXPECT_EQ(AK::ceil_log2(13835058055282163712ull), 64ull); + EXPECT_EQ(AK::ceil_log2(18446744073709551615ull), 64ull); +}