feat(math): abs/round/trunc/frexp/ldexp/scalbn/copysign/fmin/fmax/fmod
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The IEEE remainder x - n*y with n = x/y rounded to nearest, ties to
|
||||
* even (C23 7.12.10.2), plus the signed low bits of the integer quotient
|
||||
* n stored through quo (C23 7.12.10.3), all three precisions.
|
||||
*
|
||||
* The structure mirrors the host glibc remainder/remquo implementation
|
||||
* (glibc 2.44, measured byte-for-byte), because remquo's quotient is not
|
||||
* the full n: glibc tracks only the three lowest quotient bits and still
|
||||
* returns a remainder in the exact IEEE rounding, so the two functions
|
||||
* share one reduce-then-round shape. x is first reduced by fmod(x, 8y)
|
||||
* whenever |y| is small enough for 8y to be finite, which makes |x| < 8y
|
||||
* and therefore bounds x/y below 8; the tracked quotient cquo collects
|
||||
* the one 4y subtraction, the one 2y subtraction and the final rounding's
|
||||
* up-to-two y subtractions, which together hold the three lowest bits of
|
||||
* n. The final compare against y/2 (using doubled compares when y is so
|
||||
* small that halving could lose precision) rounds x to the nearest
|
||||
* multiple of y, ties to even via the natural 0.5 boundary, and each
|
||||
* subtraction that fires is exact (Sterbenz), so the returned remainder
|
||||
* is exact.
|
||||
*
|
||||
* The x86-64 host collapses the remainder and quotient signs the same
|
||||
* way the code below does: the magnitude result is computed from |x| and
|
||||
* |y| and then negated when x < 0, and *quo carries the sign of x/y.
|
||||
* Domain errors -- |y| == 0, x not finite, y a NaN -- return a NaN (the
|
||||
* classic indefinite pattern) and leave *quo untouched, as glibc does.
|
||||
* Like glibc, remquo never touches errno.
|
||||
*/
|
||||
|
||||
/* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction. */
|
||||
static double
|
||||
remquo_d(double x, double y, int *quo)
|
||||
{
|
||||
const unsigned long long dbl_inf = 0x7FF0000000000000ULL;
|
||||
unsigned long long xw;
|
||||
unsigned long long yw;
|
||||
unsigned long long hx;
|
||||
unsigned long long hy;
|
||||
unsigned long long sx;
|
||||
unsigned long long sy;
|
||||
double ax;
|
||||
double ay;
|
||||
double yh;
|
||||
int cquo;
|
||||
int qneg;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 63;
|
||||
sy = yw >> 63;
|
||||
qneg = (int)(sx ^ sy);
|
||||
hx = xw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
hy = yw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
|
||||
if (hy == 0 || hx >= dbl_inf || hy > dbl_inf)
|
||||
{
|
||||
xw = 0xFFF8000000000000ULL;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
|
||||
if (hx == hy)
|
||||
{
|
||||
*quo = qneg ? -1 : 1;
|
||||
return x * 0.0;
|
||||
}
|
||||
|
||||
ax = fabs(x);
|
||||
ay = fabs(y);
|
||||
if (hy <= 0x7FBFFFFFFFFFFFFFULL)
|
||||
{
|
||||
ax = fmod(ax, 8.0 * ay); /* now ax < 8 * ay */
|
||||
}
|
||||
|
||||
cquo = 0;
|
||||
if (hy <= 0x7FCFFFFFFFFFFFFFULL && ax >= 4.0 * ay)
|
||||
{
|
||||
ax -= 4.0 * ay;
|
||||
cquo += 4;
|
||||
}
|
||||
if (hy <= 0x7FDFFFFFFFFFFFFFULL && ax >= 2.0 * ay)
|
||||
{
|
||||
ax -= 2.0 * ay;
|
||||
cquo += 2;
|
||||
}
|
||||
|
||||
if (hy < 0x0020000000000000ULL)
|
||||
{
|
||||
if (ax + ax > ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax + ax >= ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yh = 0.5 * ay;
|
||||
if (ax > yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax >= yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*quo = qneg ? -cquo : cquo;
|
||||
if (ax == 0.0)
|
||||
{
|
||||
ax = 0.0;
|
||||
}
|
||||
if (sx)
|
||||
{
|
||||
ax = -ax;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
/* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction. */
|
||||
static float
|
||||
remquo_f(float x, float y, int *quo)
|
||||
{
|
||||
const unsigned int flt_inf = 0x7F800000U;
|
||||
unsigned int xw;
|
||||
unsigned int yw;
|
||||
unsigned int hx;
|
||||
unsigned int hy;
|
||||
unsigned int sx;
|
||||
unsigned int sy;
|
||||
float ax;
|
||||
float ay;
|
||||
float yh;
|
||||
int cquo;
|
||||
int qneg;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 31;
|
||||
sy = yw >> 31;
|
||||
qneg = (int)(sx ^ sy);
|
||||
hx = xw & 0x7FFFFFFFU;
|
||||
hy = yw & 0x7FFFFFFFU;
|
||||
|
||||
if (hy == 0 || hx >= flt_inf || hy > flt_inf)
|
||||
{
|
||||
xw = 0xFFC00000U;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
|
||||
if (hx == hy)
|
||||
{
|
||||
*quo = qneg ? -1 : 1;
|
||||
return x * 0.0f;
|
||||
}
|
||||
|
||||
ax = fabsf(x);
|
||||
ay = fabsf(y);
|
||||
if (hy <= 0x7DFFFFFFU)
|
||||
{
|
||||
ax = fmodf(ax, 8.0f * ay); /* now ax < 8 * ay */
|
||||
}
|
||||
|
||||
cquo = 0;
|
||||
if (hy <= 0x7E7FFFFFU && ax >= 4.0f * ay)
|
||||
{
|
||||
ax -= 4.0f * ay;
|
||||
cquo += 4;
|
||||
}
|
||||
if (hy <= 0x7EFFFFFFU && ax >= 2.0f * ay)
|
||||
{
|
||||
ax -= 2.0f * ay;
|
||||
cquo += 2;
|
||||
}
|
||||
|
||||
if (hy < 0x01000000U)
|
||||
{
|
||||
if (ax + ax > ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax + ax >= ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yh = 0.5f * ay;
|
||||
if (ax > yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax >= yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*quo = qneg ? -cquo : cquo;
|
||||
if (ax == 0.0f)
|
||||
{
|
||||
ax = 0.0f;
|
||||
}
|
||||
if (sx)
|
||||
{
|
||||
ax = -ax;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m in bytes 0..7 (the
|
||||
* integer bit is explicit) and a sign/exponent word se in bytes 8..9 with
|
||||
* the sign in bit 15 and the exponent biased by 16383 in bits 14..0.
|
||||
*/
|
||||
struct remquo_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static long double
|
||||
remquo_l(long double x, long double y, int *quo)
|
||||
{
|
||||
struct remquo_ld_word p;
|
||||
struct remquo_ld_word q;
|
||||
int ex;
|
||||
int ey;
|
||||
int qneg;
|
||||
int sx;
|
||||
int sy;
|
||||
long double ax;
|
||||
long double ay;
|
||||
long double yh;
|
||||
int cquo;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
sx = (int)(p.se >> 15);
|
||||
sy = (int)(q.se >> 15);
|
||||
qneg = sx ^ sy;
|
||||
ex = p.se & 0x7fff;
|
||||
ey = q.se & 0x7fff;
|
||||
|
||||
if ((q.m == 0 && ey == 0) || ex == 0x7fff ||
|
||||
(ey == 0x7fff && (q.m & 0x7FFFFFFFFFFFFFFFULL) != 0))
|
||||
{
|
||||
p.m = 0xC000000000000000ULL;
|
||||
p.se = 0xFFFF;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
if (ex == ey && p.m == q.m)
|
||||
{
|
||||
*quo = qneg ? -1 : 1;
|
||||
return x * 0.0L;
|
||||
}
|
||||
|
||||
ax = fabsl(x);
|
||||
ay = fabsl(y);
|
||||
if (ey <= 0x7ffb)
|
||||
{
|
||||
ax = fmodl(ax, 8.0L * ay); /* now ax < 8 * ay */
|
||||
}
|
||||
|
||||
cquo = 0;
|
||||
if (ey <= 0x7ffc && ax >= 4.0L * ay)
|
||||
{
|
||||
ax -= 4.0L * ay;
|
||||
cquo += 4;
|
||||
}
|
||||
if (ey <= 0x7ffd && ax >= 2.0L * ay)
|
||||
{
|
||||
ax -= 2.0L * ay;
|
||||
cquo += 2;
|
||||
}
|
||||
|
||||
if (ey < 0x0002)
|
||||
{
|
||||
if (ax + ax > ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax + ax >= ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yh = 0.5L * ay;
|
||||
if (ax > yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax >= yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*quo = qneg ? -cquo : cquo;
|
||||
if (ax == 0.0L)
|
||||
{
|
||||
ax = 0.0L;
|
||||
}
|
||||
if (sx)
|
||||
{
|
||||
ax = -ax;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
/*
|
||||
* remquo for a float argument.
|
||||
*/
|
||||
float
|
||||
remquof(float x, float y, int *quo) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remquo_f(x, y, quo);
|
||||
}
|
||||
|
||||
/*
|
||||
* remquo for a double argument.
|
||||
*/
|
||||
double
|
||||
remquo(double x, double y, int *quo) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remquo_d(x, y, quo);
|
||||
}
|
||||
|
||||
/*
|
||||
* remquo for a long double argument.
|
||||
*/
|
||||
long double
|
||||
remquol(long double x, long double y, int *quo) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remquo_l(x, y, quo);
|
||||
}
|
||||
Reference in New Issue
Block a user