Deluge Firmware 1.3.0
Build date: 2026.03.02
Loading...
Searching...
No Matches
fixedpoint.h
1/*
2 * Copyright © 2014-2023 Synthstrom Audible Limited
3 *
4 * This file is part of The Synthstrom Audible Deluge Firmware.
5 *
6 * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software Foundation,
8 * either version 3 of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along with this program.
15 * If not, see <https://www.gnu.org/licenses/>.
16 */
17#pragma once
18
19#include <bit>
20#include <cmath>
21#include <compare>
22#include <cstdint>
23#include <limits>
24#include <type_traits>
25
26// signed 31 fractional bits (e.g. one would be 1<<31 but can't be represented)
27using q31_t = int32_t;
28
29constexpr q31_t ONE_Q31{2147483647};
30constexpr float ONE_Q31f{2147483647.0f};
31constexpr q31_t ONE_Q15{65536};
32constexpr q31_t NEGATIVE_ONE_Q31{-2147483648};
33constexpr q31_t ONE_OVER_SQRT2_Q31{1518500250};
34
35// This converts the range -2^31 to 2^31 to the range 0-2^31
36static inline q31_t toPositive(q31_t a) __attribute__((always_inline, unused));
37static inline q31_t toPositive(q31_t a) {
38 return ((a / 2) + (1073741824));
39}
40
41// this is only defined for 32 bit arm
42#if defined(__arm__)
43// This multiplies two numbers in signed Q31 fixed point as if they were q32, so the return value is half what it should
44// be. Use this when several corrective shifts can be accumulated and then combined
45static inline q31_t multiply_32x32_rshift32(q31_t a, q31_t b) __attribute__((always_inline, unused));
46static inline q31_t multiply_32x32_rshift32(q31_t a, q31_t b) {
47 q31_t out;
48 asm("smmul %0, %1, %2" : "=r"(out) : "r"(a), "r"(b));
49 return out;
50}
51
52// This multiplies two numbers in signed Q31 fixed point and rounds the result
53static inline q31_t multiply_32x32_rshift32_rounded(q31_t a, q31_t b) __attribute__((always_inline, unused));
54static inline q31_t multiply_32x32_rshift32_rounded(q31_t a, q31_t b) {
55 q31_t out;
56 asm("smmulr %0, %1, %2" : "=r"(out) : "r"(a), "r"(b));
57 return out;
58}
59
60// This multiplies two numbers in signed Q31 fixed point, returning the result in q31. This is more useful for readable
61// multiplies
62static inline q31_t q31_mult(q31_t a, q31_t b) __attribute__((always_inline, unused));
63static inline q31_t q31_mult(q31_t a, q31_t b) {
64 q31_t out;
65 asm("smmul %0, %1, %2" : "=r"(out) : "r"(a), "r"(b));
66 return out * 2;
67}
68
69// This multiplies a number in q31 by a number in q32 (e.g. unsigned, 2^32 representing one), returning a scaled value a
70static inline q31_t q31tRescale(q31_t a, uint32_t proportion) __attribute__((always_inline, unused));
71static inline q31_t q31tRescale(q31_t a, uint32_t proportion) {
72 q31_t out;
73 asm("smmul %0, %1, %2" : "=r"(out) : "r"(a), "r"(proportion));
74 return out;
75}
76
77// Multiplies A and B, adds to sum, and returns output
78static inline q31_t multiply_accumulate_32x32_rshift32_rounded(q31_t sum, q31_t a, q31_t b)
79 __attribute__((always_inline, unused));
80static inline q31_t multiply_accumulate_32x32_rshift32_rounded(q31_t sum, q31_t a, q31_t b) {
81 q31_t out;
82 asm("smmlar %0, %1, %2, %3" : "=r"(out) : "r"(a), "r"(b), "r"(sum));
83 return out;
84}
85
86// Multiplies A and B, adds to sum, and returns output
87static inline q31_t multiply_accumulate_32x32_rshift32(q31_t sum, q31_t a, q31_t b)
88 __attribute__((always_inline, unused));
89static inline q31_t multiply_accumulate_32x32_rshift32(q31_t sum, q31_t a, q31_t b) {
90 q31_t out;
91 asm("smmla %0, %1, %2, %3" : "=r"(out) : "r"(a), "r"(b), "r"(sum));
92 return out;
93}
94
95// Multiplies A and B, subtracts from sum, and returns output
96static inline q31_t multiply_subtract_32x32_rshift32_rounded(q31_t sum, q31_t a, q31_t b)
97 __attribute__((always_inline, unused));
98static inline q31_t multiply_subtract_32x32_rshift32_rounded(q31_t sum, q31_t a, q31_t b) {
99 q31_t out;
100 asm("smmlsr %0, %1, %2, %3" : "=r"(out) : "r"(a), "r"(b), "r"(sum));
101 return out;
102}
103
104// computes limit((val >> rshift), 2**bits)
105template <uint8_t bits>
106static inline int32_t signed_saturate(int32_t val) __attribute__((always_inline, unused));
107template <uint8_t bits>
108static inline int32_t signed_saturate(int32_t val) {
109 int32_t out;
110 asm("ssat %0, %1, %2" : "=r"(out) : "I"(bits), "r"(val));
111 return out;
112}
113
114static inline int32_t add_saturate(int32_t a, int32_t b) __attribute__((always_inline, unused));
115static inline int32_t add_saturate(int32_t a, int32_t b) {
116 int32_t out;
117 asm("qadd %0, %1, %2" : "=r"(out) : "r"(a), "r"(b));
118 return out;
119}
120
121static inline int32_t subtract_saturate(int32_t a, int32_t b) __attribute__((always_inline, unused));
122static inline int32_t subtract_saturate(int32_t a, int32_t b) {
123 int32_t out;
124 asm("qsub %0, %1, %2" : "=r"(out) : "r"(a), "r"(b));
125 return out;
126}
127
128inline int32_t clz(uint32_t input) {
129 int32_t out;
130 asm("clz %0, %1" : "=r"(out) : "r"(input));
131 return out;
132}
133
136static inline q31_t q31_from_float(float value) {
137 asm("vcvt.s32.f32 %0, %0, #31" : "=t"(value) : "t"(value));
138 return std::bit_cast<q31_t>(value);
139}
140
143static inline float q31_to_float(q31_t value) {
144 asm("vcvt.f32.s32 %0, %0, #31" : "=t"(value) : "t"(value));
145 return std::bit_cast<float>(value);
146}
147#else
148
149static inline q31_t multiply_32x32_rshift32(q31_t a, q31_t b) {
150 return (int32_t)(((int64_t)a * b) >> 32);
151}
152
153// This multiplies two numbers in signed Q31 fixed point and rounds the result
154static inline q31_t multiply_32x32_rshift32_rounded(q31_t a, q31_t b) {
155 return (int32_t)(((int64_t)a * b + 0x80000000) >> 32);
156}
157
158// Multiplies A and B, adds to sum, and returns output
159static inline q31_t multiply_accumulate_32x32_rshift32(q31_t sum, q31_t a, q31_t b) {
160 return (int32_t)(((((int64_t)sum) << 32) + ((int64_t)a * b)) >> 32);
161}
162
163// Multiplies A and B, adds to sum, and returns output, rounding
164static inline q31_t multiply_accumulate_32x32_rshift32_rounded(q31_t sum, q31_t a, q31_t b) {
165 return (int32_t)(((((int64_t)sum) << 32) + ((int64_t)a * b) + 0x80000000) >> 32);
166}
167
168// Multiplies A and B, subtracts from sum, and returns output
169static inline q31_t multiply_subtract_32x32_rshift32(q31_t sum, q31_t a, q31_t b) {
170 return (int32_t)(((((int64_t)sum) << 32) - ((int64_t)a * b)) >> 32);
171}
172
173// Multiplies A and B, subtracts from sum, and returns output, rounding
174static inline q31_t multiply_subtract_32x32_rshift32_rounded(q31_t sum, q31_t a, q31_t b) {
175 return (int32_t)((((((int64_t)sum) << 32) - ((int64_t)a * b)) + 0x80000000) >> 32);
176}
177
178// computes limit((val >> rshift), 2**bits)
179template <uint8_t bits>
180static inline int32_t signed_saturate(int32_t val) {
181 return std::min(val, 1 << bits);
182}
183
184static inline int32_t add_saturate(int32_t a, int32_t b) __attribute__((always_inline, unused));
185static inline int32_t add_saturate(int32_t a, int32_t b) {
186 return a + b;
187}
188
189static inline int32_t subtract_saturate(int32_t a, int32_t b) __attribute__((always_inline, unused));
190static inline int32_t subtract_saturate(int32_t a, int32_t b) {
191 return a - b;
192}
193
194inline int32_t clz(uint32_t input) {
195 return __builtin_clz(input);
196}
197
198[[gnu::always_inline]] constexpr q31_t q31_from_float(float value) {
199 // A float is represented as 32 bits:
200 // 1-bit sign, 8-bit exponent, 24-bit mantissa
201
202 auto bits = std::bit_cast<uint32_t>(value);
203
204 // Sign bit being 1 indicates negative value
205 bool negative = bits & 0x80000000;
206
207 // Extract exponent and adjust for bias (IEEE 754)
208 int32_t exponent = static_cast<int32_t>((bits >> 23) & 0xFF) - 127;
209
210 q31_t output_value = 0;
211
212 // saturate if above 1.f
213 if (exponent >= 0) {
214 output_value = std::numeric_limits<q31_t>::max();
215 }
216
217 // extract mantissa
218 else {
219 uint32_t mantissa = (bits << 8) | 0x80000000;
220 output_value = static_cast<q31_t>(mantissa >> -exponent);
221 }
222
223 // Sign bit
224 return (negative) ? -output_value : output_value;
225}
226#endif
227
233template <std::size_t FractionalBits, bool Rounded = false, bool FastApproximation = true>
235 static_assert(FractionalBits > 0, "FractionalBits must be greater than 0");
236 static_assert(FractionalBits < 32, "FractionalBits must be less than 32");
237
238 using BaseType = int32_t;
239 using IntermediateType = int64_t;
240
242 [[gnu::always_inline]] static int32_t signed_most_significant_word_multiply_add(int32_t a, int32_t b, int32_t c) {
243 if constexpr (Rounded) {
244 return multiply_accumulate_32x32_rshift32_rounded(a, b, c);
245 }
246 else {
247 return multiply_accumulate_32x32_rshift32(a, b, c);
248 }
249 }
250
251 // a * b
252 [[gnu::always_inline]] static int32_t signed_most_significant_word_multiply(int32_t a, int32_t b) {
253 if constexpr (Rounded) {
254 return multiply_32x32_rshift32_rounded(a, b);
255 }
256 else {
257 return multiply_32x32_rshift32(a, b);
258 }
259 }
260
261 static constexpr BaseType one() noexcept {
262 if constexpr (fractional_bits == 31) {
263 return std::numeric_limits<BaseType>::max();
264 }
265 else {
266 return 1 << fractional_bits;
267 }
268 }
269
270public:
271 constexpr static std::size_t fractional_bits = FractionalBits;
272 constexpr static std::size_t integral_bits = 32 - FractionalBits;
273 constexpr static bool rounded = Rounded;
274 constexpr static bool fast_approximation = FastApproximation;
275
277 constexpr FixedPoint() = default;
278
281 template <std::size_t OtherFractionalBits>
282 constexpr explicit FixedPoint(FixedPoint<OtherFractionalBits> other) noexcept : value_(other.raw()) {
283 if constexpr (FractionalBits == OtherFractionalBits) {
284 return;
285 }
286 else if constexpr (FractionalBits > OtherFractionalBits) {
287 // saturate
288 constexpr int32_t shift = FractionalBits - OtherFractionalBits;
289 value_ = signed_saturate<32 - shift>(value_);
290 value_ = (value_ << shift) + (value_ % 2);
291 }
292 else if constexpr (rounded) {
293 // round
294 constexpr int32_t shift = OtherFractionalBits - FractionalBits;
295 value_ >>= shift + ((1 << shift) - 1);
296 }
297 else {
298 // truncate
299 value_ >>= (OtherFractionalBits - FractionalBits);
300 }
301 }
302
305 template <std::integral T>
306 constexpr explicit FixedPoint(T value) noexcept : value_(static_cast<BaseType>(value) << fractional_bits) {}
307
310 constexpr explicit FixedPoint(float value) noexcept {
311 if constexpr (std::is_constant_evaluated()) {
312 value *= FixedPoint::one();
313 // convert from floating-point to fixed point
314 if constexpr (rounded) {
315 value_ = static_cast<BaseType>((value >= 0.0) ? std::ceil(value) : std::floor(value));
316 }
317 else {
318 value_ = static_cast<BaseType>(value);
319 }
320 }
321 else {
322 asm("vcvt.s32.f32 %0, %1, %2" : "=t"(value) : "t"(value), "I"(fractional_bits));
323 value_ = std::bit_cast<int32_t>(value); // NOLINT
324 }
325 }
326
329 constexpr explicit operator float() const noexcept {
330 if constexpr (std::is_constant_evaluated()) {
331 return static_cast<float>(value_) / FixedPoint::one();
332 }
333 int32_t output = value_;
334 asm("vcvt.f32.s32 %0, %1, %2" : "=t"(output) : "t"(output), "I"(fractional_bits));
335 return std::bit_cast<float>(output);
336 }
337
340 constexpr explicit FixedPoint(double value) noexcept {
341 if constexpr (std::is_constant_evaluated()) {
342 value *= FixedPoint::one();
343 // convert from floating-point to fixed point
344 if constexpr (rounded) {
345 value_ = static_cast<BaseType>((value >= 0.0) ? std::ceil(value) : std::floor(value));
346 }
347 else {
348 value_ = static_cast<BaseType>(value);
349 }
350 }
351 else {
352 auto output = std::bit_cast<int64_t>(value);
353 asm("vcvt.s32.f64 %0, %1, %2" : "=w"(output) : "w"(output), "I"(fractional_bits));
354 value_ = static_cast<BaseType>(output);
355 }
356 }
357
360 explicit operator double() const noexcept {
361 if constexpr (std::is_constant_evaluated()) {
362 return static_cast<double>(value_) / FixedPoint::one();
363 }
364 auto output = std::bit_cast<double>((int64_t)value_);
365 asm("vcvt.f64.s32 %0, %1, %2" : "=w"(output) : "w"(output), "I"(fractional_bits));
366 return output;
367 }
368
370 template <std::size_t OutputFractionalBits>
372 return static_cast<FixedPoint<OutputFractionalBits>>(*this);
373 }
374
376 template <std::integral T>
377 explicit operator T() const noexcept {
378 return static_cast<T>(value_ >> fractional_bits);
379 }
380
381 explicit operator bool() const noexcept { return value_ != 0; }
382
384 constexpr FixedPoint operator-() const { return FixedPoint::from_raw(-value_); }
385
387 [[nodiscard]] constexpr BaseType raw() const noexcept { return value_; }
388
390 static constexpr FixedPoint from_raw(BaseType raw) noexcept {
391 FixedPoint result{};
392 result.value_ = raw;
393 return result;
394 }
395
398 constexpr FixedPoint operator+(const FixedPoint& rhs) const { return from_raw(add_saturate(value_, rhs.value_)); }
399
402 constexpr FixedPoint operator+=(const FixedPoint& rhs) {
403 value_ = add_saturate(value_, rhs.value_);
404 return *this;
405 }
406
409 constexpr FixedPoint operator-(const FixedPoint& rhs) const {
410 return from_raw(subtract_saturate(value_, rhs.value_));
411 }
412
415 constexpr FixedPoint operator-=(const FixedPoint& rhs) {
416 value_ = subtract_saturate(value_, rhs.value_);
417 return *this;
418 }
419
422 constexpr FixedPoint operator*(const FixedPoint& rhs) const {
423 if constexpr (fast_approximation && fractional_bits > 16) {
424 // less than 16 would mean no fractional bits remain after right shift by 32
425 constexpr int32_t shift = fractional_bits - ((fractional_bits * 2) - 32);
426 return from_raw(signed_most_significant_word_multiply(value_, rhs.value_) << shift);
427 }
428
429 if constexpr (rounded) {
430 IntermediateType value = (static_cast<IntermediateType>(value_) * rhs.value_) >> (fractional_bits - 1);
431 return from_raw(static_cast<BaseType>((value >> 1) + (value % 2)));
432 }
433
434 IntermediateType value = (static_cast<IntermediateType>(value_) * rhs.value_) >> fractional_bits;
435 return from_raw(static_cast<BaseType>(value));
436 }
437
440 constexpr FixedPoint operator*=(const FixedPoint& rhs) {
441 value_ = this->operator*(rhs).value_;
442 return *this;
443 }
444
447 template <std::size_t OutputFractionalBits = FractionalBits, std::size_t OtherFractionalBits, bool OtherRounded,
448 bool OtherApproximating>
449 requires(OtherRounded == Rounded && OtherApproximating == FastApproximation)
452 if constexpr (fast_approximation) {
453 constexpr int32_t l_shift = OutputFractionalBits - ((FractionalBits + OtherFractionalBits) - 32);
454 static_assert(l_shift < 32 && l_shift > -32);
455 BaseType value = signed_most_significant_word_multiply(value_, rhs.raw());
456 return from_raw(l_shift > 0 ? value << l_shift : value >> -l_shift);
457 }
458
459 constexpr int32_t r_shift = (FractionalBits + OtherFractionalBits) - OutputFractionalBits;
460 if constexpr (rounded) {
461 IntermediateType value = (static_cast<IntermediateType>(value_) * rhs.raw())
462 >> (r_shift - 1); // At this point Q is FractionalBits + OtherFractionalBits
463 return from_raw(static_cast<BaseType>((value / 2) + (value % 2)));
464 }
465
466 IntermediateType value = (static_cast<IntermediateType>(value_) * rhs.raw()) >> r_shift;
467 return from_raw(static_cast<BaseType>(value));
468 }
469
472 template <std::size_t OtherFractionalBits>
474 value_ = this->operator*(rhs).value_;
475 return *this;
476 }
477
480 constexpr FixedPoint operator/(const FixedPoint& rhs) const {
481 if (rhs.value_ == 0) {
482 return from_raw(std::numeric_limits<BaseType>::max());
483 }
484 if constexpr (rounded) {
485 IntermediateType value = (static_cast<IntermediateType>(value_) << (fractional_bits + 1)) / rhs.value_;
486 return from_raw(static_cast<BaseType>((value / 2) + (value % 2)));
487 }
488
489 IntermediateType value = (static_cast<IntermediateType>(value_) << fractional_bits) / rhs.value_;
490 return from_raw(static_cast<BaseType>(value));
491 }
492
495 constexpr FixedPoint operator/=(const FixedPoint& rhs) {
496 value_ = this->operator/(rhs).value_;
497 return *this;
498 }
499
501 template <std::size_t OtherFractionalBitsA, std::size_t OtherFractionalBitsB>
503 const FixedPoint<OtherFractionalBitsB>& b) const {
504 // ensure that the number of fractional bits in the addend is equal to the sum of the number of fractional bits
505 // in multiplicands, minus 32 (due to right shift) before using smmla/smmlar
506 if constexpr (fast_approximation && (OtherFractionalBitsA + OtherFractionalBitsB) - 32 == FractionalBits) {
508 }
509 return *this + static_cast<FixedPoint>(a * b);
510 }
511
513 constexpr FixedPoint MultiplyAdd(const FixedPoint& a, const FixedPoint& b) const {
514 if constexpr (fast_approximation && (((FractionalBits * 2) - 32) == (FractionalBits - 1))) {
515 return from_raw(static_cast<FixedPoint<FractionalBits - 1>>(*this).MultiplyAdd(a, b).raw() << 1);
516 }
517 return *this + (a * b);
518 }
519
521 constexpr bool operator==(const FixedPoint& rhs) const noexcept { return value_ == rhs.value_; }
522
524 constexpr std::strong_ordering operator<=>(const FixedPoint& rhs) const noexcept { return value_ <=> rhs.value_; }
525
527 template <std::size_t OtherFractionalBits>
528 constexpr bool operator==(const FixedPoint<OtherFractionalBits>& rhs) const noexcept {
529 int integral_value = value_ >> fractional_bits;
530 int other_integral_value = rhs.raw() >> OtherFractionalBits;
531 int fractional_value = value_ & ((1 << fractional_bits) - 1); // Mask out the integral part
532 int other_fractional_value = rhs.raw() & ((1 << OtherFractionalBits) - 1); // Mask out the integral parts
533
534 // Shift the fractional part if the number of fractional bits is different
535 if (fractional_bits > OtherFractionalBits) {
536 fractional_value >>= fractional_bits - OtherFractionalBits;
537 }
538 else {
539 other_fractional_value >>= OtherFractionalBits - fractional_bits;
540 }
541
542 return integral_value == other_integral_value && fractional_value == other_fractional_value;
543 }
544
546 template <std::size_t OtherFractionalBits>
547 constexpr std::strong_ordering operator<=>(const FixedPoint<OtherFractionalBits>& rhs) const noexcept {
548 // Compare integral and fractional components separately
549 int integral_value = value_ >> fractional_bits;
550 int other_integral_value = rhs.raw() >> OtherFractionalBits;
551 int fractional_value = value_ & ((1 << fractional_bits) - 1); // Mask out the integral part
552 int other_fractional_value = rhs.raw() & ((1 << OtherFractionalBits) - 1); // Mask out the integral part
553
554 // Shift the fractional part if the number of fractional bits is different
555 if (fractional_bits > OtherFractionalBits) {
556 fractional_value >>= fractional_bits - OtherFractionalBits;
557 }
558 else {
559 other_fractional_value >>= OtherFractionalBits - fractional_bits;
560 }
561
562 if (integral_value < other_integral_value) {
563 return std::strong_ordering::less;
564 }
565 if (integral_value > other_integral_value) {
566 return std::strong_ordering::greater;
567 }
568 if (fractional_value < other_fractional_value) {
569 return std::strong_ordering::less;
570 }
571 if (fractional_value > other_fractional_value) {
572 return std::strong_ordering::greater;
573 }
574 return std::strong_ordering::equal;
575 }
576
578 template <typename T>
579 requires std::integral<T> || std::floating_point<T>
580 constexpr bool operator==(const T& rhs) const noexcept {
581 return value_ == FixedPoint(rhs).value_;
582 }
583
584private:
585 BaseType value_{0};
586};
Fixed point number with a configurable number of fractional bits.
Definition fixedpoint.h:234
constexpr bool operator==(const FixedPoint &rhs) const noexcept
Equality operator.
Definition fixedpoint.h:521
constexpr FixedPoint operator*=(const FixedPoint< OtherFractionalBits > &rhs)
Multiplication operator Multiply two fixed point numbers with different number of fractional bits.
Definition fixedpoint.h:473
constexpr FixedPoint(float value) noexcept
Convert from a float to a fixed point number.
Definition fixedpoint.h:310
constexpr std::strong_ordering operator<=>(const FixedPoint &rhs) const noexcept
Three-way comparison operator.
Definition fixedpoint.h:524
constexpr FixedPoint operator/(const FixedPoint &rhs) const
Division operator Divide two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:480
constexpr FixedPoint(T value) noexcept
Convert an integer to a fixed point number.
Definition fixedpoint.h:306
constexpr FixedPoint< OutputFractionalBits > as() const
Convert to a fixed point number with a different number of fractional bits.
Definition fixedpoint.h:371
constexpr FixedPoint MultiplyAdd(const FixedPoint< OtherFractionalBitsA > &a, const FixedPoint< OtherFractionalBitsB > &b) const
Fused multiply-add operation for fixed point numbers with a different number of fractional bits.
Definition fixedpoint.h:502
constexpr FixedPoint operator/=(const FixedPoint &rhs)
Division operator Divide two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:495
static int32_t signed_most_significant_word_multiply_add(int32_t a, int32_t b, int32_t c)
a + b * c
Definition fixedpoint.h:242
constexpr bool operator==(const FixedPoint< OtherFractionalBits > &rhs) const noexcept
Equality operator for fixed point numbers with different number of fractional bits.
Definition fixedpoint.h:528
constexpr FixedPoint(FixedPoint< OtherFractionalBits > other) noexcept
Construct a fixed point number from another fixed point number.
Definition fixedpoint.h:282
constexpr FixedPoint operator+(const FixedPoint &rhs) const
Addition operator Add two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:398
constexpr FixedPoint operator-() const
Negation operator.
Definition fixedpoint.h:384
constexpr FixedPoint operator*(const FixedPoint &rhs) const
Multiplication operator Multiply two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:422
constexpr BaseType raw() const noexcept
Get the internal value.
Definition fixedpoint.h:387
constexpr std::strong_ordering operator<=>(const FixedPoint< OtherFractionalBits > &rhs) const noexcept
Three-way comparison operator for fixed point numbers with different number of fractional bits.
Definition fixedpoint.h:547
static constexpr FixedPoint from_raw(BaseType raw) noexcept
Construct from a raw value.
Definition fixedpoint.h:390
constexpr FixedPoint operator-(const FixedPoint &rhs) const
Subtraction operator Subtract two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:409
constexpr FixedPoint operator*=(const FixedPoint &rhs)
Multiplication operator Multiply two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:440
constexpr FixedPoint(double value) noexcept
Convert from a double to a fixed point number.
Definition fixedpoint.h:340
constexpr FixedPoint operator+=(const FixedPoint &rhs)
Addition operator Add two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:402
constexpr FixedPoint MultiplyAdd(const FixedPoint &a, const FixedPoint &b) const
Fused multiply-add operation for fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:513
constexpr bool operator==(const T &rhs) const noexcept
Equality operator for integers and floating point numbers.
Definition fixedpoint.h:580
constexpr FixedPoint operator-=(const FixedPoint &rhs)
Subtraction operator Subtract two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:415
constexpr FixedPoint()=default
Default constructor.