// // Created by erki on 30.04.21. // #ifndef SKULLC_UTILITY_FIXEDPOINT_HPP #define SKULLC_UTILITY_FIXEDPOINT_HPP #include #include #include namespace Peripherals { namespace Details { template struct NextRankingInteger { }; template<> struct NextRankingInteger { using T = std::int16_t; }; template<> struct NextRankingInteger { using T = std::int32_t; }; template<> struct NextRankingInteger { using T = std::int64_t; }; template<> struct NextRankingInteger { using T = std::int64_t; }; template<> struct NextRankingInteger { using T = std::uint16_t; }; template<> struct NextRankingInteger { using T = std::uint32_t; }; template<> struct NextRankingInteger { using T = std::uint64_t; }; template<> struct NextRankingInteger { using T = std::uint64_t; }; }// namespace Details template struct FixedPoint { using underlying = U; using isSigned = std::is_signed; constexpr static std::size_t fractionalBits = D; underlying data; constexpr FixedPoint() : data{0} {} template constexpr explicit FixedPoint(const T& value) { static_assert(std::is_arithmetic_v, "T is not an arithmetic type."); *this = FixedPoint::fromValue(value); } template constexpr FixedPoint& operator=(const T& value) { static_assert(std::is_arithmetic_v, "T is not an arithmetic type."); *this = FixedPoint::fromValue(value); return *this; } constexpr bool operator==(const FixedPoint& o) const { return data == o.data; } template constexpr bool operator==(const T& value) const { static_assert(std::is_arithmetic_v, "T is not an integral type."); const auto other = FixedPoint(value); return *this == other; } template constexpr bool operator!=(const T& value) const { static_assert(std::is_arithmetic_v, "T is not an integral type."); const auto other = FixedPoint(value); return *this != other; } constexpr bool operator!=(const FixedPoint& o) const { return !operator==(o); } constexpr bool operator<(const FixedPoint& o) const { return data < o.data; } constexpr bool operator<=(const FixedPoint& o) const { return data <= o.data; } constexpr bool operator>(const FixedPoint& o) const { return data > o.data; } constexpr bool operator>=(const FixedPoint& o) const { return data >= o.data; } template constexpr bool operator<(const T& value) const { const auto o = fromValue(value); return data < o.data; } template constexpr bool operator<=(const T& value) const { const auto o = fromValue(value); return data <= o.data; } template constexpr bool operator>(const T& value) const { const auto o = fromValue(value); return data > o.data; } template constexpr bool operator>=(const T& value) const { const auto o = fromValue(value); return data >= o.data; } friend constexpr FixedPoint operator+(const FixedPoint& a, const FixedPoint& b) { auto clone = a; clone.data += b.data; return clone; } friend constexpr FixedPoint operator-(const FixedPoint& a, const FixedPoint& b) { auto clone = a; clone.data -= b.data; return clone; } template friend constexpr FixedPoint operator+(const FixedPoint& a, const T& b) { static_assert(std::is_arithmetic_v, "T is not an arithmetic type."); return a + FixedPoint(b); } template friend constexpr FixedPoint operator-(const FixedPoint& a, const T& b) { static_assert(std::is_arithmetic_v, "T is not an arithmetic type."); return a - FixedPoint(b); } friend constexpr FixedPoint operator*(const FixedPoint& a, const FixedPoint& b) { // Ref: https://spin.atomicobject.com/2012/03/15/simple-fixed-point-math/ // Ref: https://en.wikipedia.org/wiki/Q_(number_format) using T = typename Details::NextRankingInteger::T; const T axb = T(a.data) * T(b.data); const T round_value = axb >> (fractionalBits - 1); auto dest = FixedPoint(); dest.data = (axb / (underlying(1) << fractionalBits)) + (round_value & 1); return dest; } friend constexpr FixedPoint operator/(const FixedPoint& a, const FixedPoint& b) { // Ref: https://en.wikipedia.org/wiki/Q_(number_format) using T = typename Details::NextRankingInteger::T; T temp = T(a.data) << fractionalBits; // rounding if ((temp >= 0 && b.data >= 0) || (temp < 0 && b.data < 0)) temp += b.data / 2; else temp -= b.data / 2; auto dest = FixedPoint(); dest.data = temp / b.data; return dest; } template friend constexpr FixedPoint operator*(const FixedPoint& a, const T& b) { static_assert(std::is_arithmetic_v, "T is not an arithmetic type."); return a * FixedPoint(b); } template friend constexpr FixedPoint operator/(const FixedPoint& a, const T& b) { static_assert(std::is_arithmetic_v, "T is not an arithmetic type."); return a / FixedPoint(b); } template static constexpr FixedPoint fromValue(const T& value) { static_assert(std::is_integral_v, "T is not integral value."); auto fp = FixedPoint(); fp.data = value << D; return fp; } constexpr static FixedPoint fromValue(const float& value) { auto fp = FixedPoint(); fp.data = std::round(value * (underlying(1) << fractionalBits)); return fp; } constexpr static FixedPoint fromValue(const double& value) { auto fp = FixedPoint(); fp.data = std::round(value * (underlying(1) << fractionalBits)); return fp; } template constexpr T toValue() const { static_assert(std::is_integral_v, "T is not an integral type."); return data >> fractionalBits; } constexpr explicit operator float() const { return float(data) / float(underlying(1) << fractionalBits); } constexpr explicit operator double() const { return double(data) / double(underlying(1) << fractionalBits); } }; using Q7 = FixedPoint; using Q15 = FixedPoint; using Q31 = FixedPoint; }// namespace Peripherals #endif//SKULLC_UTILITY_FIXEDPOINT_HPP