fixed_point.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. // SPDX-FileCopyrightText: 2015 Evan Teran
  2. // SPDX-License-Identifier: MIT
  3. // From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h
  4. // See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math
  5. #pragma once
  6. #include <cstddef> // for size_t
  7. #include <cstdint>
  8. #include <exception>
  9. #include <ostream>
  10. #include <type_traits>
  11. #include <common/concepts.h>
  12. namespace Common {
  13. template <size_t I, size_t F>
  14. class FixedPoint;
  15. namespace detail {
  16. // helper templates to make magic with types :)
  17. // these allow us to determine reasonable types from
  18. // a desired size, they also let us infer the next largest type
  19. // from a type which is nice for the division op
  20. template <size_t T>
  21. struct type_from_size {
  22. using value_type = void;
  23. using unsigned_type = void;
  24. using signed_type = void;
  25. static constexpr bool is_specialized = false;
  26. };
  27. #if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__)
  28. template <>
  29. struct type_from_size<128> {
  30. static constexpr bool is_specialized = true;
  31. static constexpr size_t size = 128;
  32. using value_type = __int128;
  33. using unsigned_type = unsigned __int128;
  34. using signed_type = __int128;
  35. using next_size = type_from_size<256>;
  36. };
  37. #endif
  38. template <>
  39. struct type_from_size<64> {
  40. static constexpr bool is_specialized = true;
  41. static constexpr size_t size = 64;
  42. using value_type = int64_t;
  43. using unsigned_type = std::make_unsigned_t<value_type>;
  44. using signed_type = std::make_signed_t<value_type>;
  45. using next_size = type_from_size<128>;
  46. };
  47. template <>
  48. struct type_from_size<32> {
  49. static constexpr bool is_specialized = true;
  50. static constexpr size_t size = 32;
  51. using value_type = int32_t;
  52. using unsigned_type = std::make_unsigned_t<value_type>;
  53. using signed_type = std::make_signed_t<value_type>;
  54. using next_size = type_from_size<64>;
  55. };
  56. template <>
  57. struct type_from_size<16> {
  58. static constexpr bool is_specialized = true;
  59. static constexpr size_t size = 16;
  60. using value_type = int16_t;
  61. using unsigned_type = std::make_unsigned_t<value_type>;
  62. using signed_type = std::make_signed_t<value_type>;
  63. using next_size = type_from_size<32>;
  64. };
  65. template <>
  66. struct type_from_size<8> {
  67. static constexpr bool is_specialized = true;
  68. static constexpr size_t size = 8;
  69. using value_type = int8_t;
  70. using unsigned_type = std::make_unsigned_t<value_type>;
  71. using signed_type = std::make_signed_t<value_type>;
  72. using next_size = type_from_size<16>;
  73. };
  74. // this is to assist in adding support for non-native base
  75. // types (for adding big-int support), this should be fine
  76. // unless your bit-int class doesn't nicely support casting
  77. template <class B, class N>
  78. constexpr B next_to_base(N rhs) {
  79. return static_cast<B>(rhs);
  80. }
  81. struct divide_by_zero : std::exception {};
  82. template <size_t I, size_t F>
  83. constexpr FixedPoint<I, F> divide(
  84. FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
  85. std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
  86. using next_type = typename FixedPoint<I, F>::next_type;
  87. using base_type = typename FixedPoint<I, F>::base_type;
  88. constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
  89. next_type t(numerator.to_raw());
  90. t <<= fractional_bits;
  91. FixedPoint<I, F> quotient;
  92. quotient = FixedPoint<I, F>::from_base(next_to_base<base_type>(t / denominator.to_raw()));
  93. remainder = FixedPoint<I, F>::from_base(next_to_base<base_type>(t % denominator.to_raw()));
  94. return quotient;
  95. }
  96. template <size_t I, size_t F>
  97. constexpr FixedPoint<I, F> divide(
  98. FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
  99. std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
  100. using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
  101. constexpr int bits = FixedPoint<I, F>::total_bits;
  102. if (denominator == 0) {
  103. throw divide_by_zero();
  104. } else {
  105. int sign = 0;
  106. FixedPoint<I, F> quotient;
  107. if (numerator < 0) {
  108. sign ^= 1;
  109. numerator = -numerator;
  110. }
  111. if (denominator < 0) {
  112. sign ^= 1;
  113. denominator = -denominator;
  114. }
  115. unsigned_type n = numerator.to_raw();
  116. unsigned_type d = denominator.to_raw();
  117. unsigned_type x = 1;
  118. unsigned_type answer = 0;
  119. // egyptian division algorithm
  120. while ((n >= d) && (((d >> (bits - 1)) & 1) == 0)) {
  121. x <<= 1;
  122. d <<= 1;
  123. }
  124. while (x != 0) {
  125. if (n >= d) {
  126. n -= d;
  127. answer += x;
  128. }
  129. x >>= 1;
  130. d >>= 1;
  131. }
  132. unsigned_type l1 = n;
  133. unsigned_type l2 = denominator.to_raw();
  134. // calculate the lower bits (needs to be unsigned)
  135. while (l1 >> (bits - F) > 0) {
  136. l1 >>= 1;
  137. l2 >>= 1;
  138. }
  139. const unsigned_type lo = (l1 << F) / l2;
  140. quotient = FixedPoint<I, F>::from_base((answer << F) | lo);
  141. remainder = n;
  142. if (sign) {
  143. quotient = -quotient;
  144. }
  145. return quotient;
  146. }
  147. }
  148. // this is the usual implementation of multiplication
  149. template <size_t I, size_t F>
  150. constexpr FixedPoint<I, F> multiply(
  151. FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
  152. std::enable_if_t<type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
  153. using next_type = typename FixedPoint<I, F>::next_type;
  154. using base_type = typename FixedPoint<I, F>::base_type;
  155. constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
  156. next_type t(static_cast<next_type>(lhs.to_raw()) * static_cast<next_type>(rhs.to_raw()));
  157. t >>= fractional_bits;
  158. return FixedPoint<I, F>::from_base(next_to_base<base_type>(t));
  159. }
  160. // this is the fall back version we use when we don't have a next size
  161. // it is slightly slower, but is more robust since it doesn't
  162. // require and upgraded type
  163. template <size_t I, size_t F>
  164. constexpr FixedPoint<I, F> multiply(
  165. FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
  166. std::enable_if_t<!type_from_size<I + F>::next_size::is_specialized>* = nullptr) {
  167. using base_type = typename FixedPoint<I, F>::base_type;
  168. constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
  169. constexpr base_type integer_mask = FixedPoint<I, F>::integer_mask;
  170. constexpr base_type fractional_mask = FixedPoint<I, F>::fractional_mask;
  171. // more costly but doesn't need a larger type
  172. const base_type a_hi = (lhs.to_raw() & integer_mask) >> fractional_bits;
  173. const base_type b_hi = (rhs.to_raw() & integer_mask) >> fractional_bits;
  174. const base_type a_lo = (lhs.to_raw() & fractional_mask);
  175. const base_type b_lo = (rhs.to_raw() & fractional_mask);
  176. const base_type x1 = a_hi * b_hi;
  177. const base_type x2 = a_hi * b_lo;
  178. const base_type x3 = a_lo * b_hi;
  179. const base_type x4 = a_lo * b_lo;
  180. return FixedPoint<I, F>::from_base((x1 << fractional_bits) + (x3 + x2) +
  181. (x4 >> fractional_bits));
  182. }
  183. } // namespace detail
  184. template <size_t I, size_t F>
  185. class FixedPoint {
  186. static_assert(detail::type_from_size<I + F>::is_specialized, "invalid combination of sizes");
  187. public:
  188. static constexpr size_t fractional_bits = F;
  189. static constexpr size_t integer_bits = I;
  190. static constexpr size_t total_bits = I + F;
  191. using base_type_info = detail::type_from_size<total_bits>;
  192. using base_type = typename base_type_info::value_type;
  193. using next_type = typename base_type_info::next_size::value_type;
  194. using unsigned_type = typename base_type_info::unsigned_type;
  195. public:
  196. #ifdef __GNUC__
  197. #pragma GCC diagnostic push
  198. #pragma GCC diagnostic ignored "-Woverflow"
  199. #endif
  200. static constexpr base_type fractional_mask =
  201. ~(static_cast<unsigned_type>(~base_type(0)) << fractional_bits);
  202. static constexpr base_type integer_mask = ~fractional_mask;
  203. #ifdef __GNUC__
  204. #pragma GCC diagnostic pop
  205. #endif
  206. public:
  207. static constexpr base_type one = base_type(1) << fractional_bits;
  208. public: // constructors
  209. constexpr FixedPoint() = default;
  210. constexpr FixedPoint(const FixedPoint&) = default;
  211. constexpr FixedPoint& operator=(const FixedPoint&) = default;
  212. constexpr FixedPoint(FixedPoint&&) noexcept = default;
  213. constexpr FixedPoint& operator=(FixedPoint&&) noexcept = default;
  214. template <IsArithmetic Number>
  215. constexpr FixedPoint(Number n) : data_(static_cast<base_type>(n * one)) {}
  216. public: // conversion
  217. template <size_t I2, size_t F2>
  218. constexpr explicit FixedPoint(FixedPoint<I2, F2> other) {
  219. static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types");
  220. using T = FixedPoint<I2, F2>;
  221. const base_type fractional = (other.data_ & T::fractional_mask);
  222. const base_type integer = (other.data_ & T::integer_mask) >> T::fractional_bits;
  223. data_ =
  224. (integer << fractional_bits) | (fractional << (fractional_bits - T::fractional_bits));
  225. }
  226. private:
  227. // this makes it simpler to create a FixedPoint point object from
  228. // a native type without scaling
  229. // use "FixedPoint::from_base" in order to perform this.
  230. struct NoScale {};
  231. constexpr FixedPoint(base_type n, const NoScale&) : data_(n) {}
  232. public:
  233. static constexpr FixedPoint from_base(base_type n) {
  234. return FixedPoint(n, NoScale());
  235. }
  236. public: // comparison operators
  237. friend constexpr auto operator<=>(FixedPoint lhs, FixedPoint rhs) = default;
  238. public: // unary operators
  239. [[nodiscard]] constexpr bool operator!() const {
  240. return !data_;
  241. }
  242. [[nodiscard]] constexpr FixedPoint operator~() const {
  243. // NOTE(eteran): this will often appear to "just negate" the value
  244. // that is not an error, it is because -x == (~x+1)
  245. // and that "+1" is adding an infinitesimally small fraction to the
  246. // complimented value
  247. return FixedPoint::from_base(~data_);
  248. }
  249. [[nodiscard]] constexpr FixedPoint operator-() const {
  250. return FixedPoint::from_base(-data_);
  251. }
  252. [[nodiscard]] constexpr FixedPoint operator+() const {
  253. return FixedPoint::from_base(+data_);
  254. }
  255. constexpr FixedPoint& operator++() {
  256. data_ += one;
  257. return *this;
  258. }
  259. constexpr FixedPoint& operator--() {
  260. data_ -= one;
  261. return *this;
  262. }
  263. constexpr FixedPoint operator++(int) {
  264. FixedPoint tmp(*this);
  265. data_ += one;
  266. return tmp;
  267. }
  268. constexpr FixedPoint operator--(int) {
  269. FixedPoint tmp(*this);
  270. data_ -= one;
  271. return tmp;
  272. }
  273. public: // basic math operators
  274. constexpr FixedPoint& operator+=(FixedPoint n) {
  275. data_ += n.data_;
  276. return *this;
  277. }
  278. constexpr FixedPoint& operator-=(FixedPoint n) {
  279. data_ -= n.data_;
  280. return *this;
  281. }
  282. constexpr FixedPoint& operator*=(FixedPoint n) {
  283. return assign(detail::multiply(*this, n));
  284. }
  285. constexpr FixedPoint& operator/=(FixedPoint n) {
  286. FixedPoint temp;
  287. return assign(detail::divide(*this, n, temp));
  288. }
  289. private:
  290. constexpr FixedPoint& assign(FixedPoint rhs) {
  291. data_ = rhs.data_;
  292. return *this;
  293. }
  294. public: // binary math operators, effects underlying bit pattern since these
  295. // don't really typically make sense for non-integer values
  296. constexpr FixedPoint& operator&=(FixedPoint n) {
  297. data_ &= n.data_;
  298. return *this;
  299. }
  300. constexpr FixedPoint& operator|=(FixedPoint n) {
  301. data_ |= n.data_;
  302. return *this;
  303. }
  304. constexpr FixedPoint& operator^=(FixedPoint n) {
  305. data_ ^= n.data_;
  306. return *this;
  307. }
  308. template <IsIntegral Integer>
  309. constexpr FixedPoint& operator>>=(Integer n) {
  310. data_ >>= n;
  311. return *this;
  312. }
  313. template <IsIntegral Integer>
  314. constexpr FixedPoint& operator<<=(Integer n) {
  315. data_ <<= n;
  316. return *this;
  317. }
  318. public: // conversion to basic types
  319. constexpr void round_up() {
  320. data_ += (data_ & fractional_mask) >> 1;
  321. }
  322. [[nodiscard]] constexpr int to_int() {
  323. round_up();
  324. return static_cast<int>((data_ & integer_mask) >> fractional_bits);
  325. }
  326. [[nodiscard]] constexpr unsigned int to_uint() {
  327. round_up();
  328. return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
  329. }
  330. [[nodiscard]] constexpr int64_t to_long() {
  331. round_up();
  332. return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
  333. }
  334. [[nodiscard]] constexpr int to_int_floor() const {
  335. return static_cast<int>((data_ & integer_mask) >> fractional_bits);
  336. }
  337. [[nodiscard]] constexpr int64_t to_long_floor() const {
  338. return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
  339. }
  340. [[nodiscard]] constexpr unsigned int to_uint_floor() const {
  341. return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
  342. }
  343. [[nodiscard]] constexpr float to_float() const {
  344. return static_cast<float>(data_) / FixedPoint::one;
  345. }
  346. [[nodiscard]] constexpr double to_double() const {
  347. return static_cast<double>(data_) / FixedPoint::one;
  348. }
  349. [[nodiscard]] constexpr base_type to_raw() const {
  350. return data_;
  351. }
  352. constexpr void clear_int() {
  353. data_ &= fractional_mask;
  354. }
  355. [[nodiscard]] constexpr base_type get_frac() const {
  356. return data_ & fractional_mask;
  357. }
  358. public:
  359. constexpr void swap(FixedPoint& rhs) noexcept {
  360. using std::swap;
  361. swap(data_, rhs.data_);
  362. }
  363. public:
  364. base_type data_{};
  365. };
  366. // if we have the same fractional portion, but differing integer portions, we trivially upgrade the
  367. // smaller type
  368. template <size_t I1, size_t I2, size_t F>
  369. constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator+(
  370. FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
  371. using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
  372. const T l = T::from_base(lhs.to_raw());
  373. const T r = T::from_base(rhs.to_raw());
  374. return l + r;
  375. }
  376. template <size_t I1, size_t I2, size_t F>
  377. constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator-(
  378. FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
  379. using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
  380. const T l = T::from_base(lhs.to_raw());
  381. const T r = T::from_base(rhs.to_raw());
  382. return l - r;
  383. }
  384. template <size_t I1, size_t I2, size_t F>
  385. constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator*(
  386. FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
  387. using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
  388. const T l = T::from_base(lhs.to_raw());
  389. const T r = T::from_base(rhs.to_raw());
  390. return l * r;
  391. }
  392. template <size_t I1, size_t I2, size_t F>
  393. constexpr std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>> operator/(
  394. FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
  395. using T = std::conditional_t<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>;
  396. const T l = T::from_base(lhs.to_raw());
  397. const T r = T::from_base(rhs.to_raw());
  398. return l / r;
  399. }
  400. template <size_t I, size_t F>
  401. std::ostream& operator<<(std::ostream& os, FixedPoint<I, F> f) {
  402. os << f.to_double();
  403. return os;
  404. }
  405. // basic math operators
  406. template <size_t I, size_t F>
  407. constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
  408. lhs += rhs;
  409. return lhs;
  410. }
  411. template <size_t I, size_t F>
  412. constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
  413. lhs -= rhs;
  414. return lhs;
  415. }
  416. template <size_t I, size_t F>
  417. constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
  418. lhs *= rhs;
  419. return lhs;
  420. }
  421. template <size_t I, size_t F>
  422. constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
  423. lhs /= rhs;
  424. return lhs;
  425. }
  426. template <size_t I, size_t F, IsArithmetic Number>
  427. constexpr FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
  428. lhs += FixedPoint<I, F>(rhs);
  429. return lhs;
  430. }
  431. template <size_t I, size_t F, IsArithmetic Number>
  432. constexpr FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
  433. lhs -= FixedPoint<I, F>(rhs);
  434. return lhs;
  435. }
  436. template <size_t I, size_t F, IsArithmetic Number>
  437. constexpr FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
  438. lhs *= FixedPoint<I, F>(rhs);
  439. return lhs;
  440. }
  441. template <size_t I, size_t F, IsArithmetic Number>
  442. constexpr FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
  443. lhs /= FixedPoint<I, F>(rhs);
  444. return lhs;
  445. }
  446. template <size_t I, size_t F, IsArithmetic Number>
  447. constexpr FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
  448. FixedPoint<I, F> tmp(lhs);
  449. tmp += rhs;
  450. return tmp;
  451. }
  452. template <size_t I, size_t F, IsArithmetic Number>
  453. constexpr FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
  454. FixedPoint<I, F> tmp(lhs);
  455. tmp -= rhs;
  456. return tmp;
  457. }
  458. template <size_t I, size_t F, IsArithmetic Number>
  459. constexpr FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
  460. FixedPoint<I, F> tmp(lhs);
  461. tmp *= rhs;
  462. return tmp;
  463. }
  464. template <size_t I, size_t F, IsArithmetic Number>
  465. constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
  466. FixedPoint<I, F> tmp(lhs);
  467. tmp /= rhs;
  468. return tmp;
  469. }
  470. // shift operators
  471. template <size_t I, size_t F, IsIntegral Integer>
  472. constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
  473. lhs <<= rhs;
  474. return lhs;
  475. }
  476. template <size_t I, size_t F, IsIntegral Integer>
  477. constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
  478. lhs >>= rhs;
  479. return lhs;
  480. }
  481. // comparison operators
  482. template <size_t I, size_t F, IsArithmetic Number>
  483. constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
  484. return lhs > FixedPoint<I, F>(rhs);
  485. }
  486. template <size_t I, size_t F, IsArithmetic Number>
  487. constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
  488. return lhs < FixedPoint<I, F>(rhs);
  489. }
  490. template <size_t I, size_t F, IsArithmetic Number>
  491. constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
  492. return lhs >= FixedPoint<I, F>(rhs);
  493. }
  494. template <size_t I, size_t F, IsArithmetic Number>
  495. constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
  496. return lhs <= FixedPoint<I, F>(rhs);
  497. }
  498. template <size_t I, size_t F, IsArithmetic Number>
  499. constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
  500. return lhs == FixedPoint<I, F>(rhs);
  501. }
  502. template <size_t I, size_t F, IsArithmetic Number>
  503. constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
  504. return lhs != FixedPoint<I, F>(rhs);
  505. }
  506. template <size_t I, size_t F, IsArithmetic Number>
  507. constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
  508. return FixedPoint<I, F>(lhs) > rhs;
  509. }
  510. template <size_t I, size_t F, IsArithmetic Number>
  511. constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
  512. return FixedPoint<I, F>(lhs) < rhs;
  513. }
  514. template <size_t I, size_t F, IsArithmetic Number>
  515. constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
  516. return FixedPoint<I, F>(lhs) >= rhs;
  517. }
  518. template <size_t I, size_t F, IsArithmetic Number>
  519. constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
  520. return FixedPoint<I, F>(lhs) <= rhs;
  521. }
  522. template <size_t I, size_t F, IsArithmetic Number>
  523. constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
  524. return FixedPoint<I, F>(lhs) == rhs;
  525. }
  526. template <size_t I, size_t F, IsArithmetic Number>
  527. constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
  528. return FixedPoint<I, F>(lhs) != rhs;
  529. }
  530. } // namespace Common