mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 19:45:12 +00:00
AK: Replace FP math in is_power_of
with a purely integral algorithm
The previous naive approach was causing test failures because of rounding issues in some exotic environments. In particular, MSVC via MSBuild
This commit is contained in:
parent
5f33db9abe
commit
b5bed37074
Notes:
sideshowbarker
2024-07-17 06:39:26 +09:00
Author: https://github.com/abuneri 🔰 Commit: https://github.com/SerenityOS/serenity/commit/b5bed37074 Pull-request: https://github.com/SerenityOS/serenity/pull/24218 Reviewed-by: https://github.com/ADKaster ✅
2 changed files with 25 additions and 8 deletions
|
@ -57,15 +57,22 @@ constexpr I pow(I base, I exponent)
|
|||
template<auto base, Unsigned U = decltype(base)>
|
||||
constexpr bool is_power_of(U x)
|
||||
{
|
||||
if constexpr (base == 2)
|
||||
if constexpr (base == 1)
|
||||
return x == 1;
|
||||
else if constexpr (base == 2)
|
||||
return is_power_of_two(x);
|
||||
|
||||
// FIXME: I am naive! A log2-based approach (pow<U>(base, (log2(x) / log2(base))) == x) does not work due to rounding errors.
|
||||
for (U exponent = 0; exponent <= log2(x) / log2(base) + 1; ++exponent) {
|
||||
if (pow<U>(base, exponent) == x)
|
||||
return true;
|
||||
if (base == 0 && x == 0)
|
||||
return true;
|
||||
if (base == 0 || x == 0)
|
||||
return false;
|
||||
|
||||
while (x != 1) {
|
||||
if (x % base != 0)
|
||||
return false;
|
||||
x /= base;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
TEST_CASE(pow)
|
||||
{
|
||||
EXPECT_EQ(AK::pow<u64>(0, 0), 1ull);
|
||||
EXPECT_EQ(AK::pow<u64>(10, 0), 1ull);
|
||||
EXPECT_EQ(AK::pow<u64>(10, 1), 10ull);
|
||||
EXPECT_EQ(AK::pow<u64>(10, 2), 100ull);
|
||||
|
@ -22,12 +23,21 @@ TEST_CASE(pow)
|
|||
|
||||
TEST_CASE(is_power_of)
|
||||
{
|
||||
constexpr auto check_prime = []<u64 prime>(u64 limit) {
|
||||
for (u64 power = 0; power < limit; ++power)
|
||||
EXPECT(!AK::is_power_of<0>(10ull));
|
||||
// We don't have enough context to know if the input was from 0^0
|
||||
EXPECT(!AK::is_power_of<0>(1ull));
|
||||
|
||||
EXPECT(!AK::is_power_of<1>(10ull));
|
||||
EXPECT(!AK::is_power_of<1>(0ull));
|
||||
|
||||
constexpr auto check_prime = []<u64 prime>(u64 limit, u64 init = 0) {
|
||||
for (u64 power = init; power < limit; ++power)
|
||||
EXPECT(AK::is_power_of<prime>(AK::pow(prime, power)));
|
||||
};
|
||||
|
||||
// Limits calculated as floor( log_{prime}(2^64) ) to prevent overflows.
|
||||
check_prime.operator()<0>(42, 1);
|
||||
check_prime.operator()<1>(36);
|
||||
check_prime.operator()<2>(64);
|
||||
check_prime.operator()<3>(40);
|
||||
check_prime.operator()<5>(27);
|
||||
|
|
Loading…
Add table
Reference in a new issue