FastLED 3.9.15
Loading...
Searching...
No Matches
u4x12.h
Go to the documentation of this file.
1#pragma once
2
3// Unsigned 4.12 fixed-point arithmetic.
4// All operations are integer-only in the hot path.
5
6#include "fl/stl/int.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 4.12 fixed-point value type.
18class u4x12 {
19 public:
20 static constexpr int INT_BITS = 4;
21 static constexpr int FRAC_BITS = 12;
22 static constexpr i32 SCALE = static_cast<i32>(1) << FRAC_BITS;
23
24 // ---- Construction ------------------------------------------------------
25
26 constexpr u4x12() FL_NOEXCEPT = default;
27
28 explicit constexpr u4x12(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 // Auto-promotion from other fixed-point types
38 template <typename OtherFP>
39 constexpr u4x12(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<u16>(
46 static_cast<u32>(other.raw()) << (FRAC_BITS - OtherFP::FRAC_BITS))) {}
47
48 // Raw constructor for C++11 constexpr from_raw
49 struct RawTag {};
50 constexpr explicit u4x12(u16 raw, RawTag) FL_NOEXCEPT : mValue(raw) {}
51
53 return u4x12(raw, RawTag());
54 }
55
56 // ---- Access ------------------------------------------------------------
57
58 constexpr u16 raw() const FL_NOEXCEPT { return mValue; }
59 constexpr u16 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<u16>(
66 (static_cast<u32>(mValue) * b.mValue) >> FRAC_BITS));
67 }
68
70 return from_raw(static_cast<u16>(
71 (static_cast<u32>(mValue) * (static_cast<u32>(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 u4x12 operator>>(int shift) const FL_NOEXCEPT {
83 return from_raw(mValue >> shift);
84 }
85
86 constexpr FASTLED_FORCE_INLINE u4x12 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 u4x12 operator*(u16 scalar) const FL_NOEXCEPT {
93 return from_raw(mValue * scalar);
94 }
95
96 friend constexpr u4x12 operator*(u16 scalar, u4x12 fp) FL_NOEXCEPT {
97 return u4x12::from_raw(scalar * fp.mValue);
98 }
99
100 // ---- Comparisons -------------------------------------------------------
101
102 constexpr bool operator<(u4x12 b) const FL_NOEXCEPT { return mValue < b.mValue; }
103 constexpr bool operator>(u4x12 b) const FL_NOEXCEPT { return mValue > b.mValue; }
104 constexpr bool operator<=(u4x12 b) const FL_NOEXCEPT { return mValue <= b.mValue; }
105 constexpr bool operator>=(u4x12 b) const FL_NOEXCEPT { return mValue >= b.mValue; }
106 constexpr bool operator==(u4x12 b) const FL_NOEXCEPT { return mValue == b.mValue; }
107 constexpr bool operator!=(u4x12 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 & ~(u16((SCALE) - 1)));
117 }
118
120 return from_raw((x.mValue & ~(u16((SCALE) - 1))) +
121 ((x.mValue & u16((SCALE) - 1)) ? (SCALE) : 0));
122 }
123
125 return from_raw(x.mValue & u16((SCALE) - 1));
126 }
127
129 // For unsigned type, abs is identity
130 return x;
131 }
132
134 return a < b ? a : b;
135 }
136
138 return a > b ? 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 ? u4x12() : u4x12(1.0f);
151 }
152
154 constexpr u4x12 zero(0.0f);
155 constexpr u4x12 one(1.0f);
156 constexpr u4x12 two(2.0f);
157 constexpr u4x12 three(3.0f);
158 u4x12 t = clamp((x - edge0) / (edge1 - edge0), zero, one);
159 return t * t * (three - two * t);
160 }
161
163 return x.mValue == 0 ? u4x12() : from_raw(static_cast<u16>(
164 fl::isqrt32(static_cast<u32>(x.mValue) << FRAC_BITS)));
165 }
166
168 return sqrt(x).mValue == 0
169 ? u4x12()
170 : from_raw(static_cast<u16>(1) << FRAC_BITS) / sqrt(x);
171 }
172
174 if (base.mValue == 0) return u4x12();
175 constexpr u4x12 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 u16 kOneRaw = static_cast<u16>(SCALE);
181 if (base.mValue >= static_cast<u16>(kOneRaw - 2) &&
182 base.mValue <= kOneRaw) {
183 return one;
184 }
185 return exp2_fp(exp * log2_fp(base));
186 }
187
188 private:
189 u16 mValue = 0;
190
191 // Returns 0-based position of highest set bit, or -1 if v==0.
192 static constexpr FASTLED_FORCE_INLINE int highest_bit(u32 v) FL_NOEXCEPT {
193 return v == 0 ? -1 : _highest_bit_step(v, 0);
194 }
195
196 static constexpr int _highest_bit_step(u32 v, int r) FL_NOEXCEPT {
197 return (v & 0xFFFF0000u) ? _highest_bit_step(v >> 16, r + 16)
198 : (v & 0x0000FF00u) ? _highest_bit_step(v >> 8, r + 8)
199 : (v & 0x000000F0u) ? _highest_bit_step(v >> 4, r + 4)
200 : (v & 0x0000000Cu) ? _highest_bit_step(v >> 2, r + 2)
201 : (v & 0x00000002u) ? r + 1
202 : r;
203 }
204
205 // Fixed-point log base 2 for positive values.
206 // Uses 4-term minimax polynomial for log2(1+t), t in [0,1).
207 // Horner evaluation uses i32 intermediates (20 frac bits) to minimize
208 // rounding error, then converts back to 12 frac bits.
210 u32 val = static_cast<u32>(x.mValue);
211 int msb = highest_bit(val);
212 i32 int_part = msb - FRAC_BITS;
213 i32 t;
214 if (msb >= FRAC_BITS) {
215 t = static_cast<i32>(
216 (val >> (msb - FRAC_BITS)) - (SCALE));
217 } else {
218 t = static_cast<i32>(
219 (val << (FRAC_BITS - msb)) - (SCALE));
220 }
221 // 4-term minimax coefficients for log2(1+t), t in [0,1).
222 // Stored as i32 with 20 fractional bits. Max product ~2^33, fits i64 intermediate.
223 constexpr int IFRAC = 20;
224 constexpr i32 c0 = 1512456; // 1.44179 * 2^20
225 constexpr i32 c1 = -733024; // -0.69907 * 2^20
226 constexpr i32 c2 = 381136; // 0.36348 * 2^20
227 constexpr i32 c3 = -111776; // -0.10660 * 2^20
228 // Extend t from 12 to 20 frac bits.
229 i32 t20 = static_cast<i32>(t) << (IFRAC - FRAC_BITS);
230 // Horner: t * (c0 + t * (c1 + t * (c2 + t * c3)))
231 i32 acc = c3;
232 acc = c2 + static_cast<i32>((static_cast<i64>(acc) * t20) >> IFRAC);
233 acc = c1 + static_cast<i32>((static_cast<i64>(acc) * t20) >> IFRAC);
234 acc = c0 + static_cast<i32>((static_cast<i64>(acc) * t20) >> IFRAC);
235 i32 frac_part = static_cast<i32>((static_cast<i64>(acc) * t20) >> IFRAC);
236 // Convert from 20 frac bits back to 12.
237 u16 frac12 = static_cast<u16>(frac_part >> (IFRAC - FRAC_BITS));
238 return from_raw(static_cast<u16>((static_cast<i32>(static_cast<u32>(int_part) << FRAC_BITS)) + frac12));
239 }
240
241 // Fixed-point 2^x. Uses 4-term minimax polynomial for 2^t, t in [0,1).
242 // Horner evaluation uses i32 intermediates (20 frac bits) to minimize
243 // rounding error, then converts back to 12 frac bits.
245 u4x12 fl_val = floor(x);
246 u4x12 fr = x - fl_val;
247 i32 n = fl_val.mValue >> FRAC_BITS;
248 if (n >= INT_BITS) return from_raw(0xFFFF);
249 if (n < 0) return u4x12();
250 u32 int_pow = static_cast<u32>(SCALE) << n;
251 // 4-term minimax coefficients for 2^t - 1, t in [0,1).
252 // Stored as i32 with 20 fractional bits.
253 constexpr int IFRAC = 20;
254 constexpr i32 d0 = 726836; // 0.69316 * 2^20
255 constexpr i32 d1 = 252400; // 0.24071 * 2^20
256 constexpr i32 d2 = 55952; // 0.05336 * 2^20
257 constexpr i32 d3 = 13376; // 0.01276 * 2^20
258 // Extend fr from 12 to 20 frac bits.
259 i32 fr20 = static_cast<i32>(fr.mValue) << (IFRAC - FRAC_BITS);
260 // Horner: 1 + fr * (d0 + fr * (d1 + fr * (d2 + fr * d3)))
261 i32 acc = d3;
262 acc = d2 + static_cast<i32>((static_cast<i64>(acc) * fr20) >> IFRAC);
263 acc = d1 + static_cast<i32>((static_cast<i64>(acc) * fr20) >> IFRAC);
264 acc = d0 + static_cast<i32>((static_cast<i64>(acc) * fr20) >> IFRAC);
265 constexpr i32 one20 = static_cast<i32>(1) << IFRAC;
266 i32 frac_pow20 = one20 + static_cast<i32>((static_cast<i64>(acc) * fr20) >> IFRAC);
267 // Convert from 20 frac bits to 12 frac bits, then scale by int_pow.
268 u32 frac_pow12 = static_cast<u32>(frac_pow20) >> (IFRAC - FRAC_BITS);
269 u32 result =
270 (int_pow * frac_pow12) >> FRAC_BITS;
271 return from_raw(static_cast<u16>(result));
272 }
273};
274
275} // namespace fl
276
static constexpr int INT_BITS
Definition u4x12.h:20
constexpr bool operator!=(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:107
static constexpr i32 SCALE
Definition u4x12.h:22
static constexpr FASTLED_FORCE_INLINE u4x12 lerp(u4x12 a, u4x12 b, u4x12 t) FL_NOEXCEPT
Definition u4x12.h:141
constexpr float to_float() const FL_NOEXCEPT
Definition u4x12.h:60
constexpr bool operator<(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:102
static constexpr FASTLED_FORCE_INLINE u4x12 fract(u4x12 x) FL_NOEXCEPT
Definition u4x12.h:124
static constexpr FASTLED_FORCE_INLINE u4x12 clamp(u4x12 x, u4x12 lo, u4x12 hi) FL_NOEXCEPT
Definition u4x12.h:145
u16 mValue
Definition u4x12.h:189
static constexpr int _highest_bit_step(u32 v, int r) FL_NOEXCEPT
Definition u4x12.h:196
friend constexpr u4x12 operator*(u16 scalar, u4x12 fp) FL_NOEXCEPT
Definition u4x12.h:96
static FASTLED_FORCE_INLINE u4x12 exp2_fp(u4x12 x) FL_NOEXCEPT
Definition u4x12.h:244
constexpr FASTLED_FORCE_INLINE u4x12 operator>>(int shift) const FL_NOEXCEPT
Definition u4x12.h:82
static constexpr FASTLED_FORCE_INLINE u4x12 max(u4x12 a, u4x12 b) FL_NOEXCEPT
Definition u4x12.h:137
static constexpr int FRAC_BITS
Definition u4x12.h:21
constexpr FASTLED_FORCE_INLINE u4x12 operator+(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:74
static FASTLED_FORCE_INLINE u4x12 pow(u4x12 base, u4x12 exp) FL_NOEXCEPT
Definition u4x12.h:173
constexpr FASTLED_FORCE_INLINE u4x12 operator*(u16 scalar) const FL_NOEXCEPT
Definition u4x12.h:92
constexpr bool operator>=(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:105
constexpr FASTLED_FORCE_INLINE u4x12 operator/(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:69
constexpr u4x12(IntT n) FL_NOEXCEPT
Definition u4x12.h:34
static constexpr FASTLED_FORCE_INLINE int highest_bit(u32 v) FL_NOEXCEPT
Definition u4x12.h:192
constexpr u16 raw() const FL_NOEXCEPT
Definition u4x12.h:58
static FASTLED_FORCE_INLINE u4x12 smoothstep(u4x12 edge0, u4x12 edge1, u4x12 x) FL_NOEXCEPT
Definition u4x12.h:153
static constexpr FASTLED_FORCE_INLINE u4x12 step(u4x12 edge, u4x12 x) FL_NOEXCEPT
Definition u4x12.h:149
static constexpr FASTLED_FORCE_INLINE u4x12 from_raw(u16 raw) FL_NOEXCEPT
Definition u4x12.h:52
constexpr bool operator>(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:103
static FASTLED_FORCE_INLINE u4x12 log2_fp(u4x12 x) FL_NOEXCEPT
Definition u4x12.h:209
constexpr FASTLED_FORCE_INLINE u4x12 operator*(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:64
static constexpr FASTLED_FORCE_INLINE u4x12 floor(u4x12 x) FL_NOEXCEPT
Definition u4x12.h:115
static constexpr FASTLED_FORCE_INLINE u4x12 mod(u4x12 a, u4x12 b) FL_NOEXCEPT
Definition u4x12.h:111
constexpr FASTLED_FORCE_INLINE u4x12 operator-(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:78
constexpr u4x12() FL_NOEXCEPT=default
static constexpr FASTLED_FORCE_INLINE u4x12 rsqrt(u4x12 x) FL_NOEXCEPT
Definition u4x12.h:167
constexpr u16 to_int() const FL_NOEXCEPT
Definition u4x12.h:59
constexpr u4x12(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 u4x12.h:39
static constexpr FASTLED_FORCE_INLINE u4x12 abs(u4x12 x) FL_NOEXCEPT
Definition u4x12.h:128
constexpr bool operator<=(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:104
constexpr bool operator==(u4x12 b) const FL_NOEXCEPT
Definition u4x12.h:106
static constexpr FASTLED_FORCE_INLINE u4x12 sqrt(u4x12 x) FL_NOEXCEPT
Definition u4x12.h:162
constexpr FASTLED_FORCE_INLINE u4x12 operator<<(int shift) const FL_NOEXCEPT
Definition u4x12.h:86
static constexpr FASTLED_FORCE_INLINE u4x12 min(u4x12 a, u4x12 b) FL_NOEXCEPT
Definition u4x12.h:133
constexpr u4x12(u16 raw, RawTag) FL_NOEXCEPT
Definition u4x12.h:50
static constexpr FASTLED_FORCE_INLINE u4x12 ceil(u4x12 x) FL_NOEXCEPT
Definition u4x12.h:119
#define constexpr
Declares that it is possible to evaluate a value at compile time, introduced in C++11.
Definition cpp_compat.h:15
fl::i64 i64
Definition s16x16x4.h:222
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