FastLED 3.9.15
Loading...
Searching...
No Matches

◆ ieee754_format_decimal()

fl::string fl::ieee754_format_decimal ( u32 bits,
int precision = 6 )

Format IEEE 754 single-precision bits as decimal text.

Emits a fixed-point decimal representation with precision digits after the point. Negative values emit a leading -. Inf / NaN bit patterns emit "inf" / "-inf" / "nan" (JSON does not allow these, but the codec stays defensive).

No FP arithmetic is performed.

Parameters
bitsIEEE 754 single-precision bit pattern.
precisionDigits after the decimal point. Negative values collapse to 0 (integer form). Clamped to [0, 9].
Returns
Heap-allocated fl::string with the decimal text.

Definition at line 439 of file ieee754_string.cpp.hpp.

439 {
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}
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
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
Base definition for an LED controller.
Definition crgb.hpp:179

References append_u64_decimal(), FL_NOEXCEPT, and utoa64().

Referenced by fl::SerializerVisitor::accept(), and fl::SerializerVisitor::accept().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: