19#include "intrinsics.h"
30template <
size_t bit,
typename T>
31requires(bit > 0 && bit < 32)
32[[gnu::always_inline]] [[nodiscard]]
constexpr T roundToBit(T value) {
33 return value + (T{1} << bit) - T{1};
37[[gnu::always_inline]]
static inline int32_t toPositive(int32_t a) {
38 return ((a / 2) + (1073741824));
46template <std::
size_t FractionalBits,
bool Rounded = false,
bool FastApproximation = (FractionalBits == 31 && ARMv7a)>
48 static_assert(FractionalBits > 0,
"FractionalBits must be greater than 0");
49 static_assert(FractionalBits < 32,
"FractionalBits must be less than 32");
51 using BaseType = int32_t;
52 using IntermediateType = int64_t;
56 if constexpr (Rounded) {
57 return multiply_accumulate_32x32_rshift32_rounded(a, b, c);
60 return multiply_accumulate_32x32_rshift32(a, b, c);
65 [[gnu::always_inline]]
static int32_t signed_most_significant_word_multiply(int32_t a, int32_t b) {
66 if constexpr (Rounded) {
67 return multiply_32x32_rshift32_rounded(a, b);
70 return multiply_32x32_rshift32(a, b);
75 static consteval uint32_t one() noexcept {
76 return 1 << FractionalBits;
79 constexpr static std::size_t fractional_bits = FractionalBits;
80 constexpr static std::size_t integral_bits = 32 - FractionalBits;
81 constexpr static bool rounded = Rounded;
82 constexpr static bool fast_approximation = FastApproximation;
92 template <std::
size_t OtherFractionalBits>
94 : value_(other.raw()) {
95 if constexpr (FractionalBits == OtherFractionalBits) {
98 else if constexpr (FractionalBits > OtherFractionalBits) {
100 constexpr int32_t shift = FractionalBits - OtherFractionalBits;
101 value_ = signed_saturate<32 - shift>(value_);
102 value_ = (value_ << shift) + (value_ % 2);
104 else if constexpr (rounded) {
106 constexpr int32_t shift = OtherFractionalBits - FractionalBits;
107 value_ = roundToBit<shift>(value_) >> shift;
111 value_ >>= (OtherFractionalBits - FractionalBits);
115 template <std::
size_t OtherFractionalBits>
122 template <std::
integral T>
123 constexpr explicit FixedPoint(T value) noexcept : value_(
static_cast<BaseType
>(value) << fractional_bits) {}
127 [[gnu::always_inline]]
constexpr FixedPoint(
float value)
noexcept {
128#if __ARM_ARCH_7A__ && !defined(__clang__)
130 asm(
"vcvt.s32.f32 %0, %1, %2" :
"=t"(value) :
"t"(value),
"I"(fractional_bits));
131 value_ = std::bit_cast<int32_t>(value);
136 value *=
static_cast<double>(FixedPoint::one());
138 if constexpr (rounded) {
139 value = std::round(value);
143 value_ =
static_cast<BaseType
>(std::clamp<int64_t>(
144 static_cast<int64_t
>(value), std::numeric_limits<BaseType>::min(), std::numeric_limits<BaseType>::max()));
147 template <std::
size_t OtherFractionalBits,
bool OtherRounded = Rounded,
bool OtherApproximating = FastApproximation>
154 [[gnu::always_inline]]
explicit constexpr operator float() const noexcept {
return to_float(); }
158 [[gnu::always_inline]] [[nodiscard]]
constexpr float to_float() const noexcept {
159#if __ARM_ARCH_7A__ && !defined(__clang__)
161 int32_t output = value_;
162 asm(
"vcvt.f32.s32 %0, %1, %2" :
"=t"(output) :
"t"(output),
"I"(fractional_bits));
163 return std::bit_cast<float>(output);
167 return static_cast<float>(value_) / FixedPoint::one();
172 [[gnu::always_inline]]
constexpr FixedPoint(
double value)
noexcept {
173#if __ARM_ARCH_7A__ && !defined(__clang__)
175 auto output = std::bit_cast<int64_t>(value);
176 asm(
"vcvt.s32.f64 %0, %1, %2" :
"=w"(output) :
"w"(output),
"I"(fractional_bits));
177 value_ =
static_cast<BaseType
>(output);
181 value *=
static_cast<double>(FixedPoint::one());
183 if constexpr (rounded) {
184 value = std::round(value);
188 value_ =
static_cast<BaseType
>(std::clamp<int64_t>(
189 static_cast<int64_t
>(value), std::numeric_limits<BaseType>::min(), std::numeric_limits<BaseType>::max()));
194 [[gnu::always_inline]]
explicit constexpr operator double() const noexcept {
195#if __ARM_ARCH_7A__ && !defined(__clang__)
197 auto output = std::bit_cast<double>((int64_t)value_);
198 asm(
"vcvt.f64.s32 %0, %1, %2" :
"=w"(output) :
"w"(output),
"I"(fractional_bits));
203 return static_cast<double>(value_) / FixedPoint::one();
207 template <std::
size_t OutputFractionalBits>
213 template <std::
signed_
integral T>
214 constexpr explicit operator T() const noexcept {
215 if constexpr (rounded) {
217 return static_cast<T
>(roundToBit<fractional_bits>(value_) >> fractional_bits);
225 constexpr explicit operator bool() const noexcept {
return value_ != 0; }
231 [[gnu::always_inline]] [[nodiscard]]
constexpr BaseType
raw() const noexcept {
return value_; }
243 return from_raw(add_saturate(value_, rhs.value_));
249 value_ = add_saturate(value_, rhs.value_);
256 return from_raw(subtract_saturate(value_, rhs.value_));
262 value_ = subtract_saturate(value_, rhs.value_);
269 if constexpr (fast_approximation && fractional_bits > 16) {
271 constexpr int32_t shift = fractional_bits - ((fractional_bits * 2) - 32);
272 return from_raw(signed_most_significant_word_multiply(value_, rhs.value_) << shift);
274 else if constexpr (rounded) {
275 IntermediateType value = (
static_cast<IntermediateType
>(value_) * rhs.value_) >> (fractional_bits - 1);
276 return from_raw(
static_cast<BaseType
>((value >> 1) + (value % 2)));
279 IntermediateType value = (
static_cast<IntermediateType
>(value_) * rhs.value_) >> fractional_bits;
280 return from_raw(
static_cast<BaseType
>(value));
293 template <std::size_t OutputFractionalBits = FractionalBits, std::size_t OtherFractionalBits,
bool OtherRounded,
294 bool OtherApproximating>
295 requires(OtherRounded == Rounded && OtherApproximating == FastApproximation)
298 if constexpr (fast_approximation) {
299 constexpr int32_t l_shift = OutputFractionalBits - ((FractionalBits + OtherFractionalBits) - 32);
300 static_assert(l_shift < 32 && l_shift > -32);
301 BaseType value = signed_most_significant_word_multiply(value_, rhs.
raw());
302 return from_raw(l_shift > 0 ? value << l_shift : value >> -l_shift);
305 constexpr int32_t r_shift = (FractionalBits + OtherFractionalBits) - OutputFractionalBits;
306 if constexpr (rounded) {
307 IntermediateType value = (
static_cast<IntermediateType
>(value_) * rhs.
raw())
309 return from_raw(
static_cast<BaseType
>((value / 2) + (value % 2)));
312 IntermediateType value = (
static_cast<IntermediateType
>(value_) * rhs.
raw()) >> r_shift;
313 return from_raw(
static_cast<BaseType
>(value));
318 template <std::
size_t OtherFractionalBits>
327 if constexpr (rounded) {
328 IntermediateType value = (
static_cast<IntermediateType
>(value_) << (fractional_bits + 1)) / rhs.value_;
329 return from_raw(
static_cast<BaseType
>((value / 2) + (value % 2)));
332 IntermediateType value = (
static_cast<IntermediateType
>(value_) << fractional_bits) / rhs.value_;
333 return from_raw(
static_cast<BaseType
>(value));
339 template <std::size_t OtherFractionalBits,
340 std::size_t ResultFractionalBits = std::max(FractionalBits, OtherFractionalBits)
341 - std::min(FractionalBits, OtherFractionalBits)>
343 requires(ResultFractionalBits < std::max(FractionalBits, OtherFractionalBits))
344 && (ResultFractionalBits > std::min(FractionalBits, OtherFractionalBits))
347 if (rhs.
raw() == 0) {
351 constexpr int shift =
static_cast<int32_t
>(FractionalBits) - OtherFractionalBits;
352 IntermediateType result{};
353 if constexpr (shift > 0) {
355 result = (
static_cast<IntermediateType
>(value_) << shift) / rhs.
raw();
357 else if constexpr (shift < 0) {
359 result = (
static_cast<IntermediateType
>(value_) / (rhs.
raw() << -shift));
363 result =
static_cast<IntermediateType
>(value_) / rhs.
raw();
365 size_t result_shift = std::max(FractionalBits, OtherFractionalBits) - ResultFractionalBits;
367 if constexpr (rounded) {
369 result += (1 << (result_shift - 1));
384 template <std::
size_t OtherFractionalBitsA, std::
size_t OtherFractionalBitsB>
389 if constexpr (fast_approximation && (OtherFractionalBitsA + OtherFractionalBitsB) - 32 == FractionalBits) {
393 return *
this +
static_cast<FixedPoint>(a * b);
400 if constexpr (fast_approximation && (((fractional_bits * 2) - 32) == (fractional_bits - 1))) {
405 return *
this + (a * b);
411 return value_ == rhs.value_;
418 template <std::
size_t OtherFractionalBits>
420 int integral_value = value_ >> fractional_bits;
421 int other_integral_value = rhs.
raw() >> OtherFractionalBits;
422 int fractional_value = value_ & ((1 << fractional_bits) - 1);
423 int other_fractional_value = rhs.
raw() & ((1 << OtherFractionalBits) - 1);
426 if constexpr (fractional_bits > OtherFractionalBits) {
427 fractional_value >>= fractional_bits - OtherFractionalBits;
430 other_fractional_value >>= OtherFractionalBits - fractional_bits;
433 return integral_value == other_integral_value && fractional_value == other_fractional_value;
437 template <std::
size_t OtherFractionalBits>
440 int integral_value = value_ >> fractional_bits;
441 int other_integral_value = rhs.
raw() >> OtherFractionalBits;
442 int fractional_value = value_ & ((1 << fractional_bits) - 1);
443 int other_fractional_value = rhs.
raw() & ((1 << OtherFractionalBits) - 1);
446 if (fractional_bits > OtherFractionalBits) {
447 fractional_value >>= fractional_bits - OtherFractionalBits;
450 other_fractional_value >>= OtherFractionalBits - fractional_bits;
453 if (integral_value < other_integral_value) {
454 return std::strong_ordering::less;
456 if (integral_value > other_integral_value) {
457 return std::strong_ordering::greater;
459 if (fractional_value < other_fractional_value) {
460 return std::strong_ordering::less;
462 if (fractional_value > other_fractional_value) {
463 return std::strong_ordering::greater;
465 return std::strong_ordering::equal;
469 template <
typename T>
470 requires std::floating_point<T>
471 [[gnu::always_inline]]
constexpr bool operator==(
const T& rhs)
const noexcept {
476 template <std::
integral T>
482 template <std::
integral T>
487 template <std::
integral T>
492 template <std::
integral T>
497 [[gnu::always_inline]] [[nodiscard]]
constexpr int32_t integral()
const {
498 return static_cast<int32_t
>(value_ >> fractional_bits);
501 [[gnu::always_inline]]
constexpr FixedPoint absolute() const noexcept {
502 if constexpr (fractional_bits == 31) {
504 return from_raw((value_ / 2) + (1073741824));
507 return from_raw(value_ < 0 ? -value_ : value_);
516template <std::
integral T, std::
size_t FractionalBits,
bool Rounded,
bool FastApproximation>
522template <std::
floating_po
int T, std::
size_t FractionalBits,
bool Rounded,
bool FastApproximation>
528template <std::
floating_po
int T, std::
size_t FractionalBits,
bool Rounded,
bool FastApproximation>
534template <std::
floating_po
int T, std::
size_t FractionalBits,
bool Rounded,
bool FastApproximation>
542constexpr int32_t ONE_Q31 = std::numeric_limits<int32_t>::max();
543constexpr float ONE_Q31f =
static_cast<float>(ONE_Q31);
544constexpr int32_t ONE_Q16 = std::numeric_limits<uint16_t>::max();
545constexpr int32_t NEGATIVE_ONE_Q31 = std::numeric_limits<int32_t>::min();
546constexpr int32_t ONE_OVER_SQRT2_Q31 = ONE_Q31 / std::numbers::sqrt2;
549constexpr float ONE_Q31f =
static_cast<float>(ONE_Q31);
552constexpr int32_t ONE_OVER_SQRT2_Q31 =
FixedPoint<31>{1 / std::numbers::sqrt2}.
raw();
555static_assert(ONE_Q31 == std::numeric_limits<int32_t>::max());
556static_assert(ONE_Q31f == 1.0f * std::numeric_limits<int32_t>::max());
557static_assert(ONE_Q16 == std::numeric_limits<uint16_t>::max());
558static_assert(NEGATIVE_ONE_Q31 == std::numeric_limits<int32_t>::min());
559static_assert(ONE_OVER_SQRT2_Q31 == 1518500249);
Fixed point number with a configurable number of fractional bits.
Definition fixedpoint.h:47
constexpr bool operator==(const FixedPoint &rhs) const noexcept
Equality operator.
Definition fixedpoint.h:410
constexpr FixedPoint operator*=(const FixedPoint< OtherFractionalBits > &rhs)
Multiplication operator Multiply two fixed point numbers with different number of fractional bits.
Definition fixedpoint.h:319
constexpr FixedPoint DivideInt(const T &rhs) const
Divide by an integral type.
Definition fixedpoint.h:483
constexpr FixedPoint(float value) noexcept
Convert from a float to a fixed point number.
Definition fixedpoint.h:127
constexpr std::strong_ordering operator<=>(const FixedPoint &rhs) const noexcept
Three-way comparison operator.
Definition fixedpoint.h:415
constexpr FixedPoint operator/(const FixedPoint &rhs) const
Division operator Divide two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:326
constexpr float to_float() const noexcept
Explicit conversion to float.
Definition fixedpoint.h:158
constexpr bool operator==(const T &rhs) const noexcept
Equality operator for integers and floating point numbers.
Definition fixedpoint.h:471
constexpr FixedPoint(T value) noexcept
Convert an integer to a fixed point number.
Definition fixedpoint.h:123
constexpr FixedPoint< OutputFractionalBits > as() const
Convert to a fixed point number with a different number of fractional bits.
Definition fixedpoint.h:208
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:385
constexpr FixedPoint operator/=(const FixedPoint &rhs)
Division operator Divide two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:377
static int32_t signed_most_significant_word_multiply_add(int32_t a, int32_t b, int32_t c)
a + b * c
Definition fixedpoint.h:55
constexpr FixedPoint< ResultFractionalBits, Rounded, FastApproximation > operator/(const FixedPoint< OtherFractionalBits > &rhs) const
Division operator Divide two fixed point numbers with different number of fractional bits.
Definition fixedpoint.h:346
constexpr bool operator==(const FixedPoint< OtherFractionalBits > &rhs) const noexcept
Equality operator for fixed point numbers with different number of fractional bits.
Definition fixedpoint.h:419
constexpr FixedPoint(FixedPoint< OtherFractionalBits > other) noexcept
Construct a fixed point number from another fixed point number.
Definition fixedpoint.h:93
constexpr FixedPoint operator+(const FixedPoint &rhs) const
Addition operator Add two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:242
constexpr FixedPoint operator-() const
Negation operator.
Definition fixedpoint.h:228
constexpr FixedPoint operator*(const FixedPoint &rhs) const
Multiplication operator Multiply two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:268
constexpr BaseType raw() const noexcept
Get the internal value.
Definition fixedpoint.h:231
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:438
static constexpr FixedPoint from_raw(BaseType raw) noexcept
Construct from a raw value.
Definition fixedpoint.h:234
constexpr FixedPoint operator-(const FixedPoint &rhs) const
Subtraction operator Subtract two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:255
constexpr FixedPoint operator*=(const FixedPoint &rhs)
Multiplication operator Multiply two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:286
constexpr FixedPoint(double value) noexcept
Convert from a double to a fixed point number.
Definition fixedpoint.h:172
constexpr FixedPoint operator+=(const FixedPoint &rhs)
Addition operator Add two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:248
constexpr FixedPoint MultiplyInt(const T &rhs) const
Multiply by an integral type.
Definition fixedpoint.h:477
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:399
constexpr FixedPoint operator-=(const FixedPoint &rhs)
Subtraction operator Subtract two fixed point numbers with the same number of fractional bits.
Definition fixedpoint.h:261
constexpr FixedPoint()=default
Default constructor.