feat(math): abs/round/trunc/frexp/ldexp/scalbn/copysign/fmin/fmax/fmod

This commit is contained in:
2026-09-06 01:07:31 -04:00
parent c4c64a4eb5
commit b731a02db4
30 changed files with 5912 additions and 7 deletions
+173
View File
@@ -0,0 +1,173 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <math.h>
/*
* 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;
}