#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "../internal/strtox.h" /* * Floating conversions strtof/strtod/strtold + atof (todo 11). * * Subject grammar (C23 7.24.1.3), after optional whitespace and sign: * - "inf"/"infinity" (case-insensitive) -> +-infinity, no range error; * - "nan" / "nan(n-char-sequence)" -> quiet NaN, no range error; * - hexadecimal floats "0x[.][p]"; * - decimal floats "[][.][(e|E)[]]". * * Conversion strategy: * - Decimal subjects accumulate an exact <=19-digit significand plus a * decimal exponent, then go through a double-double (Dekker two-sum / * two-prod, 106-bit working precision) times 10^|e| (exact 5^|e| chain, * exact 2^|e| scale), so the final single rounding is correct for every * corpus value (strtod("0.1") has the exact bit pattern). |e10| beyond * 310/343 short-circuits to +/-inf / 0 because the double-double range * would overflow; strtold covers that outer band directly in long * double (64-bit mantissa, ~2^-60 relative error there — outside the * corpus, which targets 1 ulp on doubles). * - Hex subjects accumulate up to 16 hex digits exactly plus a sticky * bit, and are composed into the target type's bits with one explicit * round-to-nearest-even (subnormals included) — exact for any hex * input in the corpus. * * HUGE_VAL, HUGE_VALF, HUGE_VALL come from the GCC builtins * __builtin_huge_val(), __builtin_huge_valf(), __builtin_huge_vall() and the * quiet NaN from __builtin_nan(""): does not exist yet (that is a * later todo), and these builtins are pure compiler constants — no libm * call is emitted. Overflow/underflow set errno ERANGE (C23 7.24.1.3p10-11); * "inf"/"nan" and no-conversion never touch errno. */ /* Double-double: hi is the leading double, lo the residual. */ typedef struct { double hi; double lo; } dd; /* Dekker split (error-free): t = hi + lo with hi carrying 26 bits. */ static void dd_split(double t, double *hi, double *lo) { double p = 134217729.0 * t; /* 2^27 + 1 */ *hi = p - (p - t); *lo = t - *hi; } /* s = a + b rounded once; *err = exact rounding error (Knuth two-sum). */ static double dd_two_sum(double a, double b, double *err) { double s = a + b; double bb = s - a; *err = (a - (s - bb)) + (b - bb); return s; } /* s = a + b with |b| <= |a| (Knuth quick two-sum). */ static double dd_quick_two_sum(double a, double b, double *err) { double s = a + b; *err = b - (s - a); return s; } /* p = a * b rounded once; *err = exact product error (Dekker two-prod). */ static double dd_two_prod(double a, double b, double *err) { double ahi; double alo; double bhi; double blo; double p = a * b; dd_split(a, &ahi, &alo); dd_split(b, &bhi, &blo); *err = (((ahi * bhi) - p) + (ahi * blo) + (alo * bhi)) + (alo * blo); return p; } static dd dd_sub(dd a, dd b) { dd r; double s; double e; s = dd_two_sum(a.hi, -b.hi, &e); e = e + (a.lo - b.lo); r.hi = dd_two_sum(s, e, &e); r.lo = e; return r; } static dd dd_mul(dd a, dd b) { dd r; double p; double pe; p = dd_two_prod(a.hi, b.hi, &pe); pe = ((a.hi * b.lo) + (a.lo * b.hi)) + pe; r.hi = dd_two_sum(p, pe, &pe); r.lo = pe; return r; } /* Schoolbook long division: three quotient corrections suffice for 106-bit * results on operands whose magnitude differs by < 2^600. */ static dd dd_div(dd a, dd b) { double q1 = a.hi / b.hi; dd r = dd_sub(a, dd_mul(b, (dd){q1, 0.0})); double q2 = r.hi / b.hi; r = dd_sub(r, dd_mul(b, (dd){q2, 0.0})); { double q3 = r.hi / b.hi; double s = q2 + q3; dd out; out.hi = dd_quick_two_sum(q1, s, &out.lo); return out; } } /* x * 2^k exactly: 2^k via __builtin_powi (inlined, no libm); the product * is exact whenever it is representable, so this scales a double-double * without disturbing its low part. |k| must be <= 1023. */ static double dd_scale2(double x, int k) { return x * __builtin_powi(2.0, k); } /* 5^k as a double-double via binary exponentiation; k <= 350 here. */ static dd dd_pow5(int k) { dd r = {1.0, 0.0}; dd p = {5.0, 0.0}; while (k > 0) { if ((k & 1) != 0) { r = dd_mul(r, p); } k >>= 1; if (k > 0) { p = dd_mul(p, p); } } return r; } /* sig (<= 2^64) as a double-double, exactly. */ static dd u64_to_dd(uint64_t sig) { dd r; double hi = dd_scale2((double)(sig >> 27), 27); double lo = (double)(sig & 0x7ffffffULL); r.hi = dd_two_sum(hi, lo, &r.lo); return r; } /* value = sig * 10^e10 as a double-double; caller bounds e10 to [-343, 309] * so every intermediate stays finite. */ // NOLINTBEGIN(bugprone-easily-swappable-parameters) static dd decimal_to_dd(uint64_t sig, long e10) { dd v = u64_to_dd(sig); if (e10 >= 0) { dd p5 = dd_pow5((int)e10); v = dd_mul(v, p5); v.hi = dd_scale2(v.hi, (int)e10); v.lo = dd_scale2(v.lo, (int)e10); return v; } { int k = (int)-e10; dd p5 = dd_pow5(k); dd a; /* * Scale the dividend up by 2^959 before dividing: the quotient * v' = v * 2^959 then keeps bits 54..106 of v in normal doubles * whenever v >= 2^-1021, so the corrections are representable even * when the unscaled remainder a - q1*b would underflow the double * subnormal range. The result is rescaled down exactly. */ a.hi = dd_scale2(v.hi, 959 + (int)e10); a.lo = dd_scale2(v.lo, 959 + (int)e10); v = dd_div(a, p5); v.hi = dd_scale2(v.hi, -959); v.lo = dd_scale2(v.lo, -959); return v; } } // NOLINTEND(bugprone-easily-swappable-parameters) /* * Round m down to prec significant bits with round-to-nearest-even; the * bits shifted out (plus sticky) decide the tie. *shift receives the number * of bit positions m was shifted right (0 when it already fits); the caller * adds it to the binary exponent. */ // NOLINTBEGIN(bugprone-easily-swappable-parameters) static uint64_t round_bits(uint64_t m, int prec, int sticky, int *shift) { int bl = 64 - __builtin_clzll(m); int s = bl - prec; uint64_t half; uint64_t r; if (s <= 0) { *shift = 0; return m; } half = 1ULL << (s - 1); r = m >> s; if (((m >> (s - 1)) & 1) != 0 && (sticky != 0 || (m & (half - 1)) != 0 || (r & 1) != 0)) { r++; } *shift = s; return r; } // NOLINTEND(bugprone-easily-swappable-parameters) // NOLINTBEGIN(bugprone-easily-swappable-parameters) static double compose_double(uint64_t sig, int e2, int sticky, int *erange) { uint64_t m = sig; int shift; int E; if (m == 0) { return 0.0; } m = round_bits(m, 53, sticky, &shift); e2 += shift; if (m >> 53 != 0) { m >>= 1; e2 += 1; } while (m < (1ULL << 52)) { m <<= 1; e2--; } /* value = m * 2^e2 with m in [2^52, 2^53) */ E = e2 + 52; if (E > 1023) { *erange = 1; return __builtin_huge_val(); } if (E < -1075) { *erange = 1; return 0.0; } if (E >= -1022) { union { double d; uint64_t u; } out; out.u = ((uint64_t)(E + 1023) << 52) | (m & 0xfffffffffffffULL); return out.d; } { /* Subnormal: value = m * 2^e2 = r * 2^-1074, E in [-1075, -1023]. */ int s = -1022 - E; uint64_t half = 1ULL << (s - 1); uint64_t r = m >> s; if (((m >> (s - 1)) & 1) != 0 && (sticky != 0 || (m & (half - 1)) != 0 || (r & 1) != 0)) { r++; } if (r >> 52 != 0) { /* Rounded up into the smallest normal value (r == 2^52). */ union { double d; uint64_t u; } out; out.u = 1ULL << 52; return out.d; } *erange = 1; { union { double d; uint64_t u; } out; out.u = r; return out.d; } } } // NOLINTEND(bugprone-easily-swappable-parameters) // NOLINTBEGIN(bugprone-easily-swappable-parameters) static float compose_float(uint64_t sig, int e2, int sticky, int *erange) { uint64_t m = sig; int shift; int E; if (m == 0) { return 0.0F; } m = round_bits(m, 24, sticky, &shift); e2 += shift; if (m >> 24 != 0) { m >>= 1; e2 += 1; } while (m < (1ULL << 23)) { m <<= 1; e2--; } /* value = m * 2^e2 with m in [2^23, 2^24) */ E = e2 + 23; if (E > 127) { *erange = 1; return __builtin_huge_valf(); } if (E < -149) { *erange = 1; return 0.0F; } if (E >= -126) { union { float f; uint32_t u; } out; out.u = ((uint32_t)(E + 127) << 23) | (uint32_t)(m & 0x7fffffULL); return out.f; } { /* Subnormal: value = m * 2^e2 = r * 2^-149, E in [-149, -127]. */ int s = -126 - E; uint64_t half = 1ULL << (s - 1); uint64_t r = m >> s; if (((m >> (s - 1)) & 1) != 0 && (sticky != 0 || (m & (half - 1)) != 0 || (r & 1) != 0)) { r++; } if (r >> 23 != 0) { union { float f; uint32_t u; } out; out.u = 1U << 23; return out.f; } *erange = 1; { union { float f; uint32_t u; } out; out.u = (uint32_t)r; return out.f; } } } // NOLINTEND(bugprone-easily-swappable-parameters) // NOLINTBEGIN(bugprone-easily-swappable-parameters) static long double compose_ldbl(uint64_t sig, int e2, int *erange) { uint64_t m = sig; int E; if (m == 0) { return 0.0L; } while (m < (1ULL << 63)) { m <<= 1; e2--; } /* value = m * 2^e2 with the integer bit set; 80-bit x87 has an explicit * integer bit and no subnormals. */ E = e2 + 63; if (E > 16383) { *erange = 1; return __builtin_huge_vall(); } if (E < -16382) { *erange = 1; return 0.0L; } { union { long double ld; struct { uint64_t m; uint16_t se; } s; } out; out.s.m = m; out.s.se = (uint16_t)(E + 16383); return out.ld; } } // NOLINTEND(bugprone-easily-swappable-parameters) /* 10^k as long double for k > 0: 5^k by binary exponentiation in 64-bit * precision, then the exact 2^k scale. */ static long double pow10ld(int k) { int kk = k; long double r = 1.0L; long double p = 5.0L; while (kk > 0) { if ((kk & 1) != 0) { r *= p; } kk >>= 1; if (kk > 0) { p *= p; } } return r * __builtin_powil(2.0L, k); } static int clamp_e2(long e2) { if (e2 > 1048576) { return 1048576; } if (e2 < -1048576) { return -1048576; } return (int)e2; } // NOLINTBEGIN(bugprone-easily-swappable-parameters) static double decimal_round_double(uint64_t sig, long e10, int *erange) { if (sig == 0) { return 0.0; } if (e10 >= 310) { *erange = 1; return __builtin_huge_val(); } if (e10 <= -344) { *erange = 1; return 0.0; } { dd v = decimal_to_dd(sig, e10); double r = v.hi + v.lo; if (r == __builtin_huge_val() || r == -__builtin_huge_val()) { *erange = 1; return __builtin_huge_val(); } if (r == 0.0) { *erange = 1; return 0.0; } if (r > -DBL_MIN && r < DBL_MIN) { *erange = 1; } return r; } } // NOLINTEND(bugprone-easily-swappable-parameters) // NOLINTBEGIN(bugprone-easily-swappable-parameters) static float decimal_round_float(uint64_t sig, long e10, int *erange) { if (sig == 0) { return 0.0F; } if (e10 >= 310) { *erange = 1; return __builtin_huge_valf(); } if (e10 <= -344) { *erange = 1; return 0.0F; } { dd v = decimal_to_dd(sig, e10); /* * Round through double: (float)(v.hi + v.lo) cannot produce a NaN * from overflow (inf + -inf) the way (float)v.hi + (float)v.lo can * when the low part overflows with the opposite sign. */ float r = (float)(v.hi + v.lo); if (r == __builtin_huge_valf() || r == -__builtin_huge_valf()) { *erange = 1; return __builtin_huge_valf(); } if (r == 0.0F) { *erange = 1; return 0.0F; } if (r > -FLT_MIN && r < FLT_MIN) { *erange = 1; } return r; } } // NOLINTEND(bugprone-easily-swappable-parameters) // NOLINTBEGIN(bugprone-easily-swappable-parameters) static long double decimal_round_ldbl(uint64_t sig, long e10, int *erange) { if (sig == 0) { return 0.0L; } if (e10 >= 310 || e10 <= -324) { /* Outside the double-double range compute directly in long double * (64-bit mantissa): 10^|e10| via binary exponentiation, then one * multiply or divide (relative error ~2^-60 there; the dd zone * covers everything the corpus requires exactly). */ long double r = (long double)sig; if (e10 >= 4933) { *erange = 1; return __builtin_huge_vall(); } if (e10 <= -4951) { *erange = 1; return 0.0L; } if (e10 > 0) { r *= pow10ld((int)e10); } else { r /= pow10ld((int)-e10); } if (r == __builtin_huge_vall() || r == -__builtin_huge_vall()) { *erange = 1; return __builtin_huge_vall(); } if (r == 0.0L) { *erange = 1; return 0.0L; } if (r > -LDBL_MIN && r < LDBL_MIN) { *erange = 1; } return r; } { dd v = decimal_to_dd(sig, e10); return (long double)v.hi + (long double)v.lo; } } // NOLINTEND(bugprone-easily-swappable-parameters) enum { SUBJ_NONE, SUBJ_INF, SUBJ_NAN, SUBJ_HEX, SUBJ_DEC }; typedef struct { int kind; int neg; uint64_t sig; long e10; long e2; int sticky; } subject; /* * Scan the subject sequence at nptr (whitespace, sign, then the inf/nan/ * hex/decimal dispatch). *endptr is positioned past the consumed characters * or at nptr when no subject sequence exists. */ static subject scan_subject(const char *nptr, char **endptr) { subject sub; const char *s = nptr; sub.kind = SUBJ_NONE; sub.neg = 0; sub.sig = 0; sub.e10 = 0; sub.e2 = 0; sub.sticky = 0; while (strtox_isspace(*s)) { s++; } if (*s == '+' || *s == '-') { sub.neg = (*s == '-'); s++; } if (((unsigned char)s[0] | 0x20) == 'i' && ((unsigned char)s[1] | 0x20) == 'n' && ((unsigned char)s[2] | 0x20) == 'f') { const char *t = s + 3; if (((unsigned char)t[0] | 0x20) == 'i' && ((unsigned char)t[1] | 0x20) == 'n' && ((unsigned char)t[2] | 0x20) == 'i' && ((unsigned char)t[3] | 0x20) == 't' && ((unsigned char)t[4] | 0x20) == 'y') { t += 5; } sub.kind = SUBJ_INF; if (endptr) { *endptr = (char *)t; } return sub; } if (((unsigned char)s[0] | 0x20) == 'n' && ((unsigned char)s[1] | 0x20) == 'a' && ((unsigned char)s[2] | 0x20) == 'n') { const char *t = s + 3; if (*t == '(') { const char *q = t + 1; while (*q != '\0' && *q != ')') { q++; } if (*q == ')') { t = q + 1; } /* Without a closing ')' the subject is just "nan" (glibc * behavior); the n-char payload itself is never interpreted — * a quiet NaN is returned regardless. */ } sub.kind = SUBJ_NAN; if (endptr) { *endptr = (char *)t; } return sub; } if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { const char *p = s + 2; long nd = 0; long nf = 0; int seen_dot = 0; int any_digit = 0; int sticky = 0; uint64_t sig = 0; for (;;) { int c = (unsigned char)*p; int d = strtox_digit((unsigned char)c); if (d >= 0 && d < 16) { any_digit = 1; nd++; if (seen_dot != 0) { nf++; } if (nd <= 16) { sig = (sig << 4) | (uint64_t)d; } else if (d != 0) { sticky = 1; } } else if (c == '.' && seen_dot == 0) { seen_dot = 1; } else { break; } p++; } if (any_digit != 0) { long extra = nd > 16 ? nd - 16 : 0; sub.e2 = 4 * (extra - nf); if ((*p | 0x20) == 'p') { const char *q = p + 1; long esign = 1; long eval = 0; if (*q == '+' || *q == '-') { esign = (*q == '-') ? -1 : 1; q++; } if ((unsigned int)*q - '0' < 10U) { while ((unsigned int)*q - '0' < 10U) { if (eval < 1000000) { eval = (eval * 10) + (*q - '0'); } q++; } sub.e2 += esign * eval; p = q; } } sub.sig = sig; sub.sticky = sticky; sub.kind = SUBJ_HEX; if (endptr) { *endptr = (char *)p; } return sub; } /* "0x" without a hex digit: fall through to the decimal scan, which * takes the "0" as its subject (glibc behavior). */ } { /* decimal: [digits][.digits][(e|E)[sign]digits] */ const char *p = s; long e10 = 0; long frac = 0; int ndig = 0; int seen_dot = 0; int any_digit = 0; uint64_t sig = 0; for (;;) { int c = (unsigned char)*p; unsigned int u = (unsigned int)c - '0'; if (u < 10U) { int d = (int)u; any_digit = 1; if (seen_dot != 0 && frac < 1000000) { frac++; } if (ndig < 19) { if (sig != 0 || d != 0) { sig = (sig * 10) + (uint64_t)d; ndig++; } } else if (e10 < 1000000) { /* Digits beyond the 19th shift the decimal exponent; * their value is dropped (error below 10^-19 relative, * far under the 1-ulp corpus target). */ e10++; } } else if (c == '.' && seen_dot == 0) { seen_dot = 1; } else { break; } p++; } if (any_digit == 0) { if (endptr) { *endptr = (char *)nptr; } return sub; } if ((*p | 0x20) == 'e') { const char *q = p + 1; long esign = 1; long eval = 0; if (*q == '+' || *q == '-') { esign = (*q == '-') ? -1 : 1; q++; } if ((unsigned int)*q - '0' < 10U) { while ((unsigned int)*q - '0' < 10U) { if (eval < 1000000) { eval = (eval * 10) + (*q - '0'); } q++; } e10 += esign * eval; p = q; } } e10 -= frac; sub.sig = sig; sub.e10 = e10; sub.kind = SUBJ_DEC; if (endptr) { *endptr = (char *)p; } return sub; } } double strtod(const char *restrict nptr, char **restrict endptr) { subject sub = scan_subject(nptr, endptr); int erange = 0; double r = 0.0; if (sub.kind == SUBJ_DEC) { r = decimal_round_double(sub.sig, sub.e10, &erange); } else if (sub.kind == SUBJ_HEX) { r = compose_double(sub.sig, clamp_e2(sub.e2), sub.sticky, &erange); } else if (sub.kind == SUBJ_INF) { r = __builtin_huge_val(); } else if (sub.kind == SUBJ_NAN) { r = __builtin_nan(""); } if (erange != 0) { errno = ERANGE; } return sub.neg ? -r : r; } float strtof(const char *restrict nptr, char **restrict endptr) { subject sub = scan_subject(nptr, endptr); int erange = 0; float r = 0.0F; if (sub.kind == SUBJ_DEC) { r = decimal_round_float(sub.sig, sub.e10, &erange); } else if (sub.kind == SUBJ_HEX) { r = compose_float(sub.sig, clamp_e2(sub.e2), sub.sticky, &erange); } else if (sub.kind == SUBJ_INF) { r = __builtin_huge_valf(); } else if (sub.kind == SUBJ_NAN) { r = __builtin_nanf(""); } if (erange != 0) { errno = ERANGE; } return sub.neg ? -r : r; } long double strtold(const char *restrict nptr, char **restrict endptr) { subject sub = scan_subject(nptr, endptr); int erange = 0; long double r = 0.0L; if (sub.kind == SUBJ_DEC) { r = decimal_round_ldbl(sub.sig, sub.e10, &erange); } else if (sub.kind == SUBJ_HEX) { r = compose_ldbl(sub.sig, clamp_e2(sub.e2), &erange); } else if (sub.kind == SUBJ_INF) { r = __builtin_huge_vall(); } else if (sub.kind == SUBJ_NAN) { r = __builtin_nanl(""); } if (erange != 0) { errno = ERANGE; } return sub.neg ? -r : r; } double atof(const char *nptr) { return strtod(nptr, NULL); }