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