#ifdef HAVE_CONFIG_H #include #endif #include /* * Split x into a fraction f in [1/2, 1) (or 0) and an integer exponent * *exp with x == f * 2^(*exp), all three precisions (C23 7.12.6.4). * frexp(±0) returns x with *exp 0; frexp(±Inf) and frexp(NaN) return x * with *exp 0 (the glibc behavior the tests pin down; the C standard * leaves the Inf/NaN exponent unspecified). The fraction keeps x's sign, * so frexp(-6.0) is -0.75 with *exp 3 and frexp(0x1p-1074) is 0.5 with * *exp -1073. * * Each precision works from the same unified view used across the * src/math/ slices (see math_impl.h): a finite value is m * 2^p with m the * integer significand carrying the explicit integer bit. The fraction is * m * 2^-w (w = 53/24/64 significand bits), which sits in [1/2, 1), so * *exp is p + w and the result keeps the significand's low bits — for a * normal input the fraction field is simply unchanged and only the * exponent field is rewritten. */ /* * The double format: bit 63 the sign, bits 62..52 the exponent biased by * 1023, bits 51..0 the fraction, 53-bit significand. A normal input has * p = ef - 1075, so *exp = ef - 1022 and the fraction field is the input's * own 52-bit fraction (the implicit bit stays put as the leading 1 of the * fraction's [1/2, 1) significand). A subnormal input with fraction msb * at position s (0..51) is value frac * 2^-1074; shifting frac up to the * implicit-bit position makes m = frac << (52 - s), hence *exp = s - 1073 * and fraction field m - 2^52. */ double frexp(double x, int *exp) { unsigned long long bits; unsigned long long sign; unsigned long long ef; unsigned long long frac; unsigned long long m; int s; __builtin_memcpy(&bits, &x, sizeof bits); sign = bits & (1ULL << 63); ef = (bits >> 52) & 0x7ff; frac = bits & 0xFFFFFFFFFFFFFULL; if (ef == 0x7ff) { *exp = 0; return x; /* ±Inf and NaN */ } if (ef == 0) { if (frac == 0) { *exp = 0; return x; /* ±0 */ } s = 63 - __builtin_clzll(frac); m = frac << (52 - s); *exp = s - 1073; bits = sign | (0x3feULL << 52) | (m - (1ULL << 52)); __builtin_memcpy(&x, &bits, sizeof x); return x; } *exp = (int)ef - 1022; bits = sign | (0x3feULL << 52) | frac; __builtin_memcpy(&x, &bits, sizeof x); return x; } /* * The float format: bit 31 the sign, bits 30..23 the exponent biased by * 127, bits 22..0 the fraction, 24-bit significand. Normal *exp = * ef - 126; a subnormal input with fraction msb at s (0..22) normalizes to * m = frac << (23 - s) and has *exp = s - 148. */ float frexpf(float x, int *exp) { unsigned int bits; unsigned int sign; unsigned int ef; unsigned int frac; unsigned int m; int s; __builtin_memcpy(&bits, &x, sizeof bits); sign = bits & (1U << 31); ef = (bits >> 23) & 0xff; frac = bits & 0x7FFFFFU; if (ef == 0xff) { *exp = 0; return x; } if (ef == 0) { if (frac == 0) { *exp = 0; return x; } s = 31 - __builtin_clz(frac); m = frac << (23 - s); *exp = s - 148; bits = sign | (0x7eU << 23) | (m - (1U << 23)); __builtin_memcpy(&x, &bits, sizeof x); return x; } *exp = (int)ef - 126; bits = sign | (0x7eU << 23) | frac; __builtin_memcpy(&x, &bits, sizeof x); return x; } /* * The x86 80-bit extended format: 64 significand bits m with an explicit * integer bit, plus a sign/exponent word se biased by 16383. A canonical * normal has m in [2^63, 2^64) and p = ef - 16446, so *exp = ef - 16382 * and the fraction is m * 2^-64 with se rewritten to 16382. A subnormal * (ef == 0) is value m * 2^-16445; shifting m's msb (position s, 0..62) * up to bit 63 gives *exp = s - 16444. Unnormal inputs (ef > 0 with * m < 2^63) normalize the same way with *exp = ef + s - 16445. */ long double frexpl(long double x, int *exp) { struct { unsigned long long m; unsigned short se; } p; unsigned long long m; int ef; int s; __builtin_memcpy(&p, &x, sizeof p); m = p.m; ef = p.se & 0x7fff; if (ef == 0x7fff) { *exp = 0; return x; } if (m == 0) { *exp = 0; return x; /* ±0 and empty degenerate encodings */ } if (m < 0x8000000000000000ULL) { s = 63 - __builtin_clzll(m); m <<= (63 - s); *exp = (ef == 0 ? 1 : ef) + s - 16445; p.se = (unsigned short)((p.se & 0x8000) | 0x3ffe); p.m = m; __builtin_memcpy(&x, &p, sizeof p); return x; } *exp = ef - 16382; p.se = (unsigned short)((p.se & 0x8000) | 0x3ffe); __builtin_memcpy(&x, &p, sizeof p); return x; }