365 lines
7.8 KiB
C
365 lines
7.8 KiB
C
#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);
|
|
}
|