Files
vlibc/src/math/modf.c
T

216 lines
5.3 KiB
C

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <math.h>
/*
* Split x into an integral part stored in *iptr and a fractional part
* returned, both carrying x's sign (C23 7.12.6.5): modf(-1.5, &i) puts
* -1.0 in i and returns -0.5. The integral part is x truncated toward
* zero. modf(±0, &i) stores ±0 and returns ±0; modf(±Inf, &i) stores
* ±Inf and returns ±0 with x's sign; modf(NaN, &i) stores the NaN and
* returns it; a subnormal |x| < 1 stores ±0 (x's sign) and returns x.
*
* The implementation is a bit-level truncation (the same "clear the
* fractional mantissa bits" shape as trunc.c): a value with |x| < 1 has
* integral part ±0; a value too large to carry a fraction (|x| >= 2^52,
* >= 2^23, >= 2^63 for the three formats) is its own integral part; the
* values in between have their low (fraction) bits masked off. The
* fractional part is then x - i, an exact subtraction — x and i share the
* significand's leading bits, so the difference is a representable
* multiple of the common unit in the last place. When the subtraction is
* an exact zero the fraction is ±0 with x's sign, which the hardware
* would otherwise always report as +0.
*/
/*
* The double format: bit 63 the sign, bits 62..52 the exponent biased by
* 1023, bits 51..0 the fraction. A normal with exponent field e in
* [1023, 1074] has its low (1075 - e) fraction bits below the binary
* point; e >= 1075 means |x| >= 2^52, an integer already.
*/
double
modf(double x, double *iptr)
{
unsigned long long bits;
unsigned long long sign;
unsigned long long ef;
unsigned long long frac;
unsigned long long ibits;
double i;
double f;
int shift;
__builtin_memcpy(&bits, &x, sizeof bits);
sign = bits & (1ULL << 63);
ef = (bits >> 52) & 0x7ff;
frac = bits & 0xFFFFFFFFFFFFFULL;
if (ef == 0x7ff)
{
*iptr = x;
if (frac != 0)
{
return x; /* NaN: both parts are the NaN */
}
return sign != 0 ? -0.0 : 0.0; /* ±Inf: fraction ±0 of x's sign */
}
if (ef == 0)
{
if (frac == 0)
{
*iptr = x; /* ±0 */
return x;
}
*iptr = sign != 0 ? -0.0 : 0.0; /* subnormal: |x| < 1 */
return x;
}
if (ef >= 1075)
{
*iptr = x; /* |x| >= 2^52: already integral */
return sign != 0 ? -0.0 : 0.0;
}
if (ef < 1023)
{
*iptr = sign != 0 ? -0.0 : 0.0; /* 0 < |x| < 1 */
return x;
}
shift = 1075 - (int)ef;
ibits = bits & ~((1ULL << shift) - 1ULL);
__builtin_memcpy(&i, &ibits, sizeof i);
*iptr = i;
f = x - i;
if (f == 0.0)
{
return sign != 0 ? -0.0 : 0.0;
}
return f;
}
/*
* The float format: bit 31 the sign, bits 30..23 the exponent biased by
* 127, bits 22..0 the fraction. For e in [127, 149] the low (150 - e)
* fraction bits are fractional; e >= 150 means |x| >= 2^23, integral.
*/
float
modff(float x, float *iptr)
{
unsigned int bits;
unsigned int sign;
unsigned int ef;
unsigned int frac;
unsigned int ibits;
float i;
float f;
int shift;
__builtin_memcpy(&bits, &x, sizeof bits);
sign = bits & (1U << 31);
ef = (bits >> 23) & 0xff;
frac = bits & 0x7FFFFFU;
if (ef == 0xff)
{
*iptr = x;
if (frac != 0)
{
return x;
}
return sign != 0 ? -0.0f : 0.0f;
}
if (ef == 0)
{
if (frac == 0)
{
*iptr = x;
return x;
}
*iptr = sign != 0 ? -0.0f : 0.0f;
return x;
}
if (ef >= 150)
{
*iptr = x;
return sign != 0 ? -0.0f : 0.0f;
}
if (ef < 127)
{
*iptr = sign != 0 ? -0.0f : 0.0f;
return x;
}
shift = 150 - (int)ef;
ibits = bits & ~((1U << shift) - 1U);
__builtin_memcpy(&i, &ibits, sizeof i);
*iptr = i;
f = x - i;
if (f == 0.0f)
{
return sign != 0 ? -0.0f : 0.0f;
}
return f;
}
/*
* The x86 80-bit extended format: 64 significand bits m with an explicit
* integer bit and a sign/exponent word se biased by 16383. For e in
* [16383, 16445] the low (16446 - e) bits of m are fractional; e >= 16446
* means |x| >= 2^63, integral.
*/
long double
modfl(long double x, long double *iptr)
{
struct
{
unsigned long long m;
unsigned short se;
} p;
unsigned long long ibits;
long double i;
long double f;
int ef;
int shift;
__builtin_memcpy(&p, &x, sizeof p);
ef = p.se & 0x7fff;
if (ef == 0x7fff)
{
*iptr = x;
if (p.m != 0x8000000000000000ULL)
{
return x; /* NaN */
}
return (p.se & 0x8000) != 0 ? -0.0L : 0.0L; /* ±Inf */
}
if (p.m == 0)
{
*iptr = x; /* ±0 */
return x;
}
if (ef >= 16446)
{
*iptr = x;
return (p.se & 0x8000) != 0 ? -0.0L : 0.0L;
}
if (ef < 16383)
{
*iptr = (p.se & 0x8000) != 0 ? -0.0L : 0.0L; /* subnormal: |x| < 1 */
return x;
}
shift = 16446 - ef;
ibits = p.m & ~((1ULL << shift) - 1ULL);
p.m = ibits;
__builtin_memcpy(&i, &p, sizeof i);
*iptr = i;
f = x - i;
if (f == 0.0L)
{
return (p.se & 0x8000) != 0 ? -0.0L : 0.0L;
}
return f;
}