diff --git a/AK/FixedPoint.h b/AK/FixedPoint.h index dbb268caf8..8a23f8d067 100644 --- a/AK/FixedPoint.h +++ b/AK/FixedPoint.h @@ -20,6 +20,9 @@ class FixedPoint { using This = FixedPoint; constexpr static Underlying radix_mask = (static_cast(1) << precision) - 1; + template + friend class FixedPoint; + public: constexpr FixedPoint() = default; template @@ -34,6 +37,12 @@ public: { } + template + explicit constexpr FixedPoint(FixedPoint const& other) + : m_value(other.template cast_to().m_value) + { + } + template explicit ALWAYS_INLINE operator F() const { @@ -304,7 +313,27 @@ public: template bool operator<=(F other) const { return *this <= (This)other; } + template + operator FixedPoint() const + { + return cast_to(); + } + private: + template + constexpr FixedPoint cast_to() const + { + U raw_value = static_cast(m_value >> precision) << P; + if constexpr (precision > P) + raw_value |= (m_value & radix_mask) >> (precision - P); + else if constexpr (precision < P) + raw_value |= static_cast(m_value & radix_mask) << (P - precision); + else + raw_value |= m_value & radix_mask; + + return FixedPoint::create_raw(raw_value); + } + static This create_raw(Underlying value) { This t {}; diff --git a/Tests/AK/TestFixedPoint.cpp b/Tests/AK/TestFixedPoint.cpp index 422320d1e3..8127837ab0 100644 --- a/Tests/AK/TestFixedPoint.cpp +++ b/Tests/AK/TestFixedPoint.cpp @@ -73,6 +73,38 @@ TEST_CASE(rounding) EXPECT_EQ(Type(-1.5).ltrunk(), -1); } +TEST_CASE(cast) +{ + FixedPoint<16, u32> downcast_value1(FixedPoint<32, u64>(123.4567)); + EXPECT((double)downcast_value1 >= 123.4566 && (double)downcast_value1 <= 123.4568); + static constexpr FixedPoint<32, u64> value1(321.7654); + downcast_value1 = value1; + EXPECT((double)downcast_value1 >= 321.7653 && (double)downcast_value1 <= 321.7655); + FixedPoint<6, u32> downcast_value2(FixedPoint<32, u64>(4567.123456)); + EXPECT((double)downcast_value2 >= 4567.1 && (double)downcast_value2 <= 4567.2); + downcast_value2 = FixedPoint<32, u64>(7654.654321); + EXPECT((double)downcast_value2 >= 7654.64 && (double)downcast_value2 <= 7654.66); + + EXPECT((double)downcast_value2 >= 7654.64 && (double)downcast_value2 <= 7654.66); + FixedPoint<6, u32> downcast_value3(FixedPoint<32, u64>(4567.987654)); + EXPECT((double)downcast_value3 >= 4567.9 && (double)downcast_value3 <= 4567.99); + downcast_value3 = FixedPoint<32, u64>(7654.456789); + EXPECT((double)downcast_value3 >= 7654.45 && (double)downcast_value3 <= 7654.46); + + FixedPoint<32, u64> upcast_value1(FixedPoint<16, u32>(123.4567)); + EXPECT((double)upcast_value1 >= 123.4566 && (double)upcast_value1 <= 123.4568); + upcast_value1 = FixedPoint<16, u32>(321.7654); + EXPECT((double)upcast_value1 >= 321.7653 && (double)upcast_value1 <= 321.7655); + FixedPoint<32, u64> upcast_value2(FixedPoint<6, u32>(4567.123456)); + EXPECT((double)upcast_value2 >= 4567.1 && (double)upcast_value2 <= 4567.2); + upcast_value2 = FixedPoint<6, u32>(7654.654321); + EXPECT((double)upcast_value2 >= 7654.64 && (double)upcast_value2 <= 7654.66); + FixedPoint<32, u64> upcast_value3(FixedPoint<6, u32>(4567.987654)); + EXPECT((double)upcast_value3 >= 4567.9 && (double)upcast_value3 <= 4567.99); + upcast_value3 = FixedPoint<6, u32>(7654.456789); + EXPECT((double)upcast_value3 >= 7654.45 && (double)upcast_value3 <= 7654.46); +} + TEST_CASE(formatter) { EXPECT_EQ(String::formatted("{}", FixedPoint<16>(123.456)), "123.455993"sv);