Merge pull request #9094 from lioncash/fixed

common/fixed_point: Minor interface improvements
This commit is contained in:
liamwhite 2022-10-19 19:00:59 -04:00 committed by GitHub
commit 7daf751b8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 80 additions and 115 deletions

View file

@ -34,4 +34,12 @@ concept DerivedFrom = requires {
template <typename From, typename To> template <typename From, typename To>
concept ConvertibleTo = std::is_convertible_v<From, To>; concept ConvertibleTo = std::is_convertible_v<From, To>;
// No equivalents in the stdlib
template <typename T>
concept IsArithmetic = std::is_arithmetic_v<T>;
template <typename T>
concept IsIntegral = std::is_integral_v<T>;
} // namespace Common } // namespace Common

View file

@ -12,6 +12,8 @@
#include <ostream> #include <ostream>
#include <type_traits> #include <type_traits>
#include <common/concepts.h>
namespace Common { namespace Common {
template <size_t I, size_t F> template <size_t I, size_t F>
@ -50,8 +52,8 @@ struct type_from_size<64> {
static constexpr size_t size = 64; static constexpr size_t size = 64;
using value_type = int64_t; using value_type = int64_t;
using unsigned_type = std::make_unsigned<value_type>::type; using unsigned_type = std::make_unsigned_t<value_type>;
using signed_type = std::make_signed<value_type>::type; using signed_type = std::make_signed_t<value_type>;
using next_size = type_from_size<128>; using next_size = type_from_size<128>;
}; };
@ -61,8 +63,8 @@ struct type_from_size<32> {
static constexpr size_t size = 32; static constexpr size_t size = 32;
using value_type = int32_t; using value_type = int32_t;
using unsigned_type = std::make_unsigned<value_type>::type; using unsigned_type = std::make_unsigned_t<value_type>;
using signed_type = std::make_signed<value_type>::type; using signed_type = std::make_signed_t<value_type>;
using next_size = type_from_size<64>; using next_size = type_from_size<64>;
}; };
@ -72,8 +74,8 @@ struct type_from_size<16> {
static constexpr size_t size = 16; static constexpr size_t size = 16;
using value_type = int16_t; using value_type = int16_t;
using unsigned_type = std::make_unsigned<value_type>::type; using unsigned_type = std::make_unsigned_t<value_type>;
using signed_type = std::make_signed<value_type>::type; using signed_type = std::make_signed_t<value_type>;
using next_size = type_from_size<32>; using next_size = type_from_size<32>;
}; };
@ -83,8 +85,8 @@ struct type_from_size<8> {
static constexpr size_t size = 8; static constexpr size_t size = 8;
using value_type = int8_t; using value_type = int8_t;
using unsigned_type = std::make_unsigned<value_type>::type; using unsigned_type = std::make_unsigned_t<value_type>;
using signed_type = std::make_signed<value_type>::type; using signed_type = std::make_signed_t<value_type>;
using next_size = type_from_size<16>; using next_size = type_from_size<16>;
}; };
@ -101,7 +103,7 @@ struct divide_by_zero : std::exception {};
template <size_t I, size_t F> template <size_t I, size_t F>
constexpr FixedPoint<I, F> divide( constexpr FixedPoint<I, F> divide(
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
using next_type = typename FixedPoint<I, F>::next_type; using next_type = typename FixedPoint<I, F>::next_type;
using base_type = typename FixedPoint<I, F>::base_type; using base_type = typename FixedPoint<I, F>::base_type;
@ -121,7 +123,7 @@ constexpr FixedPoint<I, F> divide(
template <size_t I, size_t F> template <size_t I, size_t F>
constexpr FixedPoint<I, F> divide( constexpr FixedPoint<I, F> divide(
FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder, FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
using unsigned_type = typename FixedPoint<I, F>::unsigned_type; using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
@ -191,7 +193,7 @@ constexpr FixedPoint<I, F> divide(
template <size_t I, size_t F> template <size_t I, size_t F>
constexpr FixedPoint<I, F> multiply( constexpr FixedPoint<I, F> multiply(
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
using next_type = typename FixedPoint<I, F>::next_type; using next_type = typename FixedPoint<I, F>::next_type;
using base_type = typename FixedPoint<I, F>::base_type; using base_type = typename FixedPoint<I, F>::base_type;
@ -210,7 +212,7 @@ constexpr FixedPoint<I, F> multiply(
template <size_t I, size_t F> template <size_t I, size_t F>
constexpr FixedPoint<I, F> multiply( constexpr FixedPoint<I, F> multiply(
FixedPoint<I, F> lhs, FixedPoint<I, F> rhs, FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) { std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
using base_type = typename FixedPoint<I, F>::base_type; using base_type = typename FixedPoint<I, F>::base_type;
@ -265,15 +267,16 @@ public:
static constexpr base_type one = base_type(1) << fractional_bits; static constexpr base_type one = base_type(1) << fractional_bits;
public: // constructors public: // constructors
FixedPoint() = default; constexpr FixedPoint() = default;
FixedPoint(const FixedPoint&) = default;
FixedPoint(FixedPoint&&) = default;
FixedPoint& operator=(const FixedPoint&) = default;
template <class Number> constexpr FixedPoint(const FixedPoint&) = default;
constexpr FixedPoint( constexpr FixedPoint& operator=(const FixedPoint&) = default;
Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr)
: data_(static_cast<base_type>(n * one)) {} constexpr FixedPoint(FixedPoint&&) noexcept = default;
constexpr FixedPoint& operator=(FixedPoint&&) noexcept = default;
template <IsArithmetic Number>
constexpr FixedPoint(Number n) : data_(static_cast<base_type>(n * one)) {}
public: // conversion public: // conversion
template <size_t I2, size_t F2> template <size_t I2, size_t F2>
@ -301,36 +304,14 @@ public:
} }
public: // comparison operators public: // comparison operators
constexpr bool operator==(FixedPoint rhs) const { friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default;
return data_ == rhs.data_;
}
constexpr bool operator!=(FixedPoint rhs) const {
return data_ != rhs.data_;
}
constexpr bool operator<(FixedPoint rhs) const {
return data_ < rhs.data_;
}
constexpr bool operator>(FixedPoint rhs) const {
return data_ > rhs.data_;
}
constexpr bool operator<=(FixedPoint rhs) const {
return data_ <= rhs.data_;
}
constexpr bool operator>=(FixedPoint rhs) const {
return data_ >= rhs.data_;
}
public: // unary operators public: // unary operators
constexpr bool operator!() const { [[nodiscard]] constexpr bool operator!() const {
return !data_; return !data_;
} }
constexpr FixedPoint operator~() const { [[nodiscard]] constexpr FixedPoint operator~() const {
// NOTE(eteran): this will often appear to "just negate" the value // NOTE(eteran): this will often appear to "just negate" the value
// that is not an error, it is because -x == (~x+1) // that is not an error, it is because -x == (~x+1)
// and that "+1" is adding an infinitesimally small fraction to the // and that "+1" is adding an infinitesimally small fraction to the
@ -338,11 +319,11 @@ public: // unary operators
return FixedPoint::from_base(~data_); return FixedPoint::from_base(~data_);
} }
constexpr FixedPoint operator-() const { [[nodiscard]] constexpr FixedPoint operator-() const {
return FixedPoint::from_base(-data_); return FixedPoint::from_base(-data_);
} }
constexpr FixedPoint operator+() const { [[nodiscard]] constexpr FixedPoint operator+() const {
return FixedPoint::from_base(+data_); return FixedPoint::from_base(+data_);
} }
@ -411,15 +392,13 @@ public: // binary math operators, effects underlying bit pattern since these
return *this; return *this;
} }
template <class Integer, template <IsIntegral Integer>
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
constexpr FixedPoint& operator>>=(Integer n) { constexpr FixedPoint& operator>>=(Integer n) {
data_ >>= n; data_ >>= n;
return *this; return *this;
} }
template <class Integer, template <IsIntegral Integer>
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
constexpr FixedPoint& operator<<=(Integer n) { constexpr FixedPoint& operator<<=(Integer n) {
data_ <<= n; data_ <<= n;
return *this; return *this;
@ -430,42 +409,42 @@ public: // conversion to basic types
data_ += (data_ & fractional_mask) >> 1; data_ += (data_ & fractional_mask) >> 1;
} }
constexpr int to_int() { [[nodiscard]] constexpr int to_int() {
round_up(); round_up();
return static_cast<int>((data_ & integer_mask) >> fractional_bits); return static_cast<int>((data_ & integer_mask) >> fractional_bits);
} }
constexpr unsigned int to_uint() const { [[nodiscard]] constexpr unsigned int to_uint() {
round_up(); round_up();
return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
} }
constexpr int64_t to_long() { [[nodiscard]] constexpr int64_t to_long() {
round_up(); round_up();
return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
} }
constexpr int to_int_floor() const { [[nodiscard]] constexpr int to_int_floor() const {
return static_cast<int>((data_ & integer_mask) >> fractional_bits); return static_cast<int>((data_ & integer_mask) >> fractional_bits);
} }
constexpr int64_t to_long_floor() { [[nodiscard]] constexpr int64_t to_long_floor() const {
return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits); return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
} }
constexpr unsigned int to_uint_floor() const { [[nodiscard]] constexpr unsigned int to_uint_floor() const {
return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits); return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
} }
constexpr float to_float() const { [[nodiscard]] constexpr float to_float() const {
return static_cast<float>(data_) / FixedPoint::one; return static_cast<float>(data_) / FixedPoint::one;
} }
constexpr double to_double() const { [[nodiscard]] constexpr double to_double() const {
return static_cast<double>(data_) / FixedPoint::one; return static_cast<double>(data_) / FixedPoint::one;
} }
constexpr base_type to_raw() const { [[nodiscard]] constexpr base_type to_raw() const {
return data_; return data_;
} }
@ -473,27 +452,27 @@ public: // conversion to basic types
data_ &= fractional_mask; data_ &= fractional_mask;
} }
constexpr base_type get_frac() const { [[nodiscard]] constexpr base_type get_frac() const {
return data_ & fractional_mask; return data_ & fractional_mask;
} }
public: public:
constexpr void swap(FixedPoint& rhs) { constexpr void swap(FixedPoint& rhs) noexcept {
using std::swap; using std::swap;
swap(data_, rhs.data_); swap(data_, rhs.data_);
} }
public: public:
base_type data_; base_type data_{};
}; };
// if we have the same fractional portion, but differing integer portions, we trivially upgrade the // if we have the same fractional portion, but differing integer portions, we trivially upgrade the
// smaller type // smaller type
template <size_t I1, size_t I2, size_t F> template <size_t I1, size_t I2, size_t F>
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator+( constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator+(
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
const T l = T::from_base(lhs.to_raw()); const T l = T::from_base(lhs.to_raw());
const T r = T::from_base(rhs.to_raw()); const T r = T::from_base(rhs.to_raw());
@ -501,10 +480,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2,
} }
template <size_t I1, size_t I2, size_t F> template <size_t I1, size_t I2, size_t F>
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator-( constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator-(
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
const T l = T::from_base(lhs.to_raw()); const T l = T::from_base(lhs.to_raw());
const T r = T::from_base(rhs.to_raw()); const T r = T::from_base(rhs.to_raw());
@ -512,10 +491,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2,
} }
template <size_t I1, size_t I2, size_t F> template <size_t I1, size_t I2, size_t F>
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator*( constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator*(
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
const T l = T::from_base(lhs.to_raw()); const T l = T::from_base(lhs.to_raw());
const T r = T::from_base(rhs.to_raw()); const T r = T::from_base(rhs.to_raw());
@ -523,10 +502,10 @@ constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2,
} }
template <size_t I1, size_t I2, size_t F> template <size_t I1, size_t I2, size_t F>
constexpr typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type operator/( constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator/(
FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) { FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type; using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
const T l = T::from_base(lhs.to_raw()); const T l = T::from_base(lhs.to_raw());
const T r = T::from_base(rhs.to_raw()); const T r = T::from_base(rhs.to_raw());
@ -561,54 +540,46 @@ constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs)
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) { constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
lhs += FixedPoint<I, F>(rhs); lhs += FixedPoint<I, F>(rhs);
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) { constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
lhs -= FixedPoint<I, F>(rhs); lhs -= FixedPoint<I, F>(rhs);
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) { constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
lhs *= FixedPoint<I, F>(rhs); lhs *= FixedPoint<I, F>(rhs);
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) { constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
lhs /= FixedPoint<I, F>(rhs); lhs /= FixedPoint<I, F>(rhs);
return lhs; return lhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) { constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
FixedPoint<I, F> tmp(lhs); FixedPoint<I, F> tmp(lhs);
tmp += rhs; tmp += rhs;
return tmp; return tmp;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) { constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
FixedPoint<I, F> tmp(lhs); FixedPoint<I, F> tmp(lhs);
tmp -= rhs; tmp -= rhs;
return tmp; return tmp;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) { constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
FixedPoint<I, F> tmp(lhs); FixedPoint<I, F> tmp(lhs);
tmp *= rhs; tmp *= rhs;
return tmp; return tmp;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) { constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
FixedPoint<I, F> tmp(lhs); FixedPoint<I, F> tmp(lhs);
tmp /= rhs; tmp /= rhs;
@ -616,78 +587,64 @@ constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
} }
// shift operators // shift operators
template <size_t I, size_t F, class Integer, template <size_t I, size_t F, IsIntegral Integer>
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) { constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
lhs <<= rhs; lhs <<= rhs;
return lhs; return lhs;
} }
template <size_t I, size_t F, class Integer, template <size_t I, size_t F, IsIntegral Integer>
class = typename std::enable_if<std::is_integral<Integer>::value>::type>
constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) { constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
lhs >>= rhs; lhs >>= rhs;
return lhs; return lhs;
} }
// comparison operators // comparison operators
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
return lhs > FixedPoint<I, F>(rhs); return lhs > FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
return lhs < FixedPoint<I, F>(rhs); return lhs < FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
return lhs >= FixedPoint<I, F>(rhs); return lhs >= FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
return lhs <= FixedPoint<I, F>(rhs); return lhs <= FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
return lhs == FixedPoint<I, F>(rhs); return lhs == FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) { constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
return lhs != FixedPoint<I, F>(rhs); return lhs != FixedPoint<I, F>(rhs);
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) > rhs; return FixedPoint<I, F>(lhs) > rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) < rhs; return FixedPoint<I, F>(lhs) < rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) >= rhs; return FixedPoint<I, F>(lhs) >= rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) <= rhs; return FixedPoint<I, F>(lhs) <= rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) == rhs; return FixedPoint<I, F>(lhs) == rhs;
} }
template <size_t I, size_t F, class Number, template <size_t I, size_t F, IsArithmetic Number>
class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) { constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
return FixedPoint<I, F>(lhs) != rhs; return FixedPoint<I, F>(lhs) != rhs;
} }