Files
vlibc/src/stdlib/strtod.c
T

1029 lines
23 KiB
C

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <float.h>
#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<hex digits>[.<hex digits>][p<exp>]";
* - decimal floats "[<digits>][.<digits>][(e|E)[<sign>]<digits>]".
*
* 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(""): <math.h> 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);
}