FastLED 3.9.15
Loading...
Searching...
No Matches
ieee754_string.cpp.hpp
Go to the documentation of this file.
1
2// fl::ieee754_string -- integer-only IEEE 754 single-precision codec.
3//
4// See ``src/fl/stl/ieee754_string.h`` for the design rationale and the
5// embedding into FastLED #3022 / #3029.
7
9
10#include "fl/stl/string.h"
11#include "fl/stl/charconv.h"
12
13namespace fl {
14
15namespace {
16
17// ------------------------------------------------------------------ //
18// pow10 lookup table. Generated by ci/tools/gen_pow10_table.py.
19// Each entry encodes 10**k ~= mantissa * 2**binary_exp, mantissa
20// normalized so the top bit (bit 63) is set.
21// ------------------------------------------------------------------ //
22
23static constexpr int kPow10KMin = -64;
24static constexpr int kPow10KMax = 38;
25static constexpr fl::size kPow10Count = 103;
26
27static constexpr fl::u64 kPow10Mant[kPow10Count] = {
28 0xA87FEA27A539E9A5u, // 10**-64
29 0xD29FE4B18E88640Fu, // 10**-63
30 0x83A3EEEEF9153E89u, // 10**-62
31 0xA48CEAAAB75A8E2Bu, // 10**-61
32 0xCDB02555653131B6u, // 10**-60
33 0x808E17555F3EBF12u, // 10**-59
34 0xA0B19D2AB70E6ED6u, // 10**-58
35 0xC8DE047564D20A8Cu, // 10**-57
36 0xFB158592BE068D2Fu, // 10**-56
37 0x9CED737BB6C4183Du, // 10**-55
38 0xC428D05AA4751E4Du, // 10**-54
39 0xF53304714D9265E0u, // 10**-53
40 0x993FE2C6D07B7FACu, // 10**-52
41 0xBF8FDB78849A5F97u, // 10**-51
42 0xEF73D256A5C0F77Du, // 10**-50
43 0x95A8637627989AAEu, // 10**-49
44 0xBB127C53B17EC159u, // 10**-48
45 0xE9D71B689DDE71B0u, // 10**-47
46 0x9226712162AB070Eu, // 10**-46
47 0xB6B00D69BB55C8D1u, // 10**-45
48 0xE45C10C42A2B3B06u, // 10**-44
49 0x8EB98A7A9A5B04E3u, // 10**-43
50 0xB267ED1940F1C61Cu, // 10**-42
51 0xDF01E85F912E37A3u, // 10**-41
52 0x8B61313BBABCE2C6u, // 10**-40
53 0xAE397D8AA96C1B78u, // 10**-39
54 0xD9C7DCED53C72256u, // 10**-38
55 0x881CEA14545C7575u, // 10**-37
56 0xAA242499697392D3u, // 10**-36
57 0xD4AD2DBFC3D07788u, // 10**-35
58 0x84EC3C97DA624AB5u, // 10**-34
59 0xA6274BBDD0FADD62u, // 10**-33
60 0xCFB11EAD453994BAu, // 10**-32
61 0x81CEB32C4B43FCF5u, // 10**-31
62 0xA2425FF75E14FC32u, // 10**-30
63 0xCAD2F7F5359A3B3Eu, // 10**-29
64 0xFD87B5F28300CA0Eu, // 10**-28
65 0x9E74D1B791E07E48u, // 10**-27
66 0xC612062576589DDBu, // 10**-26
67 0xF79687AED3EEC551u, // 10**-25
68 0x9ABE14CD44753B53u, // 10**-24
69 0xC16D9A0095928A27u, // 10**-23
70 0xF1C90080BAF72CB1u, // 10**-22
71 0x971DA05074DA7BEFu, // 10**-21
72 0xBCE5086492111AEBu, // 10**-20
73 0xEC1E4A7DB69561A5u, // 10**-19
74 0x9392EE8E921D5D07u, // 10**-18
75 0xB877AA3236A4B449u, // 10**-17
76 0xE69594BEC44DE15Bu, // 10**-16
77 0x901D7CF73AB0ACD9u, // 10**-15
78 0xB424DC35095CD80Fu, // 10**-14
79 0xE12E13424BB40E13u, // 10**-13
80 0x8CBCCC096F5088CCu, // 10**-12
81 0xAFEBFF0BCB24AAFFu, // 10**-11
82 0xDBE6FECEBDEDD5BFu, // 10**-10
83 0x89705F4136B4A597u, // 10**-9
84 0xABCC77118461CEFDu, // 10**-8
85 0xD6BF94D5E57A42BCu, // 10**-7
86 0x8637BD05AF6C69B6u, // 10**-6
87 0xA7C5AC471B478423u, // 10**-5
88 0xD1B71758E219652Cu, // 10**-4
89 0x83126E978D4FDF3Bu, // 10**-3
90 0xA3D70A3D70A3D70Au, // 10**-2
91 0xCCCCCCCCCCCCCCCDu, // 10**-1
92 0x8000000000000000u, // 10**0
93 0xA000000000000000u, // 10**1
94 0xC800000000000000u, // 10**2
95 0xFA00000000000000u, // 10**3
96 0x9C40000000000000u, // 10**4
97 0xC350000000000000u, // 10**5
98 0xF424000000000000u, // 10**6
99 0x9896800000000000u, // 10**7
100 0xBEBC200000000000u, // 10**8
101 0xEE6B280000000000u, // 10**9
102 0x9502F90000000000u, // 10**10
103 0xBA43B74000000000u, // 10**11
104 0xE8D4A51000000000u, // 10**12
105 0x9184E72A00000000u, // 10**13
106 0xB5E620F480000000u, // 10**14
107 0xE35FA931A0000000u, // 10**15
108 0x8E1BC9BF04000000u, // 10**16
109 0xB1A2BC2EC5000000u, // 10**17
110 0xDE0B6B3A76400000u, // 10**18
111 0x8AC7230489E80000u, // 10**19
112 0xAD78EBC5AC620000u, // 10**20
113 0xD8D726B7177A8000u, // 10**21
114 0x878678326EAC9000u, // 10**22
115 0xA968163F0A57B400u, // 10**23
116 0xD3C21BCECCEDA100u, // 10**24
117 0x84595161401484A0u, // 10**25
118 0xA56FA5B99019A5C8u, // 10**26
119 0xCECB8F27F4200F3Au, // 10**27
120 0x813F3978F8940984u, // 10**28
121 0xA18F07D736B90BE5u, // 10**29
122 0xC9F2C9CD04674EDFu, // 10**30
123 0xFC6F7C4045812296u, // 10**31
124 0x9DC5ADA82B70B59Eu, // 10**32
125 0xC5371912364CE305u, // 10**33
126 0xF684DF56C3E01BC7u, // 10**34
127 0x9A130B963A6C115Cu, // 10**35
128 0xC097CE7BC90715B3u, // 10**36
129 0xF0BDC21ABB48DB20u, // 10**37
130 0x96769950B50D88F4u, // 10**38
131};
132
133static constexpr fl::i16 kPow10BExp[kPow10Count] = {
134 -276, // 10**-64
135 -273, // 10**-63
136 -269, // 10**-62
137 -266, // 10**-61
138 -263, // 10**-60
139 -259, // 10**-59
140 -256, // 10**-58
141 -253, // 10**-57
142 -250, // 10**-56
143 -246, // 10**-55
144 -243, // 10**-54
145 -240, // 10**-53
146 -236, // 10**-52
147 -233, // 10**-51
148 -230, // 10**-50
149 -226, // 10**-49
150 -223, // 10**-48
151 -220, // 10**-47
152 -216, // 10**-46
153 -213, // 10**-45
154 -210, // 10**-44
155 -206, // 10**-43
156 -203, // 10**-42
157 -200, // 10**-41
158 -196, // 10**-40
159 -193, // 10**-39
160 -190, // 10**-38
161 -186, // 10**-37
162 -183, // 10**-36
163 -180, // 10**-35
164 -176, // 10**-34
165 -173, // 10**-33
166 -170, // 10**-32
167 -166, // 10**-31
168 -163, // 10**-30
169 -160, // 10**-29
170 -157, // 10**-28
171 -153, // 10**-27
172 -150, // 10**-26
173 -147, // 10**-25
174 -143, // 10**-24
175 -140, // 10**-23
176 -137, // 10**-22
177 -133, // 10**-21
178 -130, // 10**-20
179 -127, // 10**-19
180 -123, // 10**-18
181 -120, // 10**-17
182 -117, // 10**-16
183 -113, // 10**-15
184 -110, // 10**-14
185 -107, // 10**-13
186 -103, // 10**-12
187 -100, // 10**-11
188 -97, // 10**-10
189 -93, // 10**-9
190 -90, // 10**-8
191 -87, // 10**-7
192 -83, // 10**-6
193 -80, // 10**-5
194 -77, // 10**-4
195 -73, // 10**-3
196 -70, // 10**-2
197 -67, // 10**-1
198 -63, // 10**0
199 -60, // 10**1
200 -57, // 10**2
201 -54, // 10**3
202 -50, // 10**4
203 -47, // 10**5
204 -44, // 10**6
205 -40, // 10**7
206 -37, // 10**8
207 -34, // 10**9
208 -30, // 10**10
209 -27, // 10**11
210 -24, // 10**12
211 -20, // 10**13
212 -17, // 10**14
213 -14, // 10**15
214 -10, // 10**16
215 -7, // 10**17
216 -4, // 10**18
217 0, // 10**19
218 3, // 10**20
219 6, // 10**21
220 10, // 10**22
221 13, // 10**23
222 16, // 10**24
223 20, // 10**25
224 23, // 10**26
225 26, // 10**27
226 30, // 10**28
227 33, // 10**29
228 36, // 10**30
229 39, // 10**31
230 43, // 10**32
231 46, // 10**33
232 49, // 10**34
233 53, // 10**35
234 56, // 10**36
235 59, // 10**37
236 63, // 10**38
237};
238
239// Top 64 bits of the 128-bit product of two u64 values, computed via four
240// 32x32->64 widening multiplies. Portable to every Cortex-M target -- uses
241// only integer ops, never the libgcc soft-FP cascade.
243 const fl::u64 ah = a >> 32;
244 const fl::u64 al = a & 0xFFFFFFFFull;
245 const fl::u64 bh = b >> 32;
246 const fl::u64 bl = b & 0xFFFFFFFFull;
247 const fl::u64 ll = al * bl;
248 const fl::u64 hl = ah * bl;
249 const fl::u64 lh = al * bh;
250 const fl::u64 hh = ah * bh;
251 const fl::u64 mid = (ll >> 32) + (hl & 0xFFFFFFFFull) + (lh & 0xFFFFFFFFull);
252 return hh + (hl >> 32) + (lh >> 32) + (mid >> 32);
253}
254
255constexpr fl::u32 kSignBitMask = 0x80000000u;
256constexpr fl::u32 kInfBitsPos = 0x7F800000u;
257constexpr fl::u32 kInfBitsNeg = 0xFF800000u;
258
259} // namespace
260
261u32 ieee754_parse_decimal(const char* s, fl::size len, fl::size* consumed) FL_NOEXCEPT {
262 fl::size i = 0;
263
264 auto fail = [&]() -> u32 {
265 if (consumed) *consumed = 0;
266 return 0;
267 };
268
269 // Skip leading whitespace.
270 while (i < len && (s[i] == ' ' || s[i] == '\t' || s[i] == '\n' ||
271 s[i] == '\r' || s[i] == '\f' || s[i] == '\v')) {
272 ++i;
273 }
274
275 // Sign.
276 u32 sign_bit = 0;
277 if (i < len && s[i] == '-') { sign_bit = kSignBitMask; ++i; }
278 else if (i < len && s[i] == '+') { ++i; }
279
280 // Mantissa: integer + optional fractional. Cap at 19 significant decimal
281 // digits -- that is the max u64 can hold (10**19 < 2**64). Beyond that we
282 // drop digits but keep tracking the decimal exponent so values like
283 // "100000000000000000000" (1e20) still round to the right magnitude.
284 fl::u64 mantissa = 0;
285 int decimal_exp = 0;
286 int significant_digits = 0;
287 bool seen_decimal = false;
288 bool seen_digit = false;
289 constexpr int kMaxSignificantDigits = 19;
290
291 while (i < len) {
292 const char c = s[i];
293 if (c >= '0' && c <= '9') {
294 seen_digit = true;
295 if (significant_digits < kMaxSignificantDigits) {
296 // u64 max is ~1.8e19, mantissa * 10 + 9 stays below for 18 digits.
297 mantissa = mantissa * 10u + static_cast<fl::u64>(c - '0');
298 if (significant_digits != 0 || c != '0') {
299 ++significant_digits;
300 }
301 if (seen_decimal) {
302 --decimal_exp;
303 }
304 } else {
305 // Out of mantissa precision -- track magnitude via decimal_exp only.
306 if (!seen_decimal) {
307 ++decimal_exp;
308 }
309 }
310 ++i;
311 } else if (c == '.' && !seen_decimal) {
312 seen_decimal = true;
313 ++i;
314 } else {
315 break;
316 }
317 }
318
319 // Reject inputs that contained no digit (e.g. "", ".", "-.", "+.", "e5").
320 // `i == mantissa_start` only catches "" and "-"/"+" -- a lone "." passes
321 // the start-position guard because the loop consumed it.
322 if (!seen_digit) {
323 return fail();
324 }
325
326 // Optional exponent.
327 if (i < len && (s[i] == 'e' || s[i] == 'E')) {
328 const fl::size exp_start = i + 1;
329 fl::size j = exp_start;
330 int exp_sign = 1;
331 if (j < len && s[j] == '-') { exp_sign = -1; ++j; }
332 else if (j < len && s[j] == '+') { ++j; }
333 int exp_val = 0;
334 const fl::size digits_begin = j;
335 while (j < len && s[j] >= '0' && s[j] <= '9') {
336 if (exp_val < 10000) {
337 exp_val = exp_val * 10 + (s[j] - '0');
338 }
339 ++j;
340 }
341 if (j != digits_begin) {
342 decimal_exp += exp_sign * exp_val;
343 i = j;
344 }
345 // else: lone 'e' with no digits -- treat as end-of-number, leave i at 'e'.
346 }
347
348 if (consumed) *consumed = i;
349
350 if (mantissa == 0) {
351 return sign_bit;
352 }
353
354 // Normalize mantissa into [2**63, 2**64) so the top bit is set. The
355 // shift count becomes part of the binary exponent.
356 int mantissa_shift = 0;
357 while ((mantissa & 0x8000000000000000ull) == 0) {
358 mantissa <<= 1;
359 ++mantissa_shift;
360 }
361 // After normalization: original_mantissa = mantissa * 2**(-mantissa_shift).
362
363 // Out-of-table decimal exponent: under/overflow before we even multiply.
364 // The table floor (kPow10KMin) is chosen so the parser's raw 19-digit
365 // mantissa still covers IEEE 754 single-precision range -- see
366 // ci/tools/gen_pow10_table.py.
367 if (decimal_exp < kPow10KMin) {
368 return sign_bit; // +/- 0
369 }
370 if (decimal_exp > kPow10KMax) {
371 return sign_bit | kInfBitsPos; // +/- inf
372 }
373
374 const fl::size idx = static_cast<fl::size>(decimal_exp - kPow10KMin);
375 const fl::u64 pow_mant = kPow10Mant[idx];
376 const int pow_exp = kPow10BExp[idx];
377
378 fl::u64 product_hi = mul_hi_u64(mantissa, pow_mant);
379 // The full product (mantissa * pow_mant) is in [2**126, 2**128). Its top
380 // 64 bits (product_hi) are in [2**62, 2**64). If bit 63 isn't set,
381 // shift left by 1 to renormalize and consume one binary-exponent unit.
382 int extra_shift = 0;
383 if ((product_hi & 0x8000000000000000ull) == 0) {
384 product_hi <<= 1;
385 extra_shift = 1;
386 }
387
388 // Binary exponent of the renormalized mantissa: combines the input
389 // mantissa shift, the pow10 binary exponent, the +64 for taking the
390 // top half of the 128-bit product, and the renormalization shift.
391 int bin_exp = -mantissa_shift + pow_exp + 64 - extra_shift;
392
393 // IEEE 754 single-precision biased exponent: the implicit-1 form is
394 // product_hi / 2**63, so the unbiased binary exponent is (bin_exp + 63).
395 int biased_exp = bin_exp + 63 + 127;
396
397 if (biased_exp >= 0xFF) {
398 return sign_bit | kInfBitsPos; // +/- inf
399 }
400 if (biased_exp <= 0) {
401 // Subnormal / underflow. For embedded-grade precision we collapse
402 // to +/- 0 -- the JSON-RPC use case never needs denormals, and
403 // avoiding the subnormal path keeps the bit-twiddling logic compact.
404 return sign_bit;
405 }
406
407 // Extract the 23-bit mantissa from the top 64 bits with round-half-to-even.
408 // product_hi bits [62:40] -> ieee_mant bits [22:0]
409 // product_hi bit [39] -> rounding bit
410 // product_hi bits [38:0] -> sticky bits
411 fl::u32 ieee_mant = static_cast<fl::u32>((product_hi >> 40) & 0x7FFFFFu);
412 const fl::u64 round_bit = (product_hi >> 39) & 1u;
413 const fl::u64 sticky = (product_hi & 0x7FFFFFFFFFull); // bits [38:0]
414 if (round_bit && (sticky != 0 || (ieee_mant & 1u))) {
415 ++ieee_mant;
416 if (ieee_mant == (1u << 23)) {
417 // Mantissa overflow on rounding -- re-renormalize.
418 ieee_mant = 0;
419 ++biased_exp;
420 if (biased_exp >= 0xFF) {
421 return sign_bit | kInfBitsPos;
422 }
423 }
424 }
425
426 return sign_bit | (static_cast<fl::u32>(biased_exp) << 23) | ieee_mant;
427}
428
429// Append the decimal digits of `value` to `out`. Reuses the integer-only
430// `fl::utoa64` helper from `fl/stl/charconv.h` -- no FP arithmetic.
432 char buf[24];
433 const int n = fl::utoa64(value, buf, 10);
434 for (int i = 0; i < n; ++i) {
435 out += fl::string(1, buf[i]);
436 }
437}
438
440 if (precision < 0) precision = 0;
441 if (precision > 9) precision = 9;
442
443 const bool neg = (bits >> 31) & 1u;
444 const int biased_exp = static_cast<int>((bits >> 23) & 0xFFu);
445 const u32 mant_bits = bits & 0x7FFFFFu;
446
447 // Inf / NaN.
448 if (biased_exp == 0xFF) {
449 if (mant_bits != 0) return fl::string("nan");
450 return neg ? fl::string("-inf") : fl::string("inf");
451 }
452
453 fl::string s;
454
455 auto append_zero_with_precision = [&]() {
456 s += "0";
457 if (precision > 0) {
458 s += ".";
459 for (int i = 0; i < precision; ++i) s += "0";
460 }
461 };
462
463 // +/- 0 (and any subnormal -- those collapse to zero per the parser's
464 // contract, so the serializer matches).
465 if (biased_exp == 0) {
466 if (neg) s += "-";
467 append_zero_with_precision();
468 return s;
469 }
470
471 // Normal number: value = mantissa_full * 2**bin_exp.
472 // mantissa_full carries the implicit leading 1.
473 const u32 mantissa_full = mant_bits | 0x800000u; // 24 bits
474 const int bin_exp_raw = biased_exp - 127 - 23; // signed
475
476 // Lift to a 64-bit normalized representation so we can multiply against
477 // the shared pow10 table. mantissa_full's bit 23 is set, so shifting left by 40
478 // puts that set bit in position 63.
479 const u64 mant64 = static_cast<u64>(mantissa_full) << 40;
480 const int bin_exp = bin_exp_raw - 40;
481
482 // Look up the normalized representation of 10**precision.
483 const fl::size idx = static_cast<fl::size>(precision - kPow10KMin);
484 const u64 pow_mant = kPow10Mant[idx];
485 const int pow_exp = kPow10BExp[idx];
486
487 // Multiply via the same widening helper the parser uses.
488 u64 scaled_hi = mul_hi_u64(mant64, pow_mant);
489 int scaled_bin_exp = bin_exp + pow_exp + 64;
490 if ((scaled_hi & 0x8000000000000000ull) == 0) {
491 scaled_hi <<= 1;
492 --scaled_bin_exp;
493 }
494
495 // Convert (scaled_hi, scaled_bin_exp) to a u64 integer count of
496 // `precision` decimal places. Overflow on the way up clamps to +/- inf;
497 // underflow on the way down collapses to +/- 0 -- same contract as the
498 // parser at the edges.
499 u64 scaled_int;
500 if (scaled_bin_exp >= 0) {
501 if (scaled_bin_exp > 0) {
502 // (scaled_hi << scaled_bin_exp) drops the top bit -- magnitude
503 // beyond what u64 can carry. Treat as overflow.
504 return neg ? fl::string("-inf") : fl::string("inf");
505 }
506 scaled_int = scaled_hi;
507 } else {
508 const int shift = -scaled_bin_exp;
509 if (shift >= 64) {
510 scaled_int = 0;
511 } else {
512 // Round-half-to-even on the shifted-off low bits.
513 const u64 mask = (shift == 64) ? ~0ull : ((1ull << shift) - 1ull);
514 const u64 low = scaled_hi & mask;
515 scaled_int = scaled_hi >> shift;
516 const u64 half = (shift == 0) ? 0 : (1ull << (shift - 1));
517 if (low > half || (low == half && (scaled_int & 1ull))) {
518 ++scaled_int;
519 }
520 }
521 }
522
523 // For the integer / fractional split we need the EXACT integer value of
524 // 10**precision (not the normalized form). precision is in [0, 9] so this
525 // fits in u32 easily -- keep it inline to avoid pulling another table.
526 static constexpr u32 kPow10IntExact[] = {
527 1u, 10u, 100u, 1000u,
528 10000u, 100000u, 1000000u, 10000000u,
529 100000000u, 1000000000u,
530 };
531 const u64 pow10_exact = kPow10IntExact[precision];
532
533 const u64 int_part = scaled_int / pow10_exact;
534 const u64 frac_part = scaled_int % pow10_exact;
535
536 if (neg && (int_part != 0 || frac_part != 0)) {
537 s += "-";
538 }
539 append_u64_decimal(s, int_part);
540
541 if (precision > 0) {
542 s += ".";
543 // Format `frac_part` as exactly `precision` digits, left-padded with
544 // zeros. utoa64 emits the minimum-digit form, so count its length and
545 // prepend the difference.
546 char buf[24];
547 const int n = fl::utoa64(frac_part, buf, 10);
548 for (int i = n; i < precision; ++i) s += "0";
549 for (int i = 0; i < n; ++i) s += fl::string(1, buf[i]);
550 }
551
552 return s;
553}
554
555} // namespace fl
static constexpr fl::u64 kPow10Mant[kPow10Count]
static constexpr fl::i16 kPow10BExp[kPow10Count]
fl::u64 mul_hi_u64(fl::u64 a, fl::u64 b) FL_NOEXCEPT
fl::string ieee754_format_decimal(u32 bits, int precision) FL_NOEXCEPT
Format IEEE 754 single-precision bits as decimal text.
constexpr int type_rank< T >::value
int utoa64(u64 value, char *sp, int radix)
Convert unsigned 64-bit integer to string buffer in given radix.
static void append_u64_decimal(fl::string &out, fl::u64 value) FL_NOEXCEPT
fl::u64 u64
Definition s16x16x4.h:221
u32 ieee754_parse_decimal(const char *s, fl::size len, fl::size *consumed) FL_NOEXCEPT
Parse a decimal floating-point number into IEEE 754 single-precision bits.
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT