diff --git a/AK/FixedPoint.h b/AK/FixedPoint.h index d3dc17b7680..aad0cba7e05 100644 --- a/AK/FixedPoint.h +++ b/AK/FixedPoint.h @@ -8,7 +8,9 @@ #include #include +#include #include +#include #include namespace AK { @@ -116,6 +118,39 @@ public: : 0); } + // http://www.claysturner.com/dsp/BinaryLogarithm.pdf + constexpr This log2() const + { + // 0.5 + This b = create_raw(1 << (precision - 1)); + This y = 0; + This x = *this; + + // FIXME: There's no negative infinity. + if (x.raw() <= 0) + return create_raw(NumericLimits::min()); + + if (x != 1) { + i32 shift_amount = AK::log2(x.raw()) - precision; + if (shift_amount > 0) + x >>= shift_amount; + else + x <<= -shift_amount; + y += shift_amount; + } + + for (size_t i = 0; i < precision; ++i) { + x *= x; + if (x >= 2) { + x >>= 1; + y += b; + } + b >>= 1; + } + + return y; + } + constexpr bool signbit() const requires(IsSigned) { return m_value >> (sizeof(Underlying) * 8 - 1); diff --git a/Tests/AK/TestFixedPoint.cpp b/Tests/AK/TestFixedPoint.cpp index 5192f07f949..7050fca6005 100644 --- a/Tests/AK/TestFixedPoint.cpp +++ b/Tests/AK/TestFixedPoint.cpp @@ -7,6 +7,7 @@ #include #include +#include using Type = FixedPoint<4>; @@ -73,6 +74,18 @@ TEST_CASE(rounding) EXPECT_EQ(Type(-1.5).ltrunk(), -1); } +TEST_CASE(logarithm) +{ + EXPECT_EQ(Type(0).log2().raw(), NumericLimits::min()); + EXPECT_EQ(Type(1).log2(), Type(0)); + EXPECT_EQ(Type(2).log2(), Type(1)); + EXPECT_EQ(Type(8).log2(), Type(3)); + EXPECT_EQ(Type(0.5).log2(), Type(-1)); + + EXPECT_EQ(Type(22.627416997969520780827019587355).log2(), Type(4.4375)); + EXPECT_EQ(Type(3088).log2(), Type(11.592457037268080419637304576833)); +} + TEST_CASE(comparison) { EXPECT(Type(0) < 1);