BEEP-8 Helper Lib 1.0.0
Loading...
Searching...
No Matches
fixed.h
1/*
2MIT License
3
4Copyright (c) 2019 Mike Lankamp
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24#pragma once
25#include <cmath>
26#include <cassert>
27#include <cmath>
28#include <cstdint>
29#include <functional>
30#include <limits>
31#include <type_traits>
32#include <trace.h>
33namespace fpm
34{
35
40template <typename BaseType, typename IntermediateType, unsigned int FractionBits>
41class fixed
42{
43 static_assert(std::is_integral<BaseType>::value, "BaseType must be an integral type");
44 static_assert(FractionBits > 0, "FractionBits must be greater than zero");
45 static_assert(FractionBits <= sizeof(BaseType) * 8, "BaseType must at least be able to contain entire fraction");
46 static_assert(FractionBits <= 62, "Fraction may be no more than 62 bits");
47 static_assert(sizeof(IntermediateType) > sizeof(BaseType), "IntermediateType must be larger than BaseType");
48 static_assert(std::is_signed<IntermediateType>::value == std::is_signed<BaseType>::value, "IntermediateType must have same signedness as BaseType");
49
50 static constexpr BaseType FRACTION_MULT = BaseType(1) << FractionBits;
51
52 struct raw_construct_tag {};
53 constexpr inline fixed(BaseType val, raw_construct_tag) noexcept : m_value(val) {}
54
55public:
56 inline fixed() noexcept {}
57
58 constexpr inline operator int() const noexcept {
59 return static_cast<int>(m_value >> FractionBits);
60 }
61
62 inline fixed& neg() {
63 m_value = -m_value;
64 return *this;
65 }
66
67 // Arithmetic shift right by n bits.
68 inline fixed& asr( int bits_ ) {
69 m_value >>= bits_;
70 return *this;
71 }
72
73 // Logical shift left by n bits.
74 inline fixed& lsl( int bits_ ) {
75 m_value <<= bits_;
76 return *this;
77 }
78
79 // Converts an integral number to the fixed-point type.
80 // Like static_cast, this truncates bits that don't fit.
81 template <typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
82 constexpr inline fixed(T val) noexcept
83 : m_value(static_cast<BaseType>(val * FRACTION_MULT))
84 {}
85
86 // Converts an floating-point number to the fixed-point type.
87 // Like static_cast, this truncates bits that don't fit.
88 template <typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
89 constexpr inline explicit fixed(T val) noexcept
90 : m_value(static_cast<BaseType>(std::round(val * FRACTION_MULT)))
91 {}
92
93 // Constructs from another fixed-point type with possibly different underlying representation.
94 // Like static_cast, this truncates bits that don't fit.
95 template <typename B, typename I, unsigned int F>
96 constexpr inline explicit fixed(fixed<B,I,F> val) noexcept
97 : m_value(from_fixed_point<F>(val.raw_value()).raw_value())
98 {}
99
100 constexpr inline explicit fixed( int numerator_ , int denominator_ ) noexcept
101 {
102 m_value = static_cast<BaseType>( (numerator_ << FractionBits) / denominator_ );
103 }
104
105 // Explicit conversion to a floating-point type
106 template <typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
107 constexpr inline explicit operator T() const noexcept
108 {
109 return static_cast<T>(m_value) / FRACTION_MULT;
110 }
111
112 // Explicit conversion to an integral type
113 template <typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
114 constexpr inline explicit operator T() const noexcept
115 {
116 return static_cast<T>(m_value >> FractionBits );
117 }
118
119 // Returns the raw underlying value of this type.
120 // Do not use this unless you know what you're doing.
121 constexpr inline BaseType raw_value() const noexcept
122 {
123 return m_value;
124 }
125 constexpr inline BaseType set_raw_value( BaseType x_ ) noexcept
126 {
127 m_value = x_;
128 return m_value;
129 }
130
131
135 template <unsigned int NumFractionBits, typename T, typename std::enable_if<(NumFractionBits > FractionBits)>::type* = nullptr>
136 static constexpr inline fixed from_fixed_point(T value) noexcept
137 {
138 // To correctly round the last bit in the result, we need one more bit of information.
139 // We do this by multiplying by two before dividing and adding the LSB to the real result.
140 return fixed(static_cast<BaseType>(
141 value / (T(1) << (NumFractionBits - FractionBits)) +
142 (value / (T(1) << (NumFractionBits - FractionBits - 1)) % 2)),
143 raw_construct_tag{});
144 }
145
146 template <unsigned int NumFractionBits, typename T, typename std::enable_if<(NumFractionBits <= FractionBits)>::type* = nullptr>
147 static constexpr inline fixed from_fixed_point(T value) noexcept
148 {
149 return fixed(static_cast<BaseType>(
150 value * (T(1) << (FractionBits - NumFractionBits))),
151 raw_construct_tag{});
152 }
153
154 // Constructs a fixed-point number from its raw underlying value.
155 // Do not use this unless you know what you're doing.
156 static constexpr inline fixed from_raw_value(BaseType value) noexcept
157 {
158 return fixed(value, raw_construct_tag{});
159 }
160
161 //
162 // Constants
163 //
164 static constexpr fixed e() { return from_fixed_point<61>(6267931151224907085ll); }
165 static constexpr fixed pi() { return from_fixed_point<61>(7244019458077122842ll); }
166 static constexpr fixed half_pi() { return from_fixed_point<62>(7244019458077122842ll); }
167 static constexpr fixed two_pi() { return from_fixed_point<60>(7244019458077122842ll); }
168
169 //
170 // Arithmetic member operators
171 //
172
173 constexpr inline fixed operator-() const noexcept
174 {
175 return fixed::from_raw_value(-m_value);
176 }
177
178 constexpr inline fixed operator+() const noexcept
179 {
180 return fixed::from_raw_value(+m_value);
181 }
182
183 inline fixed& operator+=(const fixed& y) noexcept
184 {
185 m_value += y.m_value;
186 return *this;
187 }
188
189 template <typename I, typename std::enable_if<std::is_integral<I>::value>::type* = nullptr>
190 inline fixed& operator+=(I y) noexcept
191 {
192 m_value += y * FRACTION_MULT;
193 return *this;
194 }
195
196 inline fixed& operator-=(const fixed& y) noexcept
197 {
198 m_value -= y.m_value;
199 return *this;
200 }
201
202 template <typename I, typename std::enable_if<std::is_integral<I>::value>::type* = nullptr>
203 inline fixed& operator-=(I y) noexcept
204 {
205 m_value -= y * FRACTION_MULT;
206 return *this;
207 }
208
209 inline fixed& operator*=(const fixed& y) noexcept
210 {
211 // Normal fixed-point multiplication is: x * y / 2**FractionBits.
212 // To correctly round the last bit in the result, we need one more bit of information.
213 // We do this by multiplying by two before dividing and adding the LSB to the real result.
214 auto value = (static_cast<IntermediateType>(m_value) * y.m_value) / (FRACTION_MULT / 2);
215 m_value = static_cast<BaseType>((value / 2) + (value % 2));
216 return *this;
217 }
218
219 template <typename I, typename std::enable_if<std::is_integral<I>::value>::type* = nullptr>
220 inline fixed& operator*=(I y) noexcept
221 {
222 m_value *= y;
223 return *this;
224 }
225
226 inline fixed& operator/=(const fixed& y) noexcept
227 {
228 assert(y.m_value != 0);
229 // Normal fixed-point division is: x * 2**FractionBits / y.
230 // To correctly round the last bit in the result, we need one more bit of information.
231 // We do this by multiplying by two before dividing and adding the LSB to the real result.
232 auto value = (static_cast<IntermediateType>(m_value) * FRACTION_MULT * 2) / y.m_value;
233 m_value = static_cast<BaseType>((value / 2) + (value % 2));
234 return *this;
235 }
236
237 template <typename I, typename std::enable_if<std::is_integral<I>::value>::type* = nullptr>
238 inline fixed& operator/=(I y) noexcept
239 {
240 m_value /= y;
241 return *this;
242 }
243
244private:
245 BaseType m_value;
246};
247
248//
249// Addition
250//
251
252template <typename B, typename I, unsigned int F>
253constexpr inline fixed<B, I, F> operator+(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
254{
255 return fixed<B, I, F>(x) += y;
256}
257
258template <typename B, typename I, unsigned int F, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
259constexpr inline fixed<B, I, F> operator+(const fixed<B, I, F>& x, T y) noexcept
260{
261 return fixed<B, I, F>(x) += y;
262}
263
264template <typename B, typename I, unsigned int F, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
265constexpr inline fixed<B, I, F> operator+(T x, const fixed<B, I, F>& y) noexcept
266{
267 return fixed<B, I, F>(y) += x;
268}
269
270//
271// Subtraction
272//
273
274template <typename B, typename I, unsigned int F>
275constexpr inline fixed<B, I, F> operator-(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
276{
277 return fixed<B, I, F>(x) -= y;
278}
279
280template <typename B, typename I, unsigned int F, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
281constexpr inline fixed<B, I, F> operator-(const fixed<B, I, F>& x, T y) noexcept
282{
283 return fixed<B, I, F>(x) -= y;
284}
285
286template <typename B, typename I, unsigned int F, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
287constexpr inline fixed<B, I, F> operator-(T x, const fixed<B, I, F>& y) noexcept
288{
289 return fixed<B, I, F>(x) -= y;
290}
291
292//
293// Multiplication
294//
295
296template <typename B, typename I, unsigned int F>
297constexpr inline fixed<B, I, F> operator*(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
298{
299 return fixed<B, I, F>(x) *= y;
300}
301
302template <typename B, typename I, unsigned int F, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
303constexpr inline fixed<B, I, F> operator*(const fixed<B, I, F>& x, T y) noexcept
304{
305 return fixed<B, I, F>(x) *= y;
306}
307
308template <typename B, typename I, unsigned int F, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
309constexpr inline fixed<B, I, F> operator*(T x, const fixed<B, I, F>& y) noexcept
310{
311 return fixed<B, I, F>(y) *= x;
312}
313
314//
315// Division
316//
317
318template <typename B, typename I, unsigned int F>
319constexpr inline fixed<B, I, F> operator/(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
320{
321 return fixed<B, I, F>(x) /= y;
322}
323
324template <typename B, typename I, unsigned int F, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
325constexpr inline fixed<B, I, F> operator/(const fixed<B, I, F>& x, T y) noexcept
326{
327 return fixed<B, I, F>(x) /= y;
328}
329
330template <typename B, typename I, unsigned int F, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
331constexpr inline fixed<B, I, F> operator/(T x, const fixed<B, I, F>& y) noexcept
332{
333 return fixed<B, I, F>(x) /= y;
334}
335
336//
337// Comparison operators
338//
339
340template <typename B, typename I, unsigned int F>
341constexpr inline bool operator==(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
342{
343 return x.raw_value() == y.raw_value();
344}
345
346template <typename B, typename I, unsigned int F>
347constexpr inline bool operator!=(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
348{
349 return x.raw_value() != y.raw_value();
350}
351
352template <typename B, typename I, unsigned int F>
353constexpr inline bool operator<(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
354{
355 return x.raw_value() < y.raw_value();
356}
357
358template <typename B, typename I, unsigned int F>
359constexpr inline bool operator>(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
360{
361 return x.raw_value() > y.raw_value();
362}
363
364template <typename B, typename I, unsigned int F>
365constexpr inline bool operator<=(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
366{
367 return x.raw_value() <= y.raw_value();
368}
369
370template <typename B, typename I, unsigned int F>
371constexpr inline bool operator>=(const fixed<B, I, F>& x, const fixed<B, I, F>& y) noexcept
372{
373 return x.raw_value() >= y.raw_value();
374}
375
376template <typename B, typename I, unsigned int F>
377constexpr inline bool operator>(const fixed<B, I, F>& lhs, int rhs) noexcept
378{
379 return lhs > fixed<B, I, F>(rhs);
380}
381
382template <typename B, typename I, unsigned int F>
383constexpr inline bool operator>(int lhs, const fixed<B, I, F>& rhs) noexcept
384{
385 return fixed<B, I, F>(lhs) > rhs;
386}
387
388// Equality
389template <typename B, typename I, unsigned int F>
390constexpr inline bool operator==(const fixed<B, I, F>& lhs, int rhs) noexcept
391{
392 return lhs == fixed<B, I, F>(rhs);
393}
394
395template <typename B, typename I, unsigned int F>
396constexpr inline bool operator==(int lhs, const fixed<B, I, F>& rhs) noexcept
397{
398 return fixed<B, I, F>(lhs) == rhs;
399}
400
401// Inequality
402template <typename B, typename I, unsigned int F>
403constexpr inline bool operator!=(const fixed<B, I, F>& lhs, int rhs) noexcept
404{
405 return lhs != fixed<B, I, F>(rhs);
406}
407
408template <typename B, typename I, unsigned int F>
409constexpr inline bool operator!=(int lhs, const fixed<B, I, F>& rhs) noexcept
410{
411 return fixed<B, I, F>(lhs) != rhs;
412}
413
414// Less than
415template <typename B, typename I, unsigned int F>
416constexpr inline bool operator<(const fixed<B, I, F>& lhs, int rhs) noexcept
417{
418 return lhs < fixed<B, I, F>(rhs);
419}
420
421template <typename B, typename I, unsigned int F>
422constexpr inline bool operator<(int lhs, const fixed<B, I, F>& rhs) noexcept
423{
424 return fixed<B, I, F>(lhs) < rhs;
425}
426
427// Less than or equal
428template <typename B, typename I, unsigned int F>
429constexpr inline bool operator<=(const fixed<B, I, F>& lhs, int rhs) noexcept
430{
431 return lhs <= fixed<B, I, F>(rhs);
432}
433
434template <typename B, typename I, unsigned int F>
435constexpr inline bool operator<=(int lhs, const fixed<B, I, F>& rhs) noexcept
436{
437 return fixed<B, I, F>(lhs) <= rhs;
438}
439
440// Greater than or equal
441template <typename B, typename I, unsigned int F>
442constexpr inline bool operator>=(const fixed<B, I, F>& lhs, int rhs) noexcept
443{
444 return lhs >= fixed<B, I, F>(rhs);
445}
446
447template <typename B, typename I, unsigned int F>
448constexpr inline bool operator>=(int lhs, const fixed<B, I, F>& rhs) noexcept
449{
450 return fixed<B, I, F>(lhs) >= rhs;
451}
452
453namespace detail
454{
455// Number of base-10 digits required to fully represent a number of bits
456static constexpr int max_digits10(int bits)
457{
458 // 8.24 fixed-point equivalent of (int)ceil(bits * std::log10(2));
459 using T = long long;
460 return static_cast<int>((T{bits} * 5050445 + (T{1} << 24) - 1) >> 24);
461}
462
463// Number of base-10 digits that can be fully represented by a number of bits
464static constexpr int digits10(int bits)
465{
466 // 8.24 fixed-point equivalent of (int)(bits * std::log10(2));
467 using T = long long;
468 return static_cast<int>((T{bits} * 5050445) >> 24);
469}
470
471} // namespace detail
472} // namespace fpm
473
474// Specializations for customization points
475namespace std
476{
477
478template <typename B, typename I, unsigned int F>
479struct hash<fpm::fixed<B,I,F>>
480{
482 using result_type = std::size_t;
483
484 result_type operator()(argument_type arg) const noexcept(noexcept(std::declval<std::hash<B>>()(arg.raw_value()))) {
485 return m_hash(arg.raw_value());
486 }
487
488private:
489 std::hash<B> m_hash;
490};
491
492template <typename B, typename I, unsigned int F>
493struct numeric_limits<fpm::fixed<B,I,F>>
494{
495 static constexpr bool is_specialized = true;
496 static constexpr bool is_signed = std::numeric_limits<B>::is_signed;
497 static constexpr bool is_integer = false;
498 static constexpr bool is_exact = true;
499 static constexpr bool has_infinity = false;
500 static constexpr bool has_quiet_NaN = false;
501 static constexpr bool has_signaling_NaN = false;
502 static constexpr bool has_denorm = std::denorm_absent;
503 static constexpr bool has_denorm_loss = false;
504 static constexpr std::float_round_style round_style = std::round_to_nearest;
505 static constexpr bool is_iec_559 = false;
506 static constexpr bool is_bounded = true;
507 static constexpr bool is_modulo = std::numeric_limits<B>::is_modulo;
508 static constexpr int digits = std::numeric_limits<B>::digits;
509
510 // Any number with `digits10` significant base-10 digits (that fits in
511 // the range of the type) is guaranteed to be convertible from text and
512 // back without change. Worst case, this is 0.000...001, so we can only
513 // guarantee this case. Nothing more.
514 static constexpr int digits10 = 1;
515
516 // This is equal to max_digits10 for the integer and fractional part together.
517 static constexpr int max_digits10 =
518 fpm::detail::max_digits10(std::numeric_limits<B>::digits - F) + fpm::detail::max_digits10(F);
519
520 static constexpr int radix = 2;
521 static constexpr int min_exponent = 1 - F;
522 static constexpr int min_exponent10 = -fpm::detail::digits10(F);
523 static constexpr int max_exponent = std::numeric_limits<B>::digits - F;
524 static constexpr int max_exponent10 = fpm::detail::digits10(std::numeric_limits<B>::digits - F);
525 static constexpr bool traps = true;
526 static constexpr bool tinyness_before = false;
527
528 static constexpr fpm::fixed<B,I,F> lowest() noexcept {
529 return fpm::fixed<B,I,F>::from_raw_value(std::numeric_limits<B>::lowest());
530 };
531
532 static constexpr fpm::fixed<B,I,F> min() noexcept {
533 return lowest();
534 }
535
536 static constexpr fpm::fixed<B,I,F> max() noexcept {
537 return fpm::fixed<B,I,F>::from_raw_value(std::numeric_limits<B>::max());
538 };
539
540 static constexpr fpm::fixed<B,I,F> epsilon() noexcept {
542 };
543
544 static constexpr fpm::fixed<B,I,F> round_error() noexcept {
545 return fpm::fixed<B,I,F>(1) / 2;
546 };
547
548 static constexpr fpm::fixed<B,I,F> denorm_min() noexcept {
549 return min();
550 }
551};
552
553template <typename B, typename I, unsigned int F>
554constexpr bool numeric_limits<fpm::fixed<B,I,F>>::is_specialized;
555template <typename B, typename I, unsigned int F>
556constexpr bool numeric_limits<fpm::fixed<B,I,F>>::is_signed;
557template <typename B, typename I, unsigned int F>
558constexpr bool numeric_limits<fpm::fixed<B,I,F>>::is_integer;
559template <typename B, typename I, unsigned int F>
560constexpr bool numeric_limits<fpm::fixed<B,I,F>>::is_exact;
561template <typename B, typename I, unsigned int F>
562constexpr bool numeric_limits<fpm::fixed<B,I,F>>::has_infinity;
563template <typename B, typename I, unsigned int F>
564constexpr bool numeric_limits<fpm::fixed<B,I,F>>::has_quiet_NaN;
565template <typename B, typename I, unsigned int F>
566constexpr bool numeric_limits<fpm::fixed<B,I,F>>::has_signaling_NaN;
567template <typename B, typename I, unsigned int F>
568constexpr bool numeric_limits<fpm::fixed<B,I,F>>::has_denorm;
569template <typename B, typename I, unsigned int F>
570constexpr bool numeric_limits<fpm::fixed<B,I,F>>::has_denorm_loss;
571template <typename B, typename I, unsigned int F>
572constexpr std::float_round_style numeric_limits<fpm::fixed<B,I,F>>::round_style;
573template <typename B, typename I, unsigned int F>
574constexpr bool numeric_limits<fpm::fixed<B,I,F>>::is_iec_559;
575template <typename B, typename I, unsigned int F>
576constexpr bool numeric_limits<fpm::fixed<B,I,F>>::is_bounded;
577template <typename B, typename I, unsigned int F>
578constexpr bool numeric_limits<fpm::fixed<B,I,F>>::is_modulo;
579template <typename B, typename I, unsigned int F>
580constexpr int numeric_limits<fpm::fixed<B,I,F>>::digits;
581template <typename B, typename I, unsigned int F>
582constexpr int numeric_limits<fpm::fixed<B,I,F>>::digits10;
583template <typename B, typename I, unsigned int F>
584constexpr int numeric_limits<fpm::fixed<B,I,F>>::max_digits10;
585template <typename B, typename I, unsigned int F>
586constexpr int numeric_limits<fpm::fixed<B,I,F>>::radix;
587template <typename B, typename I, unsigned int F>
588constexpr int numeric_limits<fpm::fixed<B,I,F>>::min_exponent;
589template <typename B, typename I, unsigned int F>
590constexpr int numeric_limits<fpm::fixed<B,I,F>>::min_exponent10;
591template <typename B, typename I, unsigned int F>
592constexpr int numeric_limits<fpm::fixed<B,I,F>>::max_exponent;
593template <typename B, typename I, unsigned int F>
594constexpr int numeric_limits<fpm::fixed<B,I,F>>::max_exponent10;
595template <typename B, typename I, unsigned int F>
596constexpr bool numeric_limits<fpm::fixed<B,I,F>>::traps;
597template <typename B, typename I, unsigned int F>
598constexpr bool numeric_limits<fpm::fixed<B,I,F>>::tinyness_before;
599
600}
601
Definition fixed.h:42
static constexpr fixed from_fixed_point(T value) noexcept
Definition fixed.h:136
Debug tracing macros for logging and monitoring program execution.