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

◆ ieee754_parse_decimal()

u32 fl::ieee754_parse_decimal ( const char * s,
fl::size len,
fl::size * consumed = nullptr )

Parse a decimal floating-point number into IEEE 754 single-precision bits.

Accepts the usual decimal syntax: optional sign, integer digits, optional fractional part, optional e/E exponent. Returns the IEEE 754 single-precision bit pattern of the parsed value. Caller bit-casts to float via fl::bit_cast<float>(bits) if needed.

No FP arithmetic is performed. The implementation uses only integer shifts, masks, and 32x32->64 widening multiplies – libgcc soft-FP helpers (__aeabi_d*, __aeabi_f*) are never reached.

Failure mode: malformed inputs return 0x00000000 (positive zero) and set *consumed to 0. Inputs that overflow IEEE 754 single-precision range (>~3.4e38) clamp to +inf / -inf bit patterns. Inputs that underflow (>~1.4e-45) clamp to +/-0.

Parameters
sPointer to the decimal text.
lenLength of the text in bytes (no NUL terminator assumed).
consumedIf non-null, receives the number of bytes consumed. Useful for caller-side bookkeeping (e.g. JSON tokenizer).
Returns
IEEE 754 single-precision bit pattern.

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

261 {
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}
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
void fail(const char *msg, const char *file, int line, bool isFatal) FL_NOEXCEPT
Helper for FAIL macros.
fl::u64 u64
Definition s16x16x4.h:221

References FL_NOEXCEPT.

Referenced by fl::anonymous_namespace{json.cpp.hpp}::JsonBuilder::on_token(), fl::anonymous_namespace{json.cpp.hpp}::JsonBuilder::parse_float_array(), and fl::anonymous_namespace{json.cpp.hpp}::JsonTokenizer::scan_array_lookahead().

+ Here is the caller graph for this function: