Skip to content

File fpm_adapter.hpp

File List > fpm_adapter.hpp

Go to the documentation of this file


#pragma once
#include <compare>     // For std::strong_ordering (operator<=>)
#include <concepts>    // For std::integral
#include <iostream>    // For the std::ostream operator
#include <limits>      // For std::numeric_limits
#include <stdexcept>   // For std::overflow_error, std::underflow_error
#include <type_traits> // For std::is_same_v, std::is_signed_v

// The fpm library has inherent shadowing and sign-conversion issues.
// Suppress these warnings when including fpm headers.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wconversion"

#include "fpm/fixed.hpp"
#include "fpm/ios.hpp"
#include "fpm/math.hpp"

#pragma GCC diagnostic pop
template <typename B, typename I, unsigned int F, bool E = true>
class FixedAdapter: public fpm::fixed<B, I, F, E> {
public:
    using intermediate_type = I;
    using base_type = B;
    static const int FixedBits = F;

    inline FixedAdapter() noexcept = default;

    template <typename NonFixedType>
    constexpr inline FixedAdapter(NonFixedType val) noexcept: fpm::fixed<B, I, F, E>(val) {}

    [[nodiscard]] auto operator<=>(const auto& other) const noexcept {
        return this->raw_value() <=> static_cast<decltype(*this)>(other).raw_value();
    }

    constexpr inline operator bool() const { return bool(this->raw_value()); }

    double hax = 0;
    auto operator=(auto& o) {
        auto ret = this->fpm::fixed<B,I,F,E>::operator=(o);
        hax = double(ret);
        return ret;
    }
};

template <typename T>
concept IsFixedPoint = fpm::is_fixed_v<T>;

