#ifndef FPM_FIXED_HPP #define FPM_FIXED_HPP #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) #include "../diagnostic.h" #endif // QT_VERSION < QT_VERSION_CHECK(5, 5, 0) namespace fpm { //! Fixed-point number type //! \tparam BaseType the base integer type used to store the fixed-point number. This can be a signed or //! unsigned type. //! \tparam IntermediateType the integer type used to store intermediate results during calculations. //! \tparam FractionBits the number of bits of the BaseType used to store the fraction template class fixed { static_assert(std::is_integral::value, "BaseType must be an integral type"); static_assert(FractionBits > 0, "FractionBits must be greater than zero"); static_assert(FractionBits <= sizeof(BaseType) * 8 - 1, "BaseType must at least be able to contain entire " "fraction, with space for at least one integral bit"); static_assert(sizeof(IntermediateType) > sizeof(BaseType), "IntermediateType must be larger than BaseType"); static_assert(std::is_signed::value == std::is_signed::value, "IntermediateType must have same signedness as BaseType"); // Although this value fits in the BaseType in terms of bits, if there's only one integral bit, this value // is incorrect (flips from positive to negative), so we must extend the size to IntermediateType. static constexpr IntermediateType FRACTION_MULT = IntermediateType(1) << FractionBits; struct raw_construct_tag {}; constexpr inline fixed(BaseType val, raw_construct_tag /*unused*/) noexcept : m_value(val) {} public: inline fixed() noexcept = default; // Converts an integral number to the fixed-point type. // Like static_cast, this truncates bits that don't fit. template ::value>::type* = nullptr> constexpr inline explicit fixed(T val) noexcept : m_value(static_cast(val * FRACTION_MULT)) {} // Converts an floating-point number to the fixed-point type. // Like static_cast, this truncates bits that don't fit. template ::value>::type* = nullptr> constexpr inline explicit fixed(T val) noexcept : m_value(static_cast((val >= 0.0) ? (val * FRACTION_MULT + T{0.5}) : (val * FRACTION_MULT - T{0.5}))) {} // Constructs from another fixed-point type with possibly different underlying representation. // Like static_cast, this truncates bits that don't fit. template constexpr inline explicit fixed(fixed val) noexcept : m_value(from_fixed_point(val.raw_value()).raw_value()) {} // Explicit conversion to a floating-point type template ::value>::type* = nullptr> constexpr inline explicit operator T() const noexcept { return static_cast(m_value) / FRACTION_MULT; } // Explicit conversion to an integral type template ::value>::type* = nullptr> constexpr inline explicit operator T() const noexcept { return static_cast(m_value / FRACTION_MULT); } // Returns the raw underlying value of this type. // Do not use this unless you know what you're doing. constexpr inline auto raw_value() const noexcept -> BaseType { return m_value; } //! Constructs a fixed-point number from another fixed-point number. //! \tparam NumFractionBits the number of bits used by the fraction in \a value. //! \param value the integer fixed-point number template FractionBits)>::type* = nullptr> static constexpr inline auto from_fixed_point(T value) noexcept -> fixed { // To correctly round the last bit in the result, we need one more bit of information. // We do this by multiplying by two before dividing and adding the LSB to the real result. return fixed(static_cast( value / (T(1) << (NumFractionBits - FractionBits)) + (value / (T(1) << (NumFractionBits - FractionBits - 1)) % 2)), raw_construct_tag{}); } template ::type* = nullptr> static constexpr inline auto from_fixed_point(T value) noexcept -> fixed { return fixed(static_cast( value * (T(1) << (FractionBits - NumFractionBits))), raw_construct_tag{}); } // Constructs a fixed-point number from its raw underlying value. // Do not use this unless you know what you're doing. static constexpr inline auto from_raw_value(BaseType value) noexcept -> fixed { return fixed(value, raw_construct_tag{}); } // // Constants // static constexpr auto e() -> fixed { return from_fixed_point<61>(6267931151224907085LL); } static constexpr auto pi() -> fixed { return from_fixed_point<61>(7244019458077122842LL); } static constexpr auto half_pi() -> fixed { return from_fixed_point<62>(7244019458077122842LL); } static constexpr auto two_pi() -> fixed { return from_fixed_point<60>(7244019458077122842LL); } // // Arithmetic member operators // constexpr inline auto operator-() const noexcept -> fixed { return fixed::from_raw_value(-m_value); } inline auto operator+=(const fixed& y) noexcept -> fixed& { m_value += y.m_value; return *this; } template ::value>::type* = nullptr> inline auto operator+=(I y) noexcept -> fixed& { m_value += y * FRACTION_MULT; return *this; } inline auto operator-=(const fixed& y) noexcept -> fixed& { m_value -= y.m_value; return *this; } template ::value>::type* = nullptr> inline auto operator-=(I y) noexcept -> fixed& { m_value -= y * FRACTION_MULT; return *this; } inline auto operator*=(const fixed& y) noexcept -> fixed& { // Normal fixed-point multiplication is: x * y / 2**FractionBits. // To correctly round the last bit in the result, we need one more bit of information. // We do this by multiplying by two before dividing and adding the LSB to the real result. auto value = static_cast( static_cast(static_cast(m_value) * y.m_value) / (FRACTION_MULT / 2)); m_value = static_cast((value / 2) + (value % 2)); return *this; } template ::value>::type* = nullptr> inline auto operator*=(I y) noexcept -> fixed& { m_value *= y; return *this; } inline auto operator/=(const fixed& y) noexcept -> fixed& { assert(y.m_value != 0); // Normal fixed-point division is: x * 2**FractionBits / y. // To correctly round the last bit in the result, we need one more bit of information. // We do this by multiplying by two before dividing and adding the LSB to the real result. auto value = (static_cast(m_value) * FRACTION_MULT * 2) / y.m_value; m_value = static_cast((value / 2) + (value % 2)); return *this; } template ::value>::type* = nullptr> inline auto operator/=(I y) noexcept -> fixed& { m_value /= y; return *this; } // // Shift operators // template ::value>::type* = nullptr> inline auto operator>>=(I y) noexcept -> fixed& { m_value >>= y; return *this; } template ::value>::type* = nullptr> inline auto operator<<=(I y) noexcept -> fixed& { m_value <<= y; return *this; } private: BaseType m_value; }; // // Convenience typedefs // using fixed_16_16 = fixed; using fixed_24_8 = fixed; using fixed_8_24 = fixed; // // Addition // template constexpr inline auto operator+(const fixed& x, const fixed& y) noexcept -> fixed { return fixed(x) += y; } template ::value>::type* = nullptr> constexpr inline auto operator+(const fixed& x, T y) noexcept -> fixed { return fixed(x) += y; } template ::value>::type* = nullptr> constexpr inline auto operator+(T x, const fixed& y) noexcept -> fixed { return fixed(y) += x; } // // Subtraction // template constexpr inline auto operator-(const fixed& x, const fixed& y) noexcept -> fixed { return fixed(x) -= y; } template ::value>::type* = nullptr> constexpr inline auto operator-(const fixed& x, T y) noexcept -> fixed { return fixed(x) -= y; } template ::value>::type* = nullptr> constexpr inline auto operator-(T x, const fixed& y) noexcept -> fixed { return fixed(x) -= y; } // // Multiplication // template constexpr inline auto operator*(const fixed& x, const fixed& y) noexcept -> fixed { return fixed(x) *= y; } template ::value>::type* = nullptr> constexpr inline auto operator*(const fixed& x, T y) noexcept -> fixed { return fixed(x) *= y; } template ::value>::type* = nullptr> constexpr inline auto operator*(T x, const fixed& y) noexcept -> fixed { return fixed(y) *= x; } // // Division // template constexpr inline auto operator/(const fixed& x, const fixed& y) noexcept -> fixed { return fixed(x) /= y; } template ::value>::type* = nullptr> constexpr inline auto operator/(const fixed& x, T y) noexcept -> fixed { return fixed(x) /= y; } template ::value>::type* = nullptr> constexpr inline auto operator/(T x, const fixed& y) noexcept -> fixed { return fixed(x) /= y; } // // Shift operators // template ::value>::type* = nullptr> constexpr inline auto operator>>(const fixed& x, T y) noexcept -> fixed { return fixed(x) >>= y; } template ::value>::type* = nullptr> constexpr inline auto operator<<(const fixed& x, T y) noexcept -> fixed { return fixed(x) <<= y; } // // Comparison operators // template constexpr inline auto operator==(const fixed& x, const fixed& y) noexcept -> bool { return x.raw_value() == y.raw_value(); } template constexpr inline auto operator!=(const fixed& x, const fixed& y) noexcept -> bool { return x.raw_value() != y.raw_value(); } template constexpr inline auto operator<(const fixed& x, const fixed& y) noexcept -> bool { return x.raw_value() < y.raw_value(); } template constexpr inline auto operator>(const fixed& x, const fixed& y) noexcept -> bool { return x.raw_value() > y.raw_value(); } template constexpr inline auto operator<=(const fixed& x, const fixed& y) noexcept -> bool { return x.raw_value() <= y.raw_value(); } template constexpr inline auto operator>=(const fixed& x, const fixed& y) noexcept -> bool { return x.raw_value() >= y.raw_value(); } namespace detail { QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wunneeded-internal-declaration") // Number of base-10 digits required to fully represent a number of bits static constexpr auto max_digits10(int bits) -> int { // 8.24 fixed-point equivalent of (int)ceil(bits * std::log10(2)); using T = long long; // NOLINT(google-runtime-int) return static_cast((T{bits} * 5050445 + (T{1} << 24) - 1) >> 24); // NOLINT(hicpp-signed-bitwise) } // Number of base-10 digits that can be fully represented by a number of bits static constexpr auto digits10(int bits) -> int { // 8.24 fixed-point equivalent of (int)(bits * std::log10(2)); using T = long long; // NOLINT(google-runtime-int) return static_cast((T{bits} * 5050445) >> 24); //NOLINT(hicpp-signed-bitwise) } QT_WARNING_POP } // namespace detail } // namespace fpm // Specializations for customization points namespace std // NOLINT(cert-dcl58-cpp) { template struct hash> { using argument_type = fpm::fixed; using result_type = std::size_t; auto operator()(const argument_type &arg) const noexcept(noexcept(std::declval>()(arg.raw_value()))) -> result_type { return m_hash(arg.raw_value()); } private: std::hash m_hash; }; template struct numeric_limits> { static constexpr bool is_specialized = true; static constexpr bool is_signed = std::numeric_limits::is_signed; static constexpr bool is_integer = false; static constexpr bool is_exact = true; static constexpr bool has_infinity = false; static constexpr bool has_quiet_NaN = false; static constexpr bool has_signaling_NaN = false; static constexpr std::float_denorm_style has_denorm = std::denorm_absent; static constexpr bool has_denorm_loss = false; static constexpr std::float_round_style round_style = std::round_to_nearest; static constexpr bool is_iec_559 = false; static constexpr bool is_bounded = true; static constexpr bool is_modulo = std::numeric_limits::is_modulo; static constexpr int digits = std::numeric_limits::digits; // Any number with `digits10` significant base-10 digits (that fits in // the range of the type) is guaranteed to be convertible from text and // back without change. Worst case, this is 0.000...001, so we can only // guarantee this case. Nothing more. static constexpr int digits10 = 1; // This is equal to max_digits10 for the integer and fractional part together. static constexpr int max_digits10 = fpm::detail::max_digits10(std::numeric_limits::digits - F) + fpm::detail::max_digits10(F); static constexpr int radix = 2; static constexpr int min_exponent = 1 - F; static constexpr int min_exponent10 = -fpm::detail::digits10(F); static constexpr int max_exponent = std::numeric_limits::digits - F; static constexpr int max_exponent10 = fpm::detail::digits10(std::numeric_limits::digits - F); static constexpr bool traps = true; static constexpr bool tinyness_before = false; static constexpr auto lowest() noexcept -> fpm::fixed { return fpm::fixed::from_raw_value(std::numeric_limits::lowest()); } static constexpr auto min() noexcept -> fpm::fixed { return lowest(); } static constexpr auto max() noexcept -> fpm::fixed { return fpm::fixed::from_raw_value(std::numeric_limits::max()); } static constexpr auto epsilon() noexcept -> fpm::fixed { return fpm::fixed::from_raw_value(1); } static constexpr auto round_error() noexcept -> fpm::fixed { return fpm::fixed(1) / 2; } static constexpr auto denorm_min() noexcept -> fpm::fixed { return min(); } }; // See https://stackoverflow.com/a/46719572/3045403 #if __cplusplus < 201703L template constexpr bool numeric_limits>::is_specialized; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::is_signed; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::is_integer; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::is_exact; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::has_infinity; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::has_quiet_NaN; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::has_signaling_NaN; // NOLINT(readability-redundant-declaration) template constexpr std::float_denorm_style numeric_limits>::has_denorm; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::has_denorm_loss; // NOLINT(readability-redundant-declaration) template constexpr std::float_round_style numeric_limits>::round_style; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::is_iec_559; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::is_bounded; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::is_modulo; // NOLINT(readability-redundant-declaration) template constexpr int numeric_limits>::digits; // NOLINT(readability-redundant-declaration) template constexpr int numeric_limits>::digits10; // NOLINT(readability-redundant-declaration) template constexpr int numeric_limits>::max_digits10; // NOLINT(readability-redundant-declaration) template constexpr int numeric_limits>::radix; // NOLINT(readability-redundant-declaration) template constexpr int numeric_limits>::min_exponent; // NOLINT(readability-redundant-declaration) template constexpr int numeric_limits>::min_exponent10; // NOLINT(readability-redundant-declaration) template constexpr int numeric_limits>::max_exponent; // NOLINT(readability-redundant-declaration) template constexpr int numeric_limits>::max_exponent10; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::traps; // NOLINT(readability-redundant-declaration) template constexpr bool numeric_limits>::tinyness_before; // NOLINT(readability-redundant-declaration) #endif } // namespace std #endif