FastLED 3.9.15
Loading...
Searching...
No Matches
u12x4.h
Go to the documentation of this file.
1#pragma once
2
3// Unsigned 12.4 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 12.4 fixed-point value type.
18class u12x4 {
19 public:
20 static constexpr int INT_BITS = 12;
21 static constexpr int FRAC_BITS = 4;
22 static constexpr i32 SCALE = static_cast<i32>(1) << FRAC_BITS;
23
24 // ---- Construction ------------------------------------------------------
25
26 constexpr u12x4() FL_NOEXCEPT = default;
27
28 explicit constexpr u12x4(float f) FL_NOEXCEPT
29 : mValue(static_cast<u16>(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 // Raw constructor for C++11 constexpr from_raw
38 struct RawTag {};
39 constexpr explicit u12x4(u16 raw, RawTag) FL_NOEXCEPT : mValue(raw) {}
40
42 return u12x4(raw, RawTag());
43 }
44
45 // ---- Access ------------------------------------------------------------
46
47 constexpr u16 raw() const FL_NOEXCEPT { return mValue; }
48 constexpr u16 to_int() const FL_NOEXCEPT { return mValue >> FRAC_BITS; }
49 constexpr float to_float() const FL_NOEXCEPT { return static_cast<float>(mValue) / (SCALE); }
50
51 // ---- Fixed-point arithmetic --------------------------------------------
52
54 return from_raw(static_cast<u16>(
55 (static_cast<u32>(mValue) * b.mValue) >> FRAC_BITS));
56 }
57
59 return from_raw(static_cast<u16>(
60 (static_cast<u32>(mValue) * (static_cast<u32>(1) << FRAC_BITS)) / b.mValue));
61 }
62
64 return from_raw(mValue + b.mValue);
65 }
66
68 return from_raw(mValue - b.mValue);
69 }
70
71 constexpr FASTLED_FORCE_INLINE u12x4 operator>>(int shift) const FL_NOEXCEPT {
72 return from_raw(mValue >> shift);
73 }
74
75 constexpr FASTLED_FORCE_INLINE u12x4 operator<<(int shift) const FL_NOEXCEPT {
76 return from_raw(mValue << shift);
77 }
78
79 // ---- Compound assignment operators -------------------------------------
80
82 mValue += b.mValue;
83 return *this;
84 }
85
87 mValue -= b.mValue;
88 return *this;
89 }
90
92 mValue = static_cast<u16>(
93 (static_cast<u32>(mValue) * b.mValue) >> FRAC_BITS);
94 return *this;
95 }
96
98 mValue = static_cast<u16>(
99 (static_cast<u32>(mValue) * (static_cast<u32>(1) << FRAC_BITS)) / b.mValue);
100 return *this;
101 }
102
103 // ---- Scalar multiply (no fixed-point shift) ----------------------------
104
105 constexpr FASTLED_FORCE_INLINE u12x4 operator*(u16 scalar) const FL_NOEXCEPT {
106 return from_raw(mValue * scalar);
107 }
108
109 friend constexpr u12x4 operator*(u16 scalar, u12x4 fp) FL_NOEXCEPT {
110 return u12x4::from_raw(scalar * fp.mValue);
111 }
112
113 // ---- Comparisons -------------------------------------------------------
114
115 constexpr bool operator<(u12x4 b) const FL_NOEXCEPT { return mValue < b.mValue; }
116 constexpr bool operator>(u12x4 b) const FL_NOEXCEPT { return mValue > b.mValue; }
117 constexpr bool operator<=(u12x4 b) const FL_NOEXCEPT { return mValue <= b.mValue; }
118 constexpr bool operator>=(u12x4 b) const FL_NOEXCEPT { return mValue >= b.mValue; }
119 constexpr bool operator==(u12x4 b) const FL_NOEXCEPT { return mValue == b.mValue; }
120 constexpr bool operator!=(u12x4 b) const FL_NOEXCEPT { return mValue != b.mValue; }
121
122 // ---- Math ---------------------------------------------------------------
123
125 return from_raw(a.mValue % b.mValue);
126 }
127
129 return from_raw(x.mValue & ~(u16((SCALE) - 1)));
130 }
131
133 return from_raw((x.mValue & ~(u16((SCALE) - 1))) +
134 ((x.mValue & u16((SCALE) - 1)) ? (SCALE) : 0));
135 }
136
138 return from_raw(x.mValue & u16((SCALE) - 1));
139 }
140
142 // Unsigned values are always non-negative
143 return x;
144 }
145
147 return a < b ? a : b;
148 }
149
151 return a > b ? a : b;
152 }
153
155 return a + (b - a) * t;
156 }
157
159 return x < lo ? lo : (x > hi ? hi : x);
160 }
161
163 return x < edge ? u12x4() : u12x4(1.0f);
164 }
165
167 constexpr u12x4 zero(0.0f);
168 constexpr u12x4 one(1.0f);
169 constexpr u12x4 two(2.0f);
170 constexpr u12x4 three(3.0f);
171 u12x4 t = clamp((x - edge0) / (edge1 - edge0), zero, one);
172 return t * t * (three - two * t);
173 }
174
176 return x.mValue == 0 ? u12x4() : from_raw(static_cast<u16>(
177 fl::isqrt32(static_cast<u32>(x.mValue) << FRAC_BITS)));
178 }
179
181 return sqrt(x).mValue == 0
182 ? u12x4()
183 : from_raw(static_cast<u16>(1) << FRAC_BITS) / sqrt(x);
184 }
185
187 if (base.mValue == 0) return u12x4();
188 constexpr u12x4 one(1.0f);
189 if (exp.mValue == 0) return one;
190 if (base == one) return one;
191 // Snap base values within ~2 ULPs of 1.0 to exactly 1.0 to dodge the
192 // log2(1+t) minimax polynomial's upper-endpoint residual (#2969).
193 constexpr u16 kOneRaw = static_cast<u16>(SCALE);
194 if (base.mValue >= static_cast<u16>(kOneRaw - 2) &&
195 base.mValue <= kOneRaw) {
196 return one;
197 }
198 return exp2_fp(exp * log2_fp(base));
199 }
200
201 private:
202 u16 mValue = 0;
203
204 // Returns 0-based position of highest set bit, or -1 if v==0.
205 static constexpr FASTLED_FORCE_INLINE int highest_bit(u32 v) FL_NOEXCEPT {
206 return v == 0 ? -1 : _highest_bit_step(v, 0);
207 }
208
209 static constexpr int _highest_bit_step(u32 v, int r) FL_NOEXCEPT {
210 return (v & 0xFFFF0000u) ? _highest_bit_step(v >> 16, r + 16)
211 : (v & 0x0000FF00u) ? _highest_bit_step(v >> 8, r + 8)
212 : (v & 0x000000F0u) ? _highest_bit_step(v >> 4, r + 4)
213 : (v & 0x0000000Cu) ? _highest_bit_step(v >> 2, r + 2)
214 : (v & 0x00000002u) ? r + 1
215 : r;
216 }
217
218 // Fixed-point log base 2 for positive values.
219 // Uses 4-term minimax polynomial for log2(1+t), t in [0,1).
220 // Horner evaluation uses u32 intermediates (12 frac bits) to minimize
221 // rounding error, then converts back to 4 frac bits.
223 u32 val = static_cast<u32>(x.mValue);
224 int msb = highest_bit(val);
225 u32 int_part = msb - FRAC_BITS;
226 u32 t;
227 if (msb >= FRAC_BITS) {
228 t = static_cast<u32>(
229 (val >> (msb - FRAC_BITS)) - (SCALE));
230 } else {
231 t = static_cast<u32>(
232 (val << (FRAC_BITS - msb)) - (SCALE));
233 }
234 // 4-term minimax coefficients for log2(1+t), t in [0,1).
235 // Stored as u32 with 12 fractional bits. Max product ~2^21, fits u32 comfortably.
236 constexpr int IFRAC = 12;
237 constexpr u32 c0 = 5907; // 1.44179 * 2^12
238 constexpr u32 c1 = 2864; // 0.69907 * 2^12 (no negation for unsigned)
239 constexpr u32 c2 = 1489; // 0.36348 * 2^12
240 constexpr u32 c3 = 437; // 0.10660 * 2^12 (no negation for unsigned)
241 // Extend t from 4 to 12 frac bits.
242 u32 t12 = static_cast<u32>(t) << (IFRAC - FRAC_BITS);
243 // Simplified Horner for unsigned: t * (c0 + t * c1 + t^2 * c2 + t^3 * c3)
244 u32 acc = c3;
245 acc = c2 + ((acc * t12) >> IFRAC);
246 acc = c1 + ((acc * t12) >> IFRAC);
247 acc = c0 + ((acc * t12) >> IFRAC);
248 u32 frac_part = (acc * t12) >> IFRAC;
249 // Convert from 12 frac bits back to 4.
250 u16 frac4 = static_cast<u16>(frac_part >> (IFRAC - FRAC_BITS));
251 return from_raw(static_cast<u16>((int_part << FRAC_BITS) + frac4));
252 }
253
254 // Fixed-point 2^x. Uses 4-term minimax polynomial for 2^t, t in [0,1).
255 // Horner evaluation uses u32 intermediates (12 frac bits) to minimize
256 // rounding error, then converts back to 4 frac bits.
258 u12x4 fl_val = floor(x);
259 u12x4 fr = x - fl_val;
260 u32 n = fl_val.mValue >> FRAC_BITS;
261 if (n >= INT_BITS - 1) return from_raw(0xFFFF);
262 u32 int_pow = static_cast<u32>(SCALE) << n;
263 // 4-term minimax coefficients for 2^t - 1, t in [0,1).
264 // Stored as u32 with 12 fractional bits.
265 constexpr int IFRAC = 12;
266 constexpr u32 d0 = 2839; // 0.69316 * 2^12
267 constexpr u32 d1 = 986; // 0.24071 * 2^12
268 constexpr u32 d2 = 219; // 0.05336 * 2^12
269 constexpr u32 d3 = 52; // 0.01276 * 2^12
270 // Extend fr from 4 to 12 frac bits.
271 u32 fr12 = static_cast<u32>(fr.mValue) << (IFRAC - FRAC_BITS);
272 // Horner: 1 + fr * (d0 + fr * (d1 + fr * (d2 + fr * d3)))
273 u32 acc = d3;
274 acc = d2 + ((acc * fr12) >> IFRAC);
275 acc = d1 + ((acc * fr12) >> IFRAC);
276 acc = d0 + ((acc * fr12) >> IFRAC);
277 constexpr u32 one12 = 1 << IFRAC;
278 u32 frac_pow12 = one12 + ((acc * fr12) >> IFRAC);
279 // Convert from 12 frac bits to 4 frac bits, then scale by int_pow.
280 u32 frac_pow4 = frac_pow12 >> (IFRAC - FRAC_BITS);
281 u32 result =
282 (int_pow * frac_pow4) >> FRAC_BITS;
283 return from_raw(static_cast<u16>(result));
284 }
285};
286
287} // namespace fl
288
constexpr FASTLED_FORCE_INLINE u12x4 operator>>(int shift) const FL_NOEXCEPT
Definition u12x4.h:71
static constexpr FASTLED_FORCE_INLINE u12x4 fract(u12x4 x) FL_NOEXCEPT
Definition u12x4.h:137
constexpr bool operator>(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:116
constexpr bool operator<=(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:117
static FASTLED_FORCE_INLINE u12x4 pow(u12x4 base, u12x4 exp) FL_NOEXCEPT
Definition u12x4.h:186
constexpr FASTLED_FORCE_INLINE u12x4 operator<<(int shift) const FL_NOEXCEPT
Definition u12x4.h:75
static constexpr FASTLED_FORCE_INLINE u12x4 abs(u12x4 x) FL_NOEXCEPT
Definition u12x4.h:141
static constexpr FASTLED_FORCE_INLINE u12x4 min(u12x4 a, u12x4 b) FL_NOEXCEPT
Definition u12x4.h:146
constexpr u12x4(IntT n) FL_NOEXCEPT
Definition u12x4.h:34
constexpr FASTLED_FORCE_INLINE u12x4 operator*(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:53
static constexpr FASTLED_FORCE_INLINE u12x4 floor(u12x4 x) FL_NOEXCEPT
Definition u12x4.h:128
static constexpr FASTLED_FORCE_INLINE u12x4 mod(u12x4 a, u12x4 b) FL_NOEXCEPT
Definition u12x4.h:124
constexpr u12x4() FL_NOEXCEPT=default
static constexpr FASTLED_FORCE_INLINE u12x4 ceil(u12x4 x) FL_NOEXCEPT
Definition u12x4.h:132
static constexpr FASTLED_FORCE_INLINE u12x4 step(u12x4 edge, u12x4 x) FL_NOEXCEPT
Definition u12x4.h:162
constexpr u16 to_int() const FL_NOEXCEPT
Definition u12x4.h:48
static FASTLED_FORCE_INLINE u12x4 smoothstep(u12x4 edge0, u12x4 edge1, u12x4 x) FL_NOEXCEPT
Definition u12x4.h:166
constexpr FASTLED_FORCE_INLINE u12x4 operator/(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:58
constexpr FASTLED_FORCE_INLINE u12x4 operator+(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:63
FASTLED_FORCE_INLINE u12x4 & operator/=(u12x4 b) FL_NOEXCEPT
Definition u12x4.h:97
FASTLED_FORCE_INLINE u12x4 & operator-=(u12x4 b) FL_NOEXCEPT
Definition u12x4.h:86
constexpr FASTLED_FORCE_INLINE u12x4 operator-(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:67
static constexpr i32 SCALE
Definition u12x4.h:22
static constexpr FASTLED_FORCE_INLINE u12x4 max(u12x4 a, u12x4 b) FL_NOEXCEPT
Definition u12x4.h:150
FASTLED_FORCE_INLINE u12x4 & operator*=(u12x4 b) FL_NOEXCEPT
Definition u12x4.h:91
constexpr u12x4(u16 raw, RawTag) FL_NOEXCEPT
Definition u12x4.h:39
friend constexpr u12x4 operator*(u16 scalar, u12x4 fp) FL_NOEXCEPT
Definition u12x4.h:109
static constexpr int FRAC_BITS
Definition u12x4.h:21
FASTLED_FORCE_INLINE u12x4 & operator+=(u12x4 b) FL_NOEXCEPT
Definition u12x4.h:81
static constexpr FASTLED_FORCE_INLINE u12x4 sqrt(u12x4 x) FL_NOEXCEPT
Definition u12x4.h:175
static constexpr FASTLED_FORCE_INLINE u12x4 clamp(u12x4 x, u12x4 lo, u12x4 hi) FL_NOEXCEPT
Definition u12x4.h:158
constexpr FASTLED_FORCE_INLINE u12x4 operator*(u16 scalar) const FL_NOEXCEPT
Definition u12x4.h:105
static constexpr FASTLED_FORCE_INLINE u12x4 lerp(u12x4 a, u12x4 b, u12x4 t) FL_NOEXCEPT
Definition u12x4.h:154
constexpr float to_float() const FL_NOEXCEPT
Definition u12x4.h:49
constexpr bool operator<(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:115
static constexpr int _highest_bit_step(u32 v, int r) FL_NOEXCEPT
Definition u12x4.h:209
static constexpr FASTLED_FORCE_INLINE u12x4 from_raw(u16 raw) FL_NOEXCEPT
Definition u12x4.h:41
constexpr bool operator!=(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:120
constexpr bool operator>=(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:118
constexpr bool operator==(u12x4 b) const FL_NOEXCEPT
Definition u12x4.h:119
static constexpr FASTLED_FORCE_INLINE int highest_bit(u32 v) FL_NOEXCEPT
Definition u12x4.h:205
static FASTLED_FORCE_INLINE u12x4 log2_fp(u12x4 x) FL_NOEXCEPT
Definition u12x4.h:222
constexpr u16 raw() const FL_NOEXCEPT
Definition u12x4.h:47
static constexpr FASTLED_FORCE_INLINE u12x4 rsqrt(u12x4 x) FL_NOEXCEPT
Definition u12x4.h:180
static constexpr int INT_BITS
Definition u12x4.h:20
static FASTLED_FORCE_INLINE u12x4 exp2_fp(u12x4 x) FL_NOEXCEPT
Definition u12x4.h:257
u16 mValue
Definition u12x4.h:202
#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
enable_if< is_fixed_point< T >::value, T >::type exp(T x) FL_NOEXCEPT
FL_OPTIMIZE_FUNCTION constexpr u16 isqrt32(u32 x) FL_NOEXCEPT
Definition isqrt.h:53
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