/* * Copyright (c) 2021, Leon Albrecht * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include 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) { } template explicit ALWAYS_INLINE operator F() const { return (F)m_value * pow(0.5, precision); } 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; } 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); } 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); } 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; } 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 { if (m_value > 0) return (m_value >> precision) > other || (m_value >> precision == other && (m_value & radix_mask)); if (other > 0) return false; return (m_value >> precision) > other || !(m_value >> precision == other && (m_value & radix_mask)); } template bool operator>=(I other) const { if (m_value > 0) return (m_value >> precision) >= other || (m_value >> precision == other && (m_value & radix_mask)); if (other > 0) return false; return (m_value >> precision) >= other || !(m_value >> precision == other && (m_value & radix_mask)); } template bool operator<(I other) const { if (m_value > 0) return (m_value >> precision) < other || !(m_value >> precision == other && (m_value & radix_mask)); if (other > 0) return true; return (m_value >> precision) < other || (m_value >> precision == other && (m_value & radix_mask)); } template bool operator<=(I other) const { if (m_value > 0) return (m_value >> precision) <= other || !(m_value >> precision == other && (m_value & radix_mask)); if (other > 0) return true; return (m_value >> precision) <= other || (m_value >> precision == other && (m_value & radix_mask)); } // 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); } static This create_raw(Underlying value) { This t {}; t.raw() = value; return t; } Underlying m_value; }; } using AK::FixedPoint;