FastLED 3.9.15
Loading...
Searching...
No Matches
u24x8.h
Go to the documentation of this file.
1#pragma once
2
3// Unsigned 24.8 fixed-point arithmetic.
4// All operations are integer-only in the hot path.
5
6#include "fl/stl/stdint.h"
10#include "fl/stl/noexcept.h"
11#include "fl/stl/undef.h" // Undefine abs/min/max macros from Arduino.h
12
14
15namespace fl {
16
17// Unsigned 24.8 fixed-point value type.
18class u24x8 {
19 public:
20 static constexpr int INT_BITS = 24;
21 static constexpr int FRAC_BITS = 8;
22 static constexpr i32 SCALE = static_cast<i32>(1) << FRAC_BITS;
23
24 // ---- Construction ------------------------------------------------------
25
26 constexpr u24x8() FL_NOEXCEPT = default;
27
28 explicit constexpr u24x8(float f) FL_NOEXCEPT
29 : mValue(static_cast<u32>(f * (SCALE))) {}
30
31 // Integer constructor — any integer width (portable: AVR 16-bit int, ARM/x86 32-bit).
32 // Compile error if constexpr value exceeds INT_BITS range.
33 template <typename IntT, detail::enable_if_integer_t<IntT> = 0>
36
37 // Auto-promotion from other fixed-point types
38 template <typename OtherFP>
39 constexpr u24x8(const OtherFP& other,
40 typename fl::enable_if<
41 (OtherFP::INT_BITS <= INT_BITS) &&
42 (OtherFP::FRAC_BITS <= FRAC_BITS) &&
43 (OtherFP::INT_BITS != INT_BITS || OtherFP::FRAC_BITS != FRAC_BITS),
44 int>::type = 0)
45 FL_NOEXCEPT : mValue(static_cast<u32>(
46 static_cast<u64>(other.raw()) << (FRAC_BITS - OtherFP::FRAC_BITS))) {}
47
48 // Raw constructor for C++11 constexpr from_raw
49 struct RawTag {};
50 constexpr explicit u24x8(u32 raw, RawTag) FL_NOEXCEPT : mValue(raw) {}
51
53 return u24x8(raw, RawTag());
54 }
55
56 // ---- Access ------------------------------------------------------------
57
58 constexpr u32 raw() const FL_NOEXCEPT { return mValue; }
59 constexpr u32 to_int() const FL_NOEXCEPT { return mValue >> FRAC_BITS; }
60 constexpr float to_float() const FL_NOEXCEPT { return static_cast<float>(mValue) / (SCALE); }
61
62 // ---- Fixed-point arithmetic --------------------------------------------
63
65 return from_raw(static_cast<u32>(
66 (static_cast<u64>(mValue) * b.mValue) >> FRAC_BITS));
67 }
68
70 return from_raw(static_cast<u32>(
71 (static_cast<u64>(mValue) * (static_cast<u64>(1) << FRAC_BITS)) / b.mValue));
72 }
73
75 return from_raw(mValue + b.mValue);
76 }
77
79 return from_raw(mValue - b.mValue);
80 }
81
82 constexpr FASTLED_FORCE_INLINE u24x8 operator>>(int shift) const FL_NOEXCEPT {
83 return from_raw(mValue >> shift);
84 }
85
86 constexpr FASTLED_FORCE_INLINE u24x8 operator<<(int shift) const FL_NOEXCEPT {
87 return from_raw(mValue << shift);
88 }
89
90 // ---- Scalar multiply (no fixed-point shift) ----------------------------
91
92 constexpr FASTLED_FORCE_INLINE u24x8 operator*(u32 scalar) const FL_NOEXCEPT {
93 return from_raw(mValue * scalar);
94 }
95
96 friend constexpr u24x8 operator*(u32 scalar, u24x8 fp) FL_NOEXCEPT {
97 return u24x8::from_raw(scalar * fp.mValue);
98 }
99
100 // ---- Comparisons -------------------------------------------------------
101
102 constexpr bool operator<(u24x8 b) const FL_NOEXCEPT { return mValue < b.mValue; }
103 constexpr bool operator>(u24x8 b) const FL_NOEXCEPT { return mValue > b.mValue; }
104 constexpr bool operator<=(u24x8 b) const FL_NOEXCEPT { return mValue <= b.mValue; }
105 constexpr bool operator>=(u24x8 b) const FL_NOEXCEPT { return mValue >= b.mValue; }
106 constexpr bool operator==(u24x8 b) const FL_NOEXCEPT { return mValue == b.mValue; }
107 constexpr bool operator!=(u24x8 b) const FL_NOEXCEPT { return mValue != b.mValue; }
108
109 // ---- Math ---------------------------------------------------------------
110
112 return from_raw(a.mValue % b.mValue);
113 }
114
116 return from_raw(x.mValue & ~(u32((SCALE) - 1)));
117 }
118
120 return from_raw((x.mValue & ~(u32((SCALE) - 1))) +
121 ((x.mValue & u32((SCALE) - 1)) ? (SCALE) : 0));
122 }
123
125 return from_raw(x.mValue & u32((SCALE) - 1));
126 }
127
129 // For unsigned, abs is identity
130 return x;
131 }
132
134 return a.mValue < b.mValue ? a : b;
135 }
136
138 return a.mValue > b.mValue ? a : b;
139 }
140
142 return a + (b - a) * t;
143 }
144
146 return x < lo ? lo : (x > hi ? hi : x);
147 }
148
150 return x < edge ? u24x8() : u24x8(1.0f);
151 }
152
154 constexpr u24x8 zero(0.0f);
155 constexpr u24x8 one(1.0f);
156 constexpr u24x8 two(2.0f);
157 constexpr u24x8 three(3.0f);
158 u24x8 t = clamp((x - edge0) / (edge1 - edge0), zero, one);
159 return t * t * (three - two * t);
160 }
161
163 return x.mValue == 0 ? u24x8() : from_raw(static_cast<u32>(
164 fl::isqrt64(static_cast<u64>(x.mValue) << FRAC_BITS)));
165 }
166
168 return sqrt(x).mValue == 0
169 ? u24x8()
170 : from_raw(static_cast<u32>(1) << FRAC_BITS) / sqrt(x);
171 }
172
174 if (base.mValue == 0) return u24x8();
175 constexpr u24x8 one(1.0f);
176 if (exp.mValue == 0) return one;
177 if (base == one) return one;
178 // Snap base values within ~2 ULPs of 1.0 to exactly 1.0 to dodge the
179 // log2(1+t) minimax polynomial's upper-endpoint residual (#2969).
180 constexpr u32 kOneRaw = static_cast<u32>(SCALE);
181 if (base.mValue >= (kOneRaw - 2u) && base.mValue <= kOneRaw) {
182 return one;
183 }
184 return exp2_fp(exp * log2_fp(base));
185 }
186
187 private:
188 u32 mValue = 0;
189
190 // Returns 0-based position of highest set bit, or -1 if v==0.
191 static constexpr FASTLED_FORCE_INLINE int highest_bit(u32 v) FL_NOEXCEPT {
192 return v == 0 ? -1 : _highest_bit_step(v, 0);
193 }
194
195 static constexpr int _highest_bit_step(u32 v, int r) FL_NOEXCEPT {
196 return (v & 0xFFFF0000u) ? _highest_bit_step(v >> 16, r + 16)
197 : (v & 0x0000FF00u) ? _highest_bit_step(v >> 8, r + 8)
198 : (v & 0x000000F0u) ? _highest_bit_step(v >> 4, r + 4)
199 : (v & 0x0000000Cu) ? _highest_bit_step(v >> 2, r + 2)
200 : (v & 0x00000002u) ? r + 1
201 : r;
202 }
203
204 // Fixed-point log base 2 for positive values.
205 // Uses 4-term minimax polynomial for log2(1+t), t in [0,1).
206 // Horner evaluation uses u64 intermediates (16 frac bits) to minimize
207 // rounding error, then converts back to 8 frac bits.
209 u32 val = x.mValue;
210 int msb = highest_bit(val);
211 if (msb < 0) return u24x8(); // log2(0) = undefined, return 0
212
213 u32 int_part = msb - FRAC_BITS;
214 u32 t;
215 if (msb >= FRAC_BITS) {
216 t = (val >> (msb - FRAC_BITS)) - (SCALE);
217 } else {
218 t = (val << (FRAC_BITS - msb)) - (SCALE);
219 }
220
221 // Simplified 2-term minimax coefficients for log2(1+t), t in [0,1).
222 // Stored as u64 with 16 fractional bits.
223 constexpr int IFRAC = 16;
224 constexpr u64 c0 = 94528ULL; // 1.44179 * 2^16
225 constexpr u64 c1 = 19727ULL; // 0.30093 * 2^16 (unsigned approximation)
226
227 // Extend t from 8 to 16 frac bits.
228 u64 t16 = static_cast<u64>(t) << (IFRAC - FRAC_BITS);
229
230 // Horner for unsigned: simplified polynomial
231 u64 acc = c0 + ((c1 * t16) >> IFRAC);
232 u64 frac_part = (acc * t16) >> IFRAC;
233
234 // Convert from 16 frac bits back to 8.
235 u32 frac8 = static_cast<u32>(frac_part >> (IFRAC - FRAC_BITS));
236 return from_raw((int_part << FRAC_BITS) + frac8);
237 }
238
239 // Fixed-point 2^x. Uses 4-term minimax polynomial for 2^t, t in [0,1).
240 // Horner evaluation uses u64 intermediates (16 frac bits) to minimize
241 // rounding error, then converts back to 8 frac bits.
243 u24x8 fl_val = floor(x);
244 u24x8 fr = x - fl_val;
245 u32 n = fl_val.mValue >> FRAC_BITS;
246 if (n >= INT_BITS) return from_raw(0xFFFFFFFF);
247 u32 int_pow;
248 int_pow = static_cast<u32>(SCALE) << n;
249 // 4-term minimax coefficients for 2^t - 1, t in [0,1).
250 // Stored as u64 with 16 fractional bits.
251 constexpr int IFRAC = 16;
252 constexpr u64 d0 = 45427ULL; // 0.69316 * 2^16
253 constexpr u64 d1 = 15775ULL; // 0.24071 * 2^16
254 constexpr u64 d2 = 3497ULL; // 0.05336 * 2^16
255 constexpr u64 d3 = 836ULL; // 0.01276 * 2^16
256 // Extend fr from 8 to 16 frac bits.
257 u64 fr16 = static_cast<u64>(fr.mValue) << (IFRAC - FRAC_BITS);
258 // Horner: 1 + fr * (d0 + fr * (d1 + fr * (d2 + fr * d3)))
259 u64 acc = d3;
260 acc = d2 + ((acc * fr16) >> IFRAC);
261 acc = d1 + ((acc * fr16) >> IFRAC);
262 acc = d0 + ((acc * fr16) >> IFRAC);
263 constexpr u64 one16 = 1ULL << IFRAC;
264 u64 frac_pow16 = one16 + ((acc * fr16) >> IFRAC);
265 // Convert from 16 frac bits to 8 frac bits, then scale by int_pow.
266 u32 frac_pow8 = static_cast<u32>(frac_pow16 >> (IFRAC - FRAC_BITS));
267 u64 result =
268 (static_cast<u64>(int_pow) * frac_pow8) >> FRAC_BITS;
269 return from_raw(static_cast<u32>(result));
270 }
271};
272
273} // namespace fl
274
constexpr FASTLED_FORCE_INLINE u24x8 operator<<(int shift) const FL_NOEXCEPT
Definition u24x8.h:86
static constexpr FASTLED_FORCE_INLINE u24x8 rsqrt(u24x8 x) FL_NOEXCEPT
Definition u24x8.h:167
static FASTLED_FORCE_INLINE u24x8 pow(u24x8 base, u24x8 exp) FL_NOEXCEPT
Definition u24x8.h:173
static constexpr int INT_BITS
Definition u24x8.h:20
friend constexpr u24x8 operator*(u32 scalar, u24x8 fp) FL_NOEXCEPT
Definition u24x8.h:96
constexpr bool operator>=(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:105
constexpr FASTLED_FORCE_INLINE u24x8 operator/(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:69
static constexpr FASTLED_FORCE_INLINE u24x8 mod(u24x8 a, u24x8 b) FL_NOEXCEPT
Definition u24x8.h:111
static FASTLED_FORCE_INLINE u24x8 smoothstep(u24x8 edge0, u24x8 edge1, u24x8 x) FL_NOEXCEPT
Definition u24x8.h:153
constexpr u32 raw() const FL_NOEXCEPT
Definition u24x8.h:58
constexpr bool operator>(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:103
static constexpr FASTLED_FORCE_INLINE u24x8 max(u24x8 a, u24x8 b) FL_NOEXCEPT
Definition u24x8.h:137
constexpr bool operator==(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:106
constexpr FASTLED_FORCE_INLINE u24x8 operator*(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:64
static constexpr FASTLED_FORCE_INLINE u24x8 abs(u24x8 x) FL_NOEXCEPT
Definition u24x8.h:128
static constexpr FASTLED_FORCE_INLINE u24x8 from_raw(u32 raw) FL_NOEXCEPT
Definition u24x8.h:52
static constexpr FASTLED_FORCE_INLINE u24x8 fract(u24x8 x) FL_NOEXCEPT
Definition u24x8.h:124
u32 mValue
Definition u24x8.h:188
constexpr u24x8() FL_NOEXCEPT=default
static FASTLED_FORCE_INLINE u24x8 exp2_fp(u24x8 x) FL_NOEXCEPT
Definition u24x8.h:242
constexpr bool operator<=(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:104
static constexpr FASTLED_FORCE_INLINE u24x8 floor(u24x8 x) FL_NOEXCEPT
Definition u24x8.h:115
constexpr u24x8(u32 raw, RawTag) FL_NOEXCEPT
Definition u24x8.h:50
static constexpr FASTLED_FORCE_INLINE u24x8 clamp(u24x8 x, u24x8 lo, u24x8 hi) FL_NOEXCEPT
Definition u24x8.h:145
static constexpr int _highest_bit_step(u32 v, int r) FL_NOEXCEPT
Definition u24x8.h:195
static constexpr FASTLED_FORCE_INLINE u24x8 lerp(u24x8 a, u24x8 b, u24x8 t) FL_NOEXCEPT
Definition u24x8.h:141
constexpr FASTLED_FORCE_INLINE u24x8 operator-(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:78
static FASTLED_FORCE_INLINE u24x8 log2_fp(u24x8 x) FL_NOEXCEPT
Definition u24x8.h:208
constexpr FASTLED_FORCE_INLINE u24x8 operator*(u32 scalar) const FL_NOEXCEPT
Definition u24x8.h:92
constexpr float to_float() const FL_NOEXCEPT
Definition u24x8.h:60
static constexpr FASTLED_FORCE_INLINE u24x8 sqrt(u24x8 x) FL_NOEXCEPT
Definition u24x8.h:162
static constexpr FASTLED_FORCE_INLINE u24x8 step(u24x8 edge, u24x8 x) FL_NOEXCEPT
Definition u24x8.h:149
static constexpr FASTLED_FORCE_INLINE u24x8 ceil(u24x8 x) FL_NOEXCEPT
Definition u24x8.h:119
static constexpr FASTLED_FORCE_INLINE int highest_bit(u32 v) FL_NOEXCEPT
Definition u24x8.h:191
static constexpr FASTLED_FORCE_INLINE u24x8 min(u24x8 a, u24x8 b) FL_NOEXCEPT
Definition u24x8.h:133
constexpr u24x8(const OtherFP &other, typename fl::enable_if<(OtherFP::INT_BITS<=INT_BITS) &&(OtherFP::FRAC_BITS<=FRAC_BITS) &&(OtherFP::INT_BITS !=INT_BITS||OtherFP::FRAC_BITS !=FRAC_BITS), int >::type=0) FL_NOEXCEPT
Definition u24x8.h:39
constexpr bool operator<(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:102
constexpr FASTLED_FORCE_INLINE u24x8 operator+(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:74
constexpr u24x8(IntT n) FL_NOEXCEPT
Definition u24x8.h:34
constexpr FASTLED_FORCE_INLINE u24x8 operator>>(int shift) const FL_NOEXCEPT
Definition u24x8.h:82
constexpr u32 to_int() const FL_NOEXCEPT
Definition u24x8.h:59
static constexpr i32 SCALE
Definition u24x8.h:22
constexpr bool operator!=(u24x8 b) const FL_NOEXCEPT
Definition u24x8.h:107
static constexpr int FRAC_BITS
Definition u24x8.h:21
#define constexpr
Declares that it is possible to evaluate a value at compile time, introduced in C++11.
Definition cpp_compat.h:15
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
fl::u64 u64
Definition s16x16x4.h:221
enable_if< is_fixed_point< T >::value, T >::type exp(T x) FL_NOEXCEPT
FL_OPTIMIZE_FUNCTION constexpr u32 isqrt64(u64 x) FL_NOEXCEPT
Definition isqrt.h:58
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_OPTIMIZATION_LEVEL_O3_BEGIN
#define FASTLED_FORCE_INLINE
#define FL_OPTIMIZATION_LEVEL_O3_END
#define FL_NOEXCEPT