summaryrefslogtreecommitdiff
path: root/src/common/fixed_point.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/fixed_point.h')
-rw-r--r--src/common/fixed_point.h706
1 files changed, 706 insertions, 0 deletions
diff --git a/src/common/fixed_point.h b/src/common/fixed_point.h
new file mode 100644
index 000000000..4a0f72cc9
--- /dev/null
+++ b/src/common/fixed_point.h
@@ -0,0 +1,706 @@
1// SPDX-FileCopyrightText: 2015 Evan Teran
2// SPDX-License-Identifier: MIT
3
4// From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h
5// See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math
6
7#ifndef FIXED_H_
8#define FIXED_H_
9
10#if __cplusplus >= 201402L
11#define CONSTEXPR14 constexpr
12#else
13#define CONSTEXPR14
14#endif
15
16#include <cstddef> // for size_t
17#include <cstdint>
18#include <exception>
19#include <ostream>
20#include <type_traits>
21
22namespace Common {
23
24template <size_t I, size_t F>
25class FixedPoint;
26
27namespace detail {
28
29// helper templates to make magic with types :)
30// these allow us to determine resonable types from
31// a desired size, they also let us infer the next largest type
32// from a type which is nice for the division op
33template <size_t T>
34struct type_from_size {
35 using value_type = void;
36 using unsigned_type = void;
37 using signed_type = void;
38 static constexpr bool is_specialized = false;
39};
40
41#if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__)
42template <>
43struct type_from_size<128> {
44 static constexpr bool is_specialized = true;
45 static constexpr size_t size = 128;
46
47 using value_type = __int128;
48 using unsigned_type = unsigned __int128;
49 using signed_type = __int128;
50 using next_size = type_from_size<256>;
51};
52#endif
53
54template <>
55struct type_from_size<64> {
56 static constexpr bool is_specialized = true;
57 static constexpr size_t size = 64;
58
59 using value_type = int64_t;
60 using unsigned_type = std::make_unsigned<value_type>::type;
61 using signed_type = std::make_signed<value_type>::type;
62 using next_size = type_from_size<128>;
63};
64
65template <>
66struct type_from_size<32> {
67 static constexpr bool is_specialized = true;
68 static constexpr size_t size = 32;
69
70 using value_type = int32_t;
71 using unsigned_type = std::make_unsigned<value_type>::type;
72 using signed_type = std::make_signed<value_type>::type;
73 using next_size = type_from_size<64>;
74};
75
76template <>
77struct type_from_size<16> {
78 static constexpr bool is_specialized = true;
79 static constexpr size_t size = 16;
80
81 using value_type = int16_t;
82 using unsigned_type = std::make_unsigned<value_type>::type;
83 using signed_type = std::make_signed<value_type>::type;
84 using next_size = type_from_size<32>;
85};
86
87template <>
88struct type_from_size<8> {
89 static constexpr bool is_specialized = true;
90 static constexpr size_t size = 8;
91
92 using value_type = int8_t;
93 using unsigned_type = std::make_unsigned<value_type>::type;
94 using signed_type = std::make_signed<value_type>::type;
95 using next_size = type_from_size<16>;
96};
97
98// this is to assist in adding support for non-native base
99// types (for adding big-int support), this should be fine
100// unless your bit-int class doesn't nicely support casting
101template <class B, class N>
102constexpr B next_to_base(N rhs) {
103 return static_cast<B>(rhs);
104}
105
106struct divide_by_zero : std::exception {};
107
108template <size_t I, size_t F>
109CONSTEXPR14 FixedPoint<I, F> divide(
110 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
111 typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
112
113 using next_type = typename FixedPoint<I, F>::next_type;
114 using base_type = typename FixedPoint<I, F>::base_type;
115 constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
116
117 next_type t(numerator.to_raw());
118 t <<= fractional_bits;
119
120 FixedPoint<I, F> quotient;
121
122 quotient = FixedPoint<I, F>::from_base(next_to_base<base_type>(t / denominator.to_raw()));
123 remainder = FixedPoint<I, F>::from_base(next_to_base<base_type>(t % denominator.to_raw()));
124
125 return quotient;
126}
127
128template <size_t I, size_t F>
129CONSTEXPR14 FixedPoint<I, F> divide(
130 FixedPoint<I, F> numerator, FixedPoint<I, F> denominator, FixedPoint<I, F>& remainder,
131 typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
132
133 using unsigned_type = typename FixedPoint<I, F>::unsigned_type;
134
135 constexpr int bits = FixedPoint<I, F>::total_bits;
136
137 if (denominator == 0) {
138 throw divide_by_zero();
139 } else {
140
141 int sign = 0;
142
143 FixedPoint<I, F> quotient;
144
145 if (numerator < 0) {
146 sign ^= 1;
147 numerator = -numerator;
148 }
149
150 if (denominator < 0) {
151 sign ^= 1;
152 denominator = -denominator;
153 }
154
155 unsigned_type n = numerator.to_raw();
156 unsigned_type d = denominator.to_raw();
157 unsigned_type x = 1;
158 unsigned_type answer = 0;
159
160 // egyptian division algorithm
161 while ((n >= d) && (((d >> (bits - 1)) & 1) == 0)) {
162 x <<= 1;
163 d <<= 1;
164 }
165
166 while (x != 0) {
167 if (n >= d) {
168 n -= d;
169 answer += x;
170 }
171
172 x >>= 1;
173 d >>= 1;
174 }
175
176 unsigned_type l1 = n;
177 unsigned_type l2 = denominator.to_raw();
178
179 // calculate the lower bits (needs to be unsigned)
180 while (l1 >> (bits - F) > 0) {
181 l1 >>= 1;
182 l2 >>= 1;
183 }
184 const unsigned_type lo = (l1 << F) / l2;
185
186 quotient = FixedPoint<I, F>::from_base((answer << F) | lo);
187 remainder = n;
188
189 if (sign) {
190 quotient = -quotient;
191 }
192
193 return quotient;
194 }
195}
196
197// this is the usual implementation of multiplication
198template <size_t I, size_t F>
199CONSTEXPR14 FixedPoint<I, F> multiply(
200 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
201 typename std::enable_if<type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
202
203 using next_type = typename FixedPoint<I, F>::next_type;
204 using base_type = typename FixedPoint<I, F>::base_type;
205
206 constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
207
208 next_type t(static_cast<next_type>(lhs.to_raw()) * static_cast<next_type>(rhs.to_raw()));
209 t >>= fractional_bits;
210
211 return FixedPoint<I, F>::from_base(next_to_base<base_type>(t));
212}
213
214// this is the fall back version we use when we don't have a next size
215// it is slightly slower, but is more robust since it doesn't
216// require and upgraded type
217template <size_t I, size_t F>
218CONSTEXPR14 FixedPoint<I, F> multiply(
219 FixedPoint<I, F> lhs, FixedPoint<I, F> rhs,
220 typename std::enable_if<!type_from_size<I + F>::next_size::is_specialized>::type* = nullptr) {
221
222 using base_type = typename FixedPoint<I, F>::base_type;
223
224 constexpr size_t fractional_bits = FixedPoint<I, F>::fractional_bits;
225 constexpr base_type integer_mask = FixedPoint<I, F>::integer_mask;
226 constexpr base_type fractional_mask = FixedPoint<I, F>::fractional_mask;
227
228 // more costly but doesn't need a larger type
229 const base_type a_hi = (lhs.to_raw() & integer_mask) >> fractional_bits;
230 const base_type b_hi = (rhs.to_raw() & integer_mask) >> fractional_bits;
231 const base_type a_lo = (lhs.to_raw() & fractional_mask);
232 const base_type b_lo = (rhs.to_raw() & fractional_mask);
233
234 const base_type x1 = a_hi * b_hi;
235 const base_type x2 = a_hi * b_lo;
236 const base_type x3 = a_lo * b_hi;
237 const base_type x4 = a_lo * b_lo;
238
239 return FixedPoint<I, F>::from_base((x1 << fractional_bits) + (x3 + x2) +
240 (x4 >> fractional_bits));
241}
242} // namespace detail
243
244template <size_t I, size_t F>
245class FixedPoint {
246 static_assert(detail::type_from_size<I + F>::is_specialized, "invalid combination of sizes");
247
248public:
249 static constexpr size_t fractional_bits = F;
250 static constexpr size_t integer_bits = I;
251 static constexpr size_t total_bits = I + F;
252
253 using base_type_info = detail::type_from_size<total_bits>;
254
255 using base_type = typename base_type_info::value_type;
256 using next_type = typename base_type_info::next_size::value_type;
257 using unsigned_type = typename base_type_info::unsigned_type;
258
259public:
260#ifdef __GNUC__
261#pragma GCC diagnostic push
262#pragma GCC diagnostic ignored "-Woverflow"
263#endif
264 static constexpr base_type fractional_mask =
265 ~(static_cast<unsigned_type>(~base_type(0)) << fractional_bits);
266 static constexpr base_type integer_mask = ~fractional_mask;
267#ifdef __GNUC__
268#pragma GCC diagnostic pop
269#endif
270
271public:
272 static constexpr base_type one = base_type(1) << fractional_bits;
273
274public: // constructors
275 FixedPoint() = default;
276 FixedPoint(const FixedPoint&) = default;
277 FixedPoint(FixedPoint&&) = default;
278 FixedPoint& operator=(const FixedPoint&) = default;
279
280 template <class Number>
281 constexpr FixedPoint(
282 Number n, typename std::enable_if<std::is_arithmetic<Number>::value>::type* = nullptr)
283 : data_(static_cast<base_type>(n * one)) {}
284
285public: // conversion
286 template <size_t I2, size_t F2>
287 CONSTEXPR14 explicit FixedPoint(FixedPoint<I2, F2> other) {
288 static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types");
289 using T = FixedPoint<I2, F2>;
290
291 const base_type fractional = (other.data_ & T::fractional_mask);
292 const base_type integer = (other.data_ & T::integer_mask) >> T::fractional_bits;
293 data_ =
294 (integer << fractional_bits) | (fractional << (fractional_bits - T::fractional_bits));
295 }
296
297private:
298 // this makes it simpler to create a FixedPoint point object from
299 // a native type without scaling
300 // use "FixedPoint::from_base" in order to perform this.
301 struct NoScale {};
302
303 constexpr FixedPoint(base_type n, const NoScale&) : data_(n) {}
304
305public:
306 static constexpr FixedPoint from_base(base_type n) {
307 return FixedPoint(n, NoScale());
308 }
309
310public: // comparison operators
311 constexpr bool operator==(FixedPoint rhs) const {
312 return data_ == rhs.data_;
313 }
314
315 constexpr bool operator!=(FixedPoint rhs) const {
316 return data_ != rhs.data_;
317 }
318
319 constexpr bool operator<(FixedPoint rhs) const {
320 return data_ < rhs.data_;
321 }
322
323 constexpr bool operator>(FixedPoint rhs) const {
324 return data_ > rhs.data_;
325 }
326
327 constexpr bool operator<=(FixedPoint rhs) const {
328 return data_ <= rhs.data_;
329 }
330
331 constexpr bool operator>=(FixedPoint rhs) const {
332 return data_ >= rhs.data_;
333 }
334
335public: // unary operators
336 constexpr bool operator!() const {
337 return !data_;
338 }
339
340 constexpr FixedPoint operator~() const {
341 // NOTE(eteran): this will often appear to "just negate" the value
342 // that is not an error, it is because -x == (~x+1)
343 // and that "+1" is adding an infinitesimally small fraction to the
344 // complimented value
345 return FixedPoint::from_base(~data_);
346 }
347
348 constexpr FixedPoint operator-() const {
349 return FixedPoint::from_base(-data_);
350 }
351
352 constexpr FixedPoint operator+() const {
353 return FixedPoint::from_base(+data_);
354 }
355
356 CONSTEXPR14 FixedPoint& operator++() {
357 data_ += one;
358 return *this;
359 }
360
361 CONSTEXPR14 FixedPoint& operator--() {
362 data_ -= one;
363 return *this;
364 }
365
366 CONSTEXPR14 FixedPoint operator++(int) {
367 FixedPoint tmp(*this);
368 data_ += one;
369 return tmp;
370 }
371
372 CONSTEXPR14 FixedPoint operator--(int) {
373 FixedPoint tmp(*this);
374 data_ -= one;
375 return tmp;
376 }
377
378public: // basic math operators
379 CONSTEXPR14 FixedPoint& operator+=(FixedPoint n) {
380 data_ += n.data_;
381 return *this;
382 }
383
384 CONSTEXPR14 FixedPoint& operator-=(FixedPoint n) {
385 data_ -= n.data_;
386 return *this;
387 }
388
389 CONSTEXPR14 FixedPoint& operator*=(FixedPoint n) {
390 return assign(detail::multiply(*this, n));
391 }
392
393 CONSTEXPR14 FixedPoint& operator/=(FixedPoint n) {
394 FixedPoint temp;
395 return assign(detail::divide(*this, n, temp));
396 }
397
398private:
399 CONSTEXPR14 FixedPoint& assign(FixedPoint rhs) {
400 data_ = rhs.data_;
401 return *this;
402 }
403
404public: // binary math operators, effects underlying bit pattern since these
405 // don't really typically make sense for non-integer values
406 CONSTEXPR14 FixedPoint& operator&=(FixedPoint n) {
407 data_ &= n.data_;
408 return *this;
409 }
410
411 CONSTEXPR14 FixedPoint& operator|=(FixedPoint n) {
412 data_ |= n.data_;
413 return *this;
414 }
415
416 CONSTEXPR14 FixedPoint& operator^=(FixedPoint n) {
417 data_ ^= n.data_;
418 return *this;
419 }
420
421 template <class Integer,
422 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
423 CONSTEXPR14 FixedPoint& operator>>=(Integer n) {
424 data_ >>= n;
425 return *this;
426 }
427
428 template <class Integer,
429 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
430 CONSTEXPR14 FixedPoint& operator<<=(Integer n) {
431 data_ <<= n;
432 return *this;
433 }
434
435public: // conversion to basic types
436 constexpr void round_up() {
437 data_ += (data_ & fractional_mask) >> 1;
438 }
439
440 constexpr int to_int() {
441 round_up();
442 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
443 }
444
445 constexpr unsigned int to_uint() const {
446 round_up();
447 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
448 }
449
450 constexpr int64_t to_long() {
451 round_up();
452 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
453 }
454
455 constexpr int to_int_floor() const {
456 return static_cast<int>((data_ & integer_mask) >> fractional_bits);
457 }
458
459 constexpr int64_t to_long_floor() {
460 return static_cast<int64_t>((data_ & integer_mask) >> fractional_bits);
461 }
462
463 constexpr unsigned int to_uint_floor() const {
464 return static_cast<unsigned int>((data_ & integer_mask) >> fractional_bits);
465 }
466
467 constexpr float to_float() const {
468 return static_cast<float>(data_) / FixedPoint::one;
469 }
470
471 constexpr double to_double() const {
472 return static_cast<double>(data_) / FixedPoint::one;
473 }
474
475 constexpr base_type to_raw() const {
476 return data_;
477 }
478
479 constexpr void clear_int() {
480 data_ &= fractional_mask;
481 }
482
483 constexpr base_type get_frac() const {
484 return data_ & fractional_mask;
485 }
486
487public:
488 CONSTEXPR14 void swap(FixedPoint& rhs) {
489 using std::swap;
490 swap(data_, rhs.data_);
491 }
492
493public:
494 base_type data_;
495};
496
497// if we have the same fractional portion, but differing integer portions, we trivially upgrade the
498// smaller type
499template <size_t I1, size_t I2, size_t F>
500CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
501operator+(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
502
503 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
504
505 const T l = T::from_base(lhs.to_raw());
506 const T r = T::from_base(rhs.to_raw());
507 return l + r;
508}
509
510template <size_t I1, size_t I2, size_t F>
511CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
512operator-(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
513
514 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
515
516 const T l = T::from_base(lhs.to_raw());
517 const T r = T::from_base(rhs.to_raw());
518 return l - r;
519}
520
521template <size_t I1, size_t I2, size_t F>
522CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
523operator*(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
524
525 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
526
527 const T l = T::from_base(lhs.to_raw());
528 const T r = T::from_base(rhs.to_raw());
529 return l * r;
530}
531
532template <size_t I1, size_t I2, size_t F>
533CONSTEXPR14 typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type
534operator/(FixedPoint<I1, F> lhs, FixedPoint<I2, F> rhs) {
535
536 using T = typename std::conditional<I1 >= I2, FixedPoint<I1, F>, FixedPoint<I2, F>>::type;
537
538 const T l = T::from_base(lhs.to_raw());
539 const T r = T::from_base(rhs.to_raw());
540 return l / r;
541}
542
543template <size_t I, size_t F>
544std::ostream& operator<<(std::ostream& os, FixedPoint<I, F> f) {
545 os << f.to_double();
546 return os;
547}
548
549// basic math operators
550template <size_t I, size_t F>
551CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
552 lhs += rhs;
553 return lhs;
554}
555template <size_t I, size_t F>
556CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
557 lhs -= rhs;
558 return lhs;
559}
560template <size_t I, size_t F>
561CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
562 lhs *= rhs;
563 return lhs;
564}
565template <size_t I, size_t F>
566CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, FixedPoint<I, F> rhs) {
567 lhs /= rhs;
568 return lhs;
569}
570
571template <size_t I, size_t F, class Number,
572 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
573CONSTEXPR14 FixedPoint<I, F> operator+(FixedPoint<I, F> lhs, Number rhs) {
574 lhs += FixedPoint<I, F>(rhs);
575 return lhs;
576}
577template <size_t I, size_t F, class Number,
578 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
579CONSTEXPR14 FixedPoint<I, F> operator-(FixedPoint<I, F> lhs, Number rhs) {
580 lhs -= FixedPoint<I, F>(rhs);
581 return lhs;
582}
583template <size_t I, size_t F, class Number,
584 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
585CONSTEXPR14 FixedPoint<I, F> operator*(FixedPoint<I, F> lhs, Number rhs) {
586 lhs *= FixedPoint<I, F>(rhs);
587 return lhs;
588}
589template <size_t I, size_t F, class Number,
590 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
591CONSTEXPR14 FixedPoint<I, F> operator/(FixedPoint<I, F> lhs, Number rhs) {
592 lhs /= FixedPoint<I, F>(rhs);
593 return lhs;
594}
595
596template <size_t I, size_t F, class Number,
597 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
598CONSTEXPR14 FixedPoint<I, F> operator+(Number lhs, FixedPoint<I, F> rhs) {
599 FixedPoint<I, F> tmp(lhs);
600 tmp += rhs;
601 return tmp;
602}
603template <size_t I, size_t F, class Number,
604 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
605CONSTEXPR14 FixedPoint<I, F> operator-(Number lhs, FixedPoint<I, F> rhs) {
606 FixedPoint<I, F> tmp(lhs);
607 tmp -= rhs;
608 return tmp;
609}
610template <size_t I, size_t F, class Number,
611 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
612CONSTEXPR14 FixedPoint<I, F> operator*(Number lhs, FixedPoint<I, F> rhs) {
613 FixedPoint<I, F> tmp(lhs);
614 tmp *= rhs;
615 return tmp;
616}
617template <size_t I, size_t F, class Number,
618 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
619CONSTEXPR14 FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
620 FixedPoint<I, F> tmp(lhs);
621 tmp /= rhs;
622 return tmp;
623}
624
625// shift operators
626template <size_t I, size_t F, class Integer,
627 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
628CONSTEXPR14 FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
629 lhs <<= rhs;
630 return lhs;
631}
632template <size_t I, size_t F, class Integer,
633 class = typename std::enable_if<std::is_integral<Integer>::value>::type>
634CONSTEXPR14 FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
635 lhs >>= rhs;
636 return lhs;
637}
638
639// comparison operators
640template <size_t I, size_t F, class Number,
641 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
642constexpr bool operator>(FixedPoint<I, F> lhs, Number rhs) {
643 return lhs > FixedPoint<I, F>(rhs);
644}
645template <size_t I, size_t F, class Number,
646 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
647constexpr bool operator<(FixedPoint<I, F> lhs, Number rhs) {
648 return lhs < FixedPoint<I, F>(rhs);
649}
650template <size_t I, size_t F, class Number,
651 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
652constexpr bool operator>=(FixedPoint<I, F> lhs, Number rhs) {
653 return lhs >= FixedPoint<I, F>(rhs);
654}
655template <size_t I, size_t F, class Number,
656 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
657constexpr bool operator<=(FixedPoint<I, F> lhs, Number rhs) {
658 return lhs <= FixedPoint<I, F>(rhs);
659}
660template <size_t I, size_t F, class Number,
661 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
662constexpr bool operator==(FixedPoint<I, F> lhs, Number rhs) {
663 return lhs == FixedPoint<I, F>(rhs);
664}
665template <size_t I, size_t F, class Number,
666 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
667constexpr bool operator!=(FixedPoint<I, F> lhs, Number rhs) {
668 return lhs != FixedPoint<I, F>(rhs);
669}
670
671template <size_t I, size_t F, class Number,
672 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
673constexpr bool operator>(Number lhs, FixedPoint<I, F> rhs) {
674 return FixedPoint<I, F>(lhs) > rhs;
675}
676template <size_t I, size_t F, class Number,
677 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
678constexpr bool operator<(Number lhs, FixedPoint<I, F> rhs) {
679 return FixedPoint<I, F>(lhs) < rhs;
680}
681template <size_t I, size_t F, class Number,
682 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
683constexpr bool operator>=(Number lhs, FixedPoint<I, F> rhs) {
684 return FixedPoint<I, F>(lhs) >= rhs;
685}
686template <size_t I, size_t F, class Number,
687 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
688constexpr bool operator<=(Number lhs, FixedPoint<I, F> rhs) {
689 return FixedPoint<I, F>(lhs) <= rhs;
690}
691template <size_t I, size_t F, class Number,
692 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
693constexpr bool operator==(Number lhs, FixedPoint<I, F> rhs) {
694 return FixedPoint<I, F>(lhs) == rhs;
695}
696template <size_t I, size_t F, class Number,
697 class = typename std::enable_if<std::is_arithmetic<Number>::value>::type>
698constexpr bool operator!=(Number lhs, FixedPoint<I, F> rhs) {
699 return FixedPoint<I, F>(lhs) != rhs;
700}
701
702} // namespace Common
703
704#undef CONSTEXPR14
705
706#endif