namespace std {
    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr FixedType max(const FixedType& a, const NonFixedType& b) {
        return (a < b) ? FixedType(b) : a;
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr FixedType min(const FixedType& a, const NonFixedType& b) {
        return (a >= b) ? FixedType(b) : a;
    }

    template <typename T, typename O>
        requires IsFixedPoint<T>
    constexpr T pow(const T& A, const O& B) {
        return T(fpm::pow(A, B));
    }

} // namespace std

namespace fpm {
    template <typename BaseType, typename intermediate_type, unsigned int FractionBits, bool EnableRounding>
    struct is_fixed<FixedAdapter<BaseType, intermediate_type, FractionBits, EnableRounding>>: std::true_type {};

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator+=(FixedType& x, const NonFixedType& y) noexcept {
        return x += FixedType(y);
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator-=(FixedType& x, const NonFixedType& y) noexcept {
        return x -= FixedType(y);
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator*=(FixedType& x, const NonFixedType& y) noexcept {
        return x *= FixedType(y);
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator/=(FixedType& x, const NonFixedType& y) noexcept {
        return x /= FixedType(y);
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator+(const FixedType& x, const NonFixedType& y) noexcept {
        return x + FixedType(y);
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator+(const NonFixedType& x, const FixedType& y) noexcept {
        return FixedType(x) + y;
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator-(const FixedType& x, const NonFixedType& y) noexcept {
        return x - FixedType(y);
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator-(const NonFixedType& x, const FixedType& y) noexcept {
        return FixedType(x) - y;
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator*(const FixedType& x, const NonFixedType& y) noexcept {
        return x * FixedType(y);
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator*(const NonFixedType& x, const FixedType& y) noexcept {
        return FixedType(x) * y;
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator/(const FixedType& x, const NonFixedType& y) noexcept {
        return x / FixedType(y);
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && std::is_floating_point_v<NonFixedType>
    constexpr inline FixedType operator/(const NonFixedType& x, const FixedType& y) noexcept {
        return FixedType(x) / y;
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator==(const FixedType& x, const NonFixedType& y) noexcept {
        return x.raw_value() == FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator==(const NonFixedType& y, const FixedType& x) noexcept {
        return x.raw_value() == FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator!=(const FixedType& x, const NonFixedType& y) noexcept {
        return x.raw_value() != FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator!=(const NonFixedType& y, const FixedType& x) noexcept {
        return x.raw_value() != FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator<(const FixedType& x, const NonFixedType& y) noexcept {
        return x.raw_value() < FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator<(const NonFixedType& y, const FixedType& x) noexcept {
        return x.raw_value() < FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator<=(const FixedType& x, const NonFixedType& y) noexcept {
        return x.raw_value() <= FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator<=(const NonFixedType& y, const FixedType& x) noexcept {
        return x.raw_value() <= FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator>(const FixedType& x, const NonFixedType& y) noexcept {
        return x.raw_value() > FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator>(const NonFixedType& y, const FixedType& x) noexcept {
        return x.raw_value() > FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator>=(const FixedType& x, const NonFixedType& y) noexcept {
        return x.raw_value() >= FixedType(y).raw_value();
    }

    template <typename FixedType, typename NonFixedType>
        requires IsFixedPoint<FixedType> && (!IsFixedPoint<NonFixedType>)
    constexpr inline bool operator>=(const NonFixedType& y, const FixedType& x) noexcept {
        return x.raw_value() >= FixedType(y).raw_value();
    }
} // namespace fpm

// Specializations for customization points
// Note: The following templates are specialized for the `FixedAdapter` class.
// Due to C++ template specialization rules, these specializations will not
// automatically apply to classes that inherit from `FixedAdapter`. To extend this
// functionality to a subclass of `FixedAdapter`, you can inherit from the
// `detail::HashBase` and `detail::NumericLimitsBase` helper structs.
//
// For example:
//
//   class MyFixed: public FixedAdapter<int32_t, int64_t, 16> {};
//
//   namespace std {
//     template <>
//     struct hash<MyFixed>: public detail::HashBase<MyFixed> {};
//     template <>
//     struct numeric_limits<MyFixed>: public detail::NumericLimitsBase<MyFixed> {};
//   }
//

namespace detail {
    template <IsFixedPoint FixedType>
    struct NumericLimitsBase {
        using B = typename FixedType::base_type;
        using I = typename FixedType::intermediate_type;
        static constexpr unsigned int F = FixedType::fraction_bits;

        inline static constexpr bool                    is_specialized = true;
        inline static constexpr bool                    is_signed = std::numeric_limits<B>::is_signed;
        inline static constexpr bool                    is_integer = false;
        inline static constexpr bool                    is_exact = true;
        inline static constexpr bool                    has_infinity = false;
        inline static constexpr bool                    has_quiet_NaN = false;
        inline static constexpr bool                    has_signaling_NaN = false;
        inline static constexpr std::float_denorm_style has_denorm = std::denorm_absent;
        inline static constexpr bool                    has_denorm_loss = false;
        inline static constexpr std::float_round_style  round_style = std::round_to_nearest;
        inline static constexpr bool                    is_iec559 = false;
        inline static constexpr bool                    is_bounded = true;
        inline static constexpr bool                    is_modulo = std::numeric_limits<B>::is_modulo;
        inline static constexpr int                     digits = std::numeric_limits<B>::digits;
        inline static constexpr int                     digits10 = 1;
        inline static constexpr int max_digits10 = fpm::detail::max_digits10(std::numeric_limits<B>::digits - F) +
            fpm::detail::max_digits10(F);
        inline static constexpr int  radix = 2;
        inline static constexpr int  min_exponent = 1 - F;
        inline static constexpr int  min_exponent10 = -fpm::detail::digits10(F);
        inline static constexpr int  max_exponent = std::numeric_limits<B>::digits - F;
        inline static constexpr int  max_exponent10 = fpm::detail::digits10(std::numeric_limits<B>::digits - F);
        inline static constexpr bool traps = true;
        inline static constexpr bool tinyness_before = false;

        static constexpr FixedType lowest() noexcept {
            return FixedType::from_raw_value(std::numeric_limits<B>::lowest());
        };

        static constexpr FixedType min() noexcept { return lowest(); }

        static constexpr FixedType max() noexcept { return FixedType::from_raw_value(std::numeric_limits<B>::max()); };

        static constexpr FixedType epsilon() noexcept { return FixedType::from_raw_value(1); };

        static constexpr FixedType round_error() noexcept { return FixedType(1) / 2; };

        static constexpr FixedType denorm_min() noexcept { return min(); }
    };

    template <IsFixedPoint FixedType>
    struct HashBase {
        using argument_type = FixedType;
        using result_type = std::size_t;
        using B = typename FixedType::base_type;

        result_type
        operator()(argument_type arg) const noexcept(noexcept(std::declval<std::hash<B>>()(arg.raw_value()))) {
            return m_hash(arg.raw_value());
        }

    private:
        std::hash<B> m_hash;
    };
} // namespace detail

namespace std {

    template <typename B, typename I, unsigned int F, bool R>
    struct hash<FixedAdapter<B, I, F, R>>: public detail::HashBase<FixedAdapter<B, I, F, R>> {};

    template <typename B, typename I, unsigned int F, bool R>
    struct numeric_limits<FixedAdapter<B, I, F, R>>: public detail::NumericLimitsBase<FixedAdapter<B, I, F, R>> {};

} // namespace std

using fixed_16_16 = FixedAdapter<std::int32_t, std::int64_t, 16>;
using fixed_24_8 = FixedAdapter<std::int32_t, std::int64_t, 8>;
using fixed_8_24 = FixedAdapter<std::int32_t, std::int64_t, 24>;