/* * Copyright (c) 2021, Leon Albrecht * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #ifndef KERNEL # include #endif namespace AK { // FIXME: this always uses round to nearest break-tie to even // FIXME: use the Integral concept to constrain Underlying template class FixedPoint { using This = FixedPoint; constexpr static Underlying radix_mask = (static_cast(1) << precision) - 1; template friend class FixedPoint; public: constexpr FixedPoint() = default; template constexpr FixedPoint(I value) : m_value(static_cast(value) << precision) { } template constexpr FixedPoint(F value) : m_value(static_cast(value * (static_cast(1) << precision))) { } template explicit constexpr FixedPoint(FixedPoint const& other) : m_value(other.template cast_to().m_value) { } #ifndef KERNEL template explicit ALWAYS_INLINE operator F() const { return (F)m_value * pow(0.5, precision); } #endif template explicit constexpr operator I() const { I value = m_value >> precision; // fract(m_value) >= .5? if (m_value & (1u << (precision - 1))) { // fract(m_value) > .5? if (m_value & (radix_mask >> 2u)) { // yes: round "up"; value += (m_value > 0 ? 1 : -1); } else { // no: round to even; value += value & 1; } } return value; } static constexpr This create_raw(Underlying value) { This t {}; t.raw() = value; return t; } constexpr Underlying raw() const { return m_value; } constexpr Underlying& raw() { return m_value; } constexpr This fract() const { return create_raw(m_value & radix_mask); } constexpr This round() const { return This { static_cast(*this) }; } constexpr This floor() const { return create_raw(m_value & ~radix_mask); } constexpr This ceil() const { return create_raw((m_value & ~radix_mask) + (m_value & radix_mask ? 1 << precision : 0)); } constexpr This trunk() const { return create_raw((m_value & ~radix_mask) + ((m_value & radix_mask) ? (m_value > 0 ? 0 : (1 << precision)) : 0)); } constexpr Underlying lround() const { return static_cast(*this); } constexpr Underlying lfloor() const { return m_value >> precision; } constexpr Underlying lceil() const { return (m_value >> precision) + (m_value & radix_mask ? 1 : 0); } constexpr Underlying ltrunk() const { return (m_value >> precision) + ((m_value & radix_mask) ? m_value > 0 ? 0 : 1 : 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); } constexpr This operator-() const requires(IsSigned) { return create_raw(-m_value); } constexpr This operator+(This const& other) const { return create_raw(m_value + other.m_value); } constexpr This operator-(This const& other) const { return create_raw(m_value - other.m_value); } constexpr This operator*(This const& other) const { // FIXME: Potential Overflow, although result could be represented accurately Underlying value = m_value * other.raw(); This ret {}; ret.raw() = value >> precision; // fract(value) >= .5? if (value & (1u << (precision - 1))) { // fract(value) > .5? if (value & (radix_mask >> 2u)) { // yes: round up; ret.raw() += (value > 0 ? 1 : -1); } else { // no: round to even (aka unset last sigificant bit); ret.raw() += m_value & 1; } } return ret; } constexpr This operator/(This const& other) const { // FIXME: Better rounding? return create_raw((m_value / other.m_value) << (precision)); } template constexpr This operator+(I other) const { return create_raw(m_value + (other << precision)); } template constexpr This operator-(I other) const { return create_raw(m_value - (other << precision)); } template constexpr This operator*(I other) const { return create_raw(m_value * other); } template constexpr This operator/(I other) const { return create_raw(m_value / other); } template constexpr This operator>>(I other) const { return create_raw(m_value >> other); } template constexpr This operator<<(I other) const { return create_raw(m_value << other); } This& operator+=(This const& other) { m_value += other.raw(); return *this; } This& operator-=(This const& other) { m_value -= other.raw(); return *this; } This& operator*=(This const& other) { Underlying value = m_value * other.raw(); m_value = value >> precision; // fract(value) >= .5? if (value & (1u << (precision - 1))) { // fract(value) > .5? if (value & (radix_mask >> 2u)) { // yes: round up; m_value += (value > 0 ? 1 : -1); } else { // no: round to even (aka unset last sigificant bit); m_value += m_value & 1; } } return *this; } This& operator/=(This const& other) { // FIXME: See above m_value /= other.raw(); m_value <<= precision; return *this; } template This& operator+=(I other) { m_value += other << precision; return *this; } template This& operator-=(I other) { m_value -= other << precision; return *this; } template This& operator*=(I other) { m_value *= other; return *this; } template This& operator/=(I other) { m_value /= other; return *this; } template This& operator>>=(I other) { m_value >>= other; return *this; } template This& operator<<=(I other) { m_value <<= other; return *this; } bool operator==(This const& other) const { return raw() == other.raw(); } bool operator!=(This const& other) const { return raw() != other.raw(); } bool operator>(This const& other) const { return raw() > other.raw(); } bool operator>=(This const& other) const { return raw() >= other.raw(); } bool operator<(This const& other) const { return raw() < other.raw(); } bool operator<=(This const& other) const { return raw() <= other.raw(); } // FIXE: There are probably better ways to do these template bool operator==(I other) const { return m_value >> precision == other && !(m_value & radix_mask); } template bool operator!=(I other) const { return (m_value >> precision) != other || m_value & radix_mask; } template bool operator>(I other) const { return !(*this <= other); } template bool operator>=(I other) const { return !(*this < other); } template bool operator<(I other) const { return (m_value >> precision) < other || m_value < (other << precision); } template bool operator<=(I other) const { return *this < other || *this == other; } // Casting from a float should be faster than casting to a float template bool operator==(F other) const { return *this == (This)other; } template bool operator!=(F other) const { return *this != (This)other; } template bool operator>(F other) const { return *this > (This)other; } template bool operator>=(F other) const { return *this >= (This)other; } template bool operator<(F other) const { return *this < (This)other; } 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); } Underlying m_value; }; template struct Formatter> : StandardFormatter { Formatter() = default; explicit Formatter(StandardFormatter formatter) : StandardFormatter(formatter) { } ErrorOr format(FormatBuilder& builder, FixedPoint value) { u8 base; bool upper_case; FormatBuilder::RealNumberDisplayMode real_number_display_mode = FormatBuilder::RealNumberDisplayMode::General; if (m_mode == Mode::Default || m_mode == Mode::FixedPoint) { base = 10; upper_case = false; if (m_mode == Mode::FixedPoint) real_number_display_mode = FormatBuilder::RealNumberDisplayMode::FixedPoint; } else if (m_mode == Mode::Hexfloat) { base = 16; upper_case = false; } else if (m_mode == Mode::HexfloatUppercase) { base = 16; upper_case = true; } else { VERIFY_NOT_REACHED(); } m_width = m_width.value_or(0); m_precision = m_precision.value_or(6); bool is_negative = false; if constexpr (IsSigned) is_negative = value < 0; i64 integer = value.ltrunk(); constexpr u64 one = static_cast(1) << precision; u64 fraction_raw = value.raw() & (one - 1); return builder.put_fixed_point(is_negative, integer, fraction_raw, one, base, upper_case, m_zero_pad, m_align, m_width.value(), m_precision.value(), m_fill, m_sign_mode, real_number_display_mode); } }; } #if USING_AK_GLOBALLY using AK::FixedPoint; #endif