feat(math): abs/round/trunc/frexp/ldexp/scalbn/copysign/fmin/fmax/fmod
This commit is contained in:
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* vlibc — math.h todo-39 consolidated final test: slices S1..S4.
|
||||
*
|
||||
* One pass over every implemented todo-39 family, in all three
|
||||
* precisions, as a final smoke of the whole arithmetic slice:
|
||||
*
|
||||
* S1 fabs/fabsf/fabsl, copysign, floor, ceil, trunc
|
||||
* S2 round, rint, nearbyint, lrint, llrint, ilogb, logb
|
||||
* S3 frexp, ldexp, modf, scalbn, scalbln
|
||||
* S4 fmin, fmax, fdim, lround, llround, fmod, remainder, remquo
|
||||
*
|
||||
* Expected values follow IEEE 754-2008 / C23 semantics by hand and, for
|
||||
* the S4 families, the byte-exact host glibc 2.44 behavior that the
|
||||
* differential probe checks (the remquo quotient rows, in particular, are
|
||||
* glibc's tracked-low-bits values, e.g. remquo(100.0, 3.0) reports 1, the
|
||||
* low quotient bits, not 33). Inputs travel through volatile objects so
|
||||
* the compiler cannot constant-fold the calls away; diagnostics go
|
||||
* through raw SYS_write and no host headers are included.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "../src/internal/syscall.h"
|
||||
|
||||
/* Write a NUL-terminated string to fd via the raw syscall layer. */
|
||||
static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void
|
||||
say(int fd, const char *s)
|
||||
{
|
||||
long n = 0;
|
||||
|
||||
while (s[n] != '\0')
|
||||
{
|
||||
n++;
|
||||
}
|
||||
__syscall3(SYS_write, fd, (long)s, n);
|
||||
}
|
||||
|
||||
static int failures;
|
||||
|
||||
static void
|
||||
check(int ok, const char *msg)
|
||||
{
|
||||
if (ok)
|
||||
{
|
||||
say(1, "ok ");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAIL ");
|
||||
failures++;
|
||||
}
|
||||
say(1, msg);
|
||||
say(1, "\n");
|
||||
}
|
||||
|
||||
/* S1: fabs, copysign, floor, ceil, trunc. */
|
||||
static void
|
||||
s1_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
volatile double nan = NAN;
|
||||
volatile double big = 1e300;
|
||||
volatile float nanf = NAN;
|
||||
volatile long double nanl = NAN;
|
||||
|
||||
check(fabs(-3.5) == 3.5, "fabs(-3.5) == 3.5");
|
||||
check(fabs(negzero) == 0.0 && !signbit(fabs(negzero)), "fabs(-0.0) == +0.0");
|
||||
check(fabs(nan) != nan, "fabs(NaN) is NaN");
|
||||
check(copysign(3.0, -1.0) == -3.0, "copysign(3,-1) == -3");
|
||||
check(copysign(-3.0, 1.0) == 3.0, "copysign(-3,1) == 3");
|
||||
check(copysign(negzero, 1.0) == 0.0 && !signbit(copysign(negzero, 1.0)),
|
||||
"copysign(-0,+1) == +0");
|
||||
check(floor(3.7) == 3.0, "floor(3.7) == 3");
|
||||
check(floor(-3.7) == -4.0, "floor(-3.7) == -4");
|
||||
check(floor(negzero) == 0.0 && signbit(floor(negzero)), "floor(-0.0) == -0.0");
|
||||
check(ceil(3.2) == 4.0, "ceil(3.2) == 4");
|
||||
check(ceil(-3.2) == -3.0, "ceil(-3.2) == -3");
|
||||
check(trunc(3.7) == 3.0, "trunc(3.7) == 3");
|
||||
check(trunc(-3.7) == -3.0, "trunc(-3.7) == -3");
|
||||
check(fabsf(-2.5f) == 2.5f, "fabsf(-2.5f) == 2.5f");
|
||||
check(fabsf(nanf) != nanf, "fabsf(NaN) is NaN");
|
||||
check(copysignf(1.0f, -0.0f) == -1.0f, "copysignf(1,-0) == -1");
|
||||
check(floorf(2.9f) == 2.0f, "floorf(2.9f) == 2");
|
||||
check(ceilf(-2.1f) == -2.0f, "ceilf(-2.1f) == -2");
|
||||
check(truncf(2.9f) == 2.0f, "truncf(2.9f) == 2");
|
||||
check(fabsl(-3.5L) == 3.5L, "fabsl(-3.5L) == 3.5L");
|
||||
check(fabsl(nanl) != nanl, "fabsl(NaN) is NaN");
|
||||
check(copysignl(-4.0L, -2.0L) == -4.0L, "copysignl(-4,-2) == -4");
|
||||
check(floorl(-3.7L) == -4.0L, "floorl(-3.7L) == -4");
|
||||
check(ceill(3.2L) == 4.0L, "ceill(3.2L) == 4");
|
||||
check(truncl(-3.7L) == -3.0L, "truncl(-3.7L) == -3");
|
||||
check(floor(big) == big, "floor(1e300) == 1e300");
|
||||
}
|
||||
|
||||
/* S2: round, rint, nearbyint, lrint, llrint, ilogb, logb. */
|
||||
static void
|
||||
s2_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
volatile double nan = NAN;
|
||||
volatile double sub = 0x1p-1074;
|
||||
volatile double two40 = 0x1p40;
|
||||
|
||||
check(round(0.5) == 1.0, "round(0.5) == 1");
|
||||
check(round(-0.5) == -1.0, "round(-0.5) == -1");
|
||||
check(round(2.5) == 3.0, "round(2.5) == 3");
|
||||
check(round(negzero) == 0.0 && signbit(round(negzero)), "round(-0.0) == -0.0");
|
||||
check(round(sub) == 0.0 && !signbit(round(sub)), "round(5e-324) == +0");
|
||||
check(isnan(round(nan)), "round(NaN) is NaN");
|
||||
check(rint(0.5) == 0.0, "rint(0.5) == 0 (ties to even)");
|
||||
check(rint(2.5) == 2.0, "rint(2.5) == 2 (ties to even)");
|
||||
check(rint(negzero) == 0.0 && signbit(rint(negzero)), "rint(-0.0) == -0.0");
|
||||
check(nearbyint(2.5) == 2.0, "nearbyint(2.5) == 2 (ties to even)");
|
||||
check(nearbyint(1.5) == 2.0, "nearbyint(1.5) == 2");
|
||||
check(lrint(2.5) == 2L, "lrint(2.5) == 2");
|
||||
check(lrint(two40) == 1099511627776L, "lrint(2^40) == 2^40");
|
||||
check(llrint(-2.5) == -2LL, "llrint(-2.5) == -2");
|
||||
check(llrint(1.5) == 2LL, "llrint(1.5) == 2");
|
||||
check(ilogb(8.0) == 3, "ilogb(8) == 3");
|
||||
check(ilogb(0.5) == -1, "ilogb(0.5) == -1");
|
||||
check(ilogb(sub) == -1074, "ilogb(min subnormal) == -1074");
|
||||
check(ilogb(0.0) == FP_ILOGB0, "ilogb(0.0) == FP_ILOGB0");
|
||||
check(logb(8.0) == 3.0, "logb(8) == 3");
|
||||
check(logb(0.5) == -1.0, "logb(0.5) == -1");
|
||||
check(logb(0.0) == -HUGE_VAL, "logb(0.0) == -Inf");
|
||||
check(roundf(-0.5f) == -1.0f, "roundf(-0.5f) == -1");
|
||||
check(rintf(2.5f) == 2.0f, "rintf(2.5f) == 2 (ties to even)");
|
||||
check(nearbyintf(-2.5f) == -2.0f, "nearbyintf(-2.5f) == -2");
|
||||
check(lrintf(1.5f) == 2L, "lrintf(1.5f) == 2");
|
||||
check(llrintf(2.5f) == 2LL, "llrintf(2.5f) == 2");
|
||||
check(ilogbf(0x1p-149f) == -149, "ilogbf(min subnormal) == -149");
|
||||
check(logbf(8.0f) == 3.0f, "logbf(8) == 3");
|
||||
check(roundl(2.5L) == 3.0L, "roundl(2.5L) == 3");
|
||||
check(rintl(-0.5L) == 0.0L && signbit(rintl(-0.5L)), "rintl(-0.5L) == -0.0L");
|
||||
check(nearbyintl(1.5L) == 2.0L, "nearbyintl(1.5L) == 2");
|
||||
check(lrintl(2.5L) == 2L, "lrintl(2.5L) == 2");
|
||||
check(llrintl(-2.5L) == -2LL, "llrintl(-2.5L) == -2");
|
||||
check(ilogbl(0x1p-16445L) == -16445, "ilogbl(min subnormal) == -16445");
|
||||
check(logbl(0.5L) == -1.0L, "logbl(0.5L) == -1");
|
||||
}
|
||||
|
||||
/* S3: frexp, ldexp, modf, scalbn, scalbln. */
|
||||
static void
|
||||
s3_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
int e;
|
||||
double ip;
|
||||
double fp;
|
||||
float ipf;
|
||||
float fpf;
|
||||
long double ipl;
|
||||
long double fpl;
|
||||
|
||||
check(frexp(8.0, &e) == 0.5 && e == 4, "frexp(8) == (0.5, 4)");
|
||||
check(frexp(-6.0, &e) == -0.75 && e == 3, "frexp(-6) == (-0.75, 3)");
|
||||
check(frexp(0.0, &e) == 0.0 && e == 0, "frexp(0) == (0, 0)");
|
||||
check(frexp(negzero, &e) == 0.0 && e == 0, "frexp(-0) == (-0, 0)");
|
||||
check(ldexp(1.0, 10) == 1024.0, "ldexp(1,10) == 1024");
|
||||
check(ldexp(1.0, -1075) == 0.0, "ldexp(1,-1075) == +0");
|
||||
check(scalbn(1.0, 10) == 1024.0, "scalbn(1,10) == 1024");
|
||||
check(scalbln(1.0, 10L) == 1024.0, "scalbln(1,10) == 1024");
|
||||
fp = modf(3.75, &ip);
|
||||
check(ip == 3.0 && fp == 0.75, "modf(3.75) == (3, 0.75)");
|
||||
fp = modf(-3.75, &ip);
|
||||
check(ip == -3.0 && fp == -0.75, "modf(-3.75) == (-3, -0.75)");
|
||||
check(frexpf(8.0f, &e) == 0.5f && e == 4, "frexpf(8) == (0.5, 4)");
|
||||
check(ldexpf(1.0f, 10) == 1024.0f, "ldexpf(1,10) == 1024");
|
||||
check(scalbnf(1.0f, -10) == 0.0009765625f, "scalbnf(1,-10) == 2^-10");
|
||||
fpf = modff(3.75f, &ipf);
|
||||
check(ipf == 3.0f && fpf == 0.75f, "modff(3.75f) == (3, 0.75)");
|
||||
check(frexpl(8.0L, &e) == 0.5L && e == 4, "frexpl(8) == (0.5, 4)");
|
||||
check(ldexpl(1.0L, 10) == 1024.0L, "ldexpl(1,10) == 1024");
|
||||
check(scalbnl(1.0L, 10) == 1024.0L, "scalbnl(1,10) == 1024");
|
||||
fpl = modfl(3.75L, &ipl);
|
||||
check(ipl == 3.0L && fpl == 0.75L, "modfl(3.75L) == (3, 0.75)");
|
||||
}
|
||||
|
||||
/* S4a: fmin, fmax, fdim, lround, llround. */
|
||||
static void
|
||||
s4_minmax_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
volatile double nan = NAN;
|
||||
volatile double inf = HUGE_VAL;
|
||||
volatile float nanf = NAN;
|
||||
volatile long double nanl = NAN;
|
||||
volatile double big = 1e300;
|
||||
volatile double huge = 1.7e308;
|
||||
volatile float bigf = 1e30f;
|
||||
|
||||
check(fmin(3.0, 5.0) == 3.0, "fmin(3,5) == 3");
|
||||
check(fmin(5.0, 3.0) == 3.0, "fmin(5,3) == 3");
|
||||
check(fmin(3.0, 5.0) == 3.0, "fmin(3,5) == 3");
|
||||
check(fmin(negzero, 0.0) == 0.0 && signbit(fmin(negzero, 0.0)), "fmin(-0,+0) == -0");
|
||||
check(fmin(0.0, negzero) == 0.0 && signbit(fmin(0.0, negzero)), "fmin(+0,-0) == -0");
|
||||
check(fmin(nan, 5.0) == 5.0, "fmin(NaN,5) == 5");
|
||||
check(fmin(5.0, nan) == 5.0, "fmin(5,NaN) == 5");
|
||||
check(isnan(fmin(nan, nan)), "fmin(NaN,NaN) is NaN");
|
||||
check(fmax(3.0, 5.0) == 5.0, "fmax(3,5) == 5");
|
||||
check(fmax(5.0, 3.0) == 5.0, "fmax(5,3) == 5");
|
||||
check(fmax(negzero, 0.0) == 0.0 && !signbit(fmax(negzero, 0.0)), "fmax(-0,+0) == +0");
|
||||
check(fmax(0.0, negzero) == 0.0 && !signbit(fmax(0.0, negzero)), "fmax(+0,-0) == +0");
|
||||
check(fmax(nan, 5.0) == 5.0, "fmax(NaN,5) == 5");
|
||||
check(fmax(5.0, nan) == 5.0, "fmax(5,NaN) == 5");
|
||||
check(fmin(-inf, -5.0) == -inf, "fmin(-Inf,-5) == -Inf");
|
||||
check(fmax(inf, 5.0) == inf, "fmax(+Inf,5) == +Inf");
|
||||
check(fminf(3.0f, 5.0f) == 3.0f, "fminf(3,5) == 3");
|
||||
check(fminf(nanf, 5.0f) == 5.0f, "fminf(NaN,5) == 5");
|
||||
check(fmaxf(3.0f, 5.0f) == 5.0f, "fmaxf(3,5) == 5");
|
||||
check(fminl(3.0L, 5.0L) == 3.0L, "fminl(3,5) == 3");
|
||||
check(fminl(nanl, 5.0L) == 5.0L, "fminl(NaN,5) == 5");
|
||||
check(fmaxl(3.0L, 5.0L) == 5.0L, "fmaxl(3,5) == 5");
|
||||
check(fmaxl(nanl, 5.0L) == 5.0L, "fmaxl(NaN,5) == 5");
|
||||
|
||||
check(fdim(5.0, 3.0) == 2.0, "fdim(5,3) == 2");
|
||||
check(fdim(3.0, 5.0) == 0.0 && !signbit(fdim(3.0, 5.0)), "fdim(3,5) == +0");
|
||||
check(fdim(3.0, 3.0) == 0.0, "fdim(3,3) == +0");
|
||||
check(fdim(inf, -inf) == inf, "fdim(+Inf,-Inf) == +Inf");
|
||||
check(isnan(fdim(nan, 5.0)), "fdim(NaN,5) is NaN");
|
||||
check(isnan(fdim(5.0, nan)), "fdim(5,NaN) is NaN");
|
||||
check(fdim(big, -big) == 2.0 * big, "fdim(1e300,-1e300) == 2e300");
|
||||
check(fdim(huge, -huge) == inf, "fdim(1.7e308,-1.7e308) == +Inf");
|
||||
check(fdimf(5.0f, 3.0f) == 2.0f, "fdimf(5,3) == 2");
|
||||
check(fdimf(3.0f, 5.0f) == 0.0f, "fdimf(3,5) == +0");
|
||||
check(fdiml(5.0L, 3.0L) == 2.0L, "fdiml(5,3) == 2");
|
||||
check(fdiml(3.0L, 5.0L) == 0.0L, "fdiml(3,5) == +0");
|
||||
|
||||
check(lround(0.5) == 1L, "lround(0.5) == 1");
|
||||
check(lround(-0.5) == -1L, "lround(-0.5) == -1");
|
||||
check(lround(2.5) == 3L, "lround(2.5) == 3");
|
||||
check(lround(-2.5) == -3L, "lround(-2.5) == -3");
|
||||
check(lround(big) == LONG_MIN, "lround(1e300) == LONG_MIN");
|
||||
check(lround(-big) == LONG_MIN, "lround(-1e300) == LONG_MIN");
|
||||
check(lroundf(2.5f) == 3L, "lroundf(2.5f) == 3");
|
||||
check(lroundf(bigf) == LONG_MIN, "lroundf(1e30f) == LONG_MIN");
|
||||
check(lroundl(2.5L) == 3L, "lroundl(2.5L) == 3");
|
||||
check(llround(0.5) == 1LL, "llround(0.5) == 1");
|
||||
check(llround(-0.5) == -1LL, "llround(-0.5) == -1");
|
||||
check(llround(2.5) == 3LL, "llround(2.5) == 3");
|
||||
check(llround(big) == LLONG_MIN, "llround(1e300) == LLONG_MIN");
|
||||
check(llround(nan) == LLONG_MIN, "llround(NaN) == LLONG_MIN");
|
||||
check(llroundf(2.5f) == 3LL, "llroundf(2.5f) == 3");
|
||||
check(llroundl(2.5L) == 3LL, "llroundl(2.5L) == 3");
|
||||
}
|
||||
|
||||
/* S4b: fmod, remainder, remquo (glibc-verified rows). */
|
||||
static void
|
||||
s4_rem_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
volatile double nan = NAN;
|
||||
volatile double inf = HUGE_VAL;
|
||||
volatile float nanf = NAN;
|
||||
volatile long double nanl = NAN;
|
||||
volatile double a100 = 100.0;
|
||||
volatile double a34 = 34.0;
|
||||
volatile double a40 = 40.0;
|
||||
volatile double a3 = 3.0;
|
||||
volatile double a5 = 5.0;
|
||||
volatile float f100 = 100.0f;
|
||||
volatile long double l100 = 100.0L;
|
||||
volatile long double l40 = 40.0L;
|
||||
int q;
|
||||
|
||||
check(fmod(5.0, 3.0) == 2.0, "fmod(5,3) == 2");
|
||||
check(fmod(-5.0, 3.0) == -2.0, "fmod(-5,3) == -2");
|
||||
check(fmod(5.0, -3.0) == 2.0, "fmod(5,-3) == 2");
|
||||
check(fmod(-5.0, -3.0) == -2.0, "fmod(-5,-3) == -2");
|
||||
check(fmod(6.0, 3.0) == 0.0 && !signbit(fmod(6.0, 3.0)), "fmod(6,3) == +0");
|
||||
check(fmod(-6.0, 3.0) == 0.0 && signbit(fmod(-6.0, 3.0)), "fmod(-6,3) == -0");
|
||||
check(fmod(3.0, 5.0) == 3.0, "fmod(3,5) == 3");
|
||||
check(fmod(negzero, 5.0) == 0.0 && signbit(fmod(negzero, 5.0)), "fmod(-0,5) == -0");
|
||||
check(fmod(5.0, inf) == 5.0, "fmod(5,+Inf) == 5");
|
||||
check(isnan(fmod(5.0, 0.0)), "fmod(5,0) is NaN");
|
||||
check(isnan(fmod(inf, 3.0)), "fmod(+Inf,3) is NaN");
|
||||
check(isnan(fmod(nan, 3.0)), "fmod(NaN,3) is NaN");
|
||||
check(fmodf(5.0f, 3.0f) == 2.0f, "fmodf(5,3) == 2");
|
||||
check(fmodf(-5.0f, 3.0f) == -2.0f, "fmodf(-5,3) == -2");
|
||||
check(fmodf(5.0f, 0.0f) != 0 && !(fmodf(5.0f, 0.0f) == fmodf(5.0f, 0.0f)), "fmodf(5,0) is NaN");
|
||||
check(isnan(fmodf(nanf, 3.0f)), "fmodf(NaN,3) is NaN");
|
||||
check(fmodl(5.0L, 3.0L) == 2.0L, "fmodl(5,3) == 2");
|
||||
check(fmodl(-5.0L, 3.0L) == -2.0L, "fmodl(-5,3) == -2");
|
||||
check(fmodl(5.0L, 0.0L) != fmodl(5.0L, 0.0L), "fmodl(5,0) is NaN");
|
||||
check(isnan(fmodl(nanl, 3.0L)), "fmodl(NaN,3) is NaN");
|
||||
|
||||
check(remainder(5.0, 3.0) == -1.0, "remainder(5,3) == -1");
|
||||
check(remainder(5.0, 2.0) == 1.0, "remainder(5,2) == 1");
|
||||
check(remainder(6.0, 4.0) == -2.0, "remainder(6,4) == -2");
|
||||
check(remainder(7.0, 4.0) == -1.0, "remainder(7,4) == -1");
|
||||
check(remainder(5.0, 5.0) == 0.0, "remainder(5,5) == +0");
|
||||
check(remainder(-5.0, 3.0) == 1.0, "remainder(-5,3) == 1");
|
||||
check(remainder(5.0, -3.0) == -1.0, "remainder(5,-3) == -1");
|
||||
check(isnan(remainder(5.0, 0.0)), "remainder(5,0) is NaN");
|
||||
check(isnan(remainder(inf, 3.0)), "remainder(+Inf,3) is NaN");
|
||||
check(isnan(remainder(nan, 3.0)), "remainder(NaN,3) is NaN");
|
||||
check(remainderf(5.0f, 3.0f) == -1.0f, "remainderf(5,3) == -1");
|
||||
check(isnan(remainderf(5.0f, 0.0f)), "remainderf(5,0) is NaN");
|
||||
check(remainderl(5.0L, 3.0L) == -1.0L, "remainderl(5,3) == -1");
|
||||
check(isnan(remainderl(5.0L, 0.0L)), "remainderl(5,0) is NaN");
|
||||
check(remainderl(-5.0L, 3.0L) == 1.0L, "remainderl(-5,3) == 1");
|
||||
|
||||
check(remquo(5.0, 3.0, &q) == -1.0 && q == 2, "remquo(5,3): r=-1 q=2");
|
||||
check(remquo(-5.0, 3.0, &q) == 1.0 && q == -2, "remquo(-5,3): r=1 q=-2");
|
||||
check(remquo(5.0, -3.0, &q) == -1.0 && q == -2, "remquo(5,-3): r=-1 q=-2");
|
||||
check(remquo(a100, a3, &q) == 1.0 && q == 1, "remquo(100,3): r=1 q=1");
|
||||
check(remquo(a34, a3, &q) == 1.0 && q == 3, "remquo(34,3): r=1 q=3");
|
||||
check(remquo(a40, a3, &q) == 1.0 && q == 5, "remquo(40,3): r=1 q=5");
|
||||
check(remquo(7.0, 2.0, &q) == -1.0 && q == 4, "remquo(7,2): r=-1 q=4");
|
||||
check(remquo(6.0, 3.0, &q) == 0.0 && q == 2, "remquo(6,3): r=0 q=2");
|
||||
check(remquo(2.0, 3.0, &q) == -1.0 && q == 1, "remquo(2,3): r=-1 q=1");
|
||||
check(remquo(1.0, 3.0, &q) == 1.0 && q == 0, "remquo(1,3): r=1 q=0");
|
||||
check(remquo(a5, a5, &q) == 0.0 && q == 1, "remquo(5,5): r=0 q=1");
|
||||
check(isnan(remquo(a5, 0.0, &q)), "remquo(5,0) is NaN");
|
||||
check(isnan(remquo(inf, a3, &q)), "remquo(+Inf,3) is NaN");
|
||||
check(remquof(f100, 3.0f, &q) == 1.0f && q == 1, "remquof(100,3): q=1");
|
||||
check(remquof(5.0f, 3.0f, &q) == -1.0f && q == 2, "remquof(5,3): q=2");
|
||||
check(remquol(l100, 3.0L, &q) == 1.0L && q == 1, "remquol(100,3): q=1");
|
||||
check(remquol(5.0L, 3.0L, &q) == -1.0L && q == 2, "remquol(5,3): q=2");
|
||||
check(remquol(l40, 3.0L, &q) == 1.0L && q == 5, "remquol(40,3): q=5");
|
||||
check(isnan(remquol(nanl, 3.0L, &q)), "remquol(NaN,3) is NaN");
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
s1_scenario();
|
||||
s2_scenario();
|
||||
s3_scenario();
|
||||
s4_minmax_scenario();
|
||||
s4_rem_scenario();
|
||||
|
||||
if (failures == 0)
|
||||
{
|
||||
say(1, "all math todo-39 (S1-S4) tests passed\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAILURES\n");
|
||||
}
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* vlibc — math.h slice S1 test: fabs/copysign/floor/ceil/trunc (todo 39).
|
||||
*
|
||||
* Exercises every implemented family in all three precisions plus the
|
||||
* <math.h> classification macros and the <tgmath.h> generic dispatch:
|
||||
*
|
||||
* 1. fabs/fabsf/fabsl of ±1.5 and of ±0.0 (signed-zero flattening);
|
||||
* 2. copysign/copysignf/copysignl sign transfer, including from ±0.0
|
||||
* and onto a NaN;
|
||||
* 3. floor across the sign boundary: floor(-0.5) == -1.0, floor(+0.5)
|
||||
* == +0.0, integers and ±0 pass through, and the subnormal
|
||||
* floor(5e-324) == 0.0 / floor(-5e-324) == -1.0;
|
||||
* 4. ceil with the real negative zero: ceil(-0.5) == -0.0 with signbit
|
||||
* set, ceil(+0.5) == 1.0;
|
||||
* 5. trunc toward zero: trunc(-1.7) == -1.0, trunc(1.7) == 1.0,
|
||||
* trunc(-0.7) == -0.0 (signbit set);
|
||||
* 6. NaN and ±Inf passthrough for all three rounding functions and the
|
||||
* boundary mantissa-clearing paths (values whose integer part fills
|
||||
* the whole significand minus one bit);
|
||||
* 7. classification macros (fpclassify/isnan/isinf/isfinite/isnormal/
|
||||
* signbit) over the FP_* constants.
|
||||
*
|
||||
* Values that must survive exactly are fed through volatile objects so the
|
||||
* compiler cannot constant-fold the call away and the real library
|
||||
* functions run. No host headers are included (-Iinclude shadows GCC's);
|
||||
* diagnostics go through raw SYS_write, and errno is never read (vlibc's
|
||||
* errno slot collides with the host TCB in this host-linked standalone
|
||||
* binary).
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <tgmath.h>
|
||||
|
||||
#include "../src/internal/syscall.h"
|
||||
|
||||
/* Write a NUL-terminated string to fd via the raw syscall layer. The
|
||||
* optimize attribute keeps GCC from lowering the length loop into a
|
||||
* strlen call, which would leave a vlibc-owned symbol undefined in this
|
||||
* host-linked standalone binary (house idiom, see src/string). */
|
||||
static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void
|
||||
say(int fd, const char *s)
|
||||
{
|
||||
long n = 0;
|
||||
|
||||
while (s[n] != '\0')
|
||||
{
|
||||
n++;
|
||||
}
|
||||
__syscall3(SYS_write, fd, (long)s, n);
|
||||
}
|
||||
|
||||
static int failures;
|
||||
|
||||
static void
|
||||
check(int ok, const char *msg)
|
||||
{
|
||||
if (ok)
|
||||
{
|
||||
say(1, "ok ");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAIL ");
|
||||
failures++;
|
||||
}
|
||||
say(1, msg);
|
||||
say(1, "\n");
|
||||
}
|
||||
|
||||
/* 1. fabs family: magnitude of positive and negative inputs. */
|
||||
static void
|
||||
fabs_scenario(void)
|
||||
{
|
||||
volatile float xf = -1.5f;
|
||||
volatile double xd = -1.5;
|
||||
volatile long double xl = -1.5L;
|
||||
volatile float zf = -0.0f;
|
||||
volatile double zd = -0.0;
|
||||
|
||||
check(fabsf(xf) == 1.5f, "fabsf(-1.5f) == 1.5f");
|
||||
check(fabs(xd) == 1.5, "fabs(-1.5) == 1.5");
|
||||
check(fabsl(xl) == 1.5L, "fabsl(-1.5L) == 1.5L");
|
||||
check(fabsf(1.5f) == 1.5f, "fabsf(+1.5f) == 1.5f");
|
||||
check(fabs(1.5) == 1.5, "fabs(+1.5) == 1.5");
|
||||
check(fabsl(1.5L) == 1.5L, "fabsl(+1.5L) == 1.5L");
|
||||
check(fabsf(zf) == 0.0f && !signbit(fabsf(zf)), "fabsf(-0.0f) is +0.0");
|
||||
check(fabs(zd) == 0.0 && !signbit(fabs(zd)), "fabs(-0.0) is +0.0");
|
||||
}
|
||||
|
||||
/* 2. copysign family: sign follows y, including y == ±0.0 and NaN x. */
|
||||
static void
|
||||
copysign_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
volatile double poszero = 0.0;
|
||||
volatile double nan = NAN;
|
||||
|
||||
check(copysign(1.0, negzero) == -1.0, "copysign(1.0, -0.0) == -1.0");
|
||||
check(copysign(1.0, poszero) == 1.0, "copysign(1.0, +0.0) == +1.0");
|
||||
check(copysign(-1.0, poszero) == 1.0, "copysign(-1.0, +0.0) == +1.0");
|
||||
check(signbit(copysign(1.0, negzero)), "copysign(1.0, -0.0) has signbit set");
|
||||
check(copysignf(-1.0f, -0.0f) == -1.0f, "copysignf(-1.0f, -0.0f) == -1.0f");
|
||||
check(copysignl(-1.0L, 0.0L) == 1.0L, "copysignl(-1.0L, +0.0L) == +1.0L");
|
||||
check(signbit(copysign(nan, negzero)), "copysign(NaN, -0.0) carries the sign");
|
||||
check(!signbit(copysign(nan, poszero)), "copysign(NaN, +0.0) clears the sign");
|
||||
}
|
||||
|
||||
/* 3. floor: toward -Inf, across the sign boundary and the subnormal
|
||||
* range. */
|
||||
static void
|
||||
floor_scenario(void)
|
||||
{
|
||||
volatile double half = 0.5;
|
||||
volatile long double neg_half = -0.5L;
|
||||
volatile double two = 2.0;
|
||||
volatile float twof = 2.0f;
|
||||
volatile double negzero = -0.0;
|
||||
volatile double sub = 5e-324; /* the smallest subnormal */
|
||||
volatile double big = 2251799813685248.0; /* 2^51 */
|
||||
volatile double big_half = 2251799813685248.5; /* 2^51 + 0.5 */
|
||||
|
||||
check(floor(-0.5) == -1.0, "floor(-0.5) == -1.0");
|
||||
check(floor(half) == 0.0 && !signbit(floor(half)), "floor(+0.5) == +0.0");
|
||||
check(floorl(neg_half) == -1.0L, "floorl(-0.5L) == -1.0L");
|
||||
check(floorf(-0.5f) == -1.0f, "floorf(-0.5f) == -1.0f");
|
||||
check(floor(two) == 2.0, "floor(2.0) == 2.0");
|
||||
check(floorf(twof) == 2.0f, "floorf(2.0f) == 2.0f");
|
||||
check(floor(-two) == -2.0, "floor(-2.0) == -2.0");
|
||||
check(floor(negzero) == 0.0 && signbit(floor(negzero)), "floor(-0.0) == -0.0");
|
||||
check(floor(sub) == 0.0 && !signbit(floor(sub)), "floor(5e-324) == +0.0");
|
||||
check(floor(-sub) == -1.0, "floor(-5e-324) == -1.0");
|
||||
check(floor(big_half) == big, "floor(2^51 + 0.5) == 2^51");
|
||||
check(floor(-big_half) == -big - 1.0, "floor(-(2^51 + 0.5)) == -2^51 - 1");
|
||||
check(floorl((long double)big + 0.5L) == (long double)big, "floorl(2^62 + 0.5L) == 2^62L");
|
||||
check(floorl(-((long double)big + 0.5L)) == -((long double)big) - 1.0L,
|
||||
"floorl(-(2^62 + 0.5L)) == -2^62L - 1");
|
||||
}
|
||||
|
||||
/* 4. ceil: toward +Inf; ceil(-0.5) must be a real negative zero. */
|
||||
static void
|
||||
ceil_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
volatile double neg_half = -0.5;
|
||||
volatile float pos_half = 0.5f;
|
||||
volatile long double pos_half_l = 0.5L;
|
||||
volatile double neg_third = -0.3;
|
||||
volatile double big = 2251799813685248.0;
|
||||
volatile double big_half = 2251799813685248.5;
|
||||
|
||||
check(ceil(neg_half) == 0.0 && signbit(ceil(neg_half)), "ceil(-0.5) == -0.0 (signbit)");
|
||||
check(ceil(-1.5) == -1.0, "ceil(-1.5) == -1.0");
|
||||
check(ceilf(pos_half) == 1.0f, "ceilf(+0.5f) == 1.0f");
|
||||
check(ceill(pos_half_l) == 1.0L, "ceill(+0.5L) == 1.0L");
|
||||
check(ceil(neg_third) == 0.0 && signbit(ceil(neg_third)), "ceil(-0.3) == -0.0 (signbit)");
|
||||
check(ceil(negzero) == 0.0 && signbit(ceil(negzero)), "ceil(-0.0) == -0.0");
|
||||
check(ceilf(-0.5f) == 0.0f && signbit(ceilf(-0.5f)), "ceilf(-0.5f) == -0.0f (signbit)");
|
||||
check(ceill(-0.5L) == 0.0L && signbit(ceill(-0.5L)), "ceill(-0.5L) == -0.0L (signbit)");
|
||||
check(ceil(big_half) == big + 1.0, "ceil(2^51 + 0.5) == 2^51 + 1");
|
||||
check(ceil(-big_half) == -big, "ceil(-(2^51 + 0.5)) == -2^51");
|
||||
}
|
||||
|
||||
/* 5. trunc: toward zero, with signed-zero results below 1. */
|
||||
static void
|
||||
trunc_scenario(void)
|
||||
{
|
||||
volatile double neg = -1.7;
|
||||
volatile double pos = 1.7;
|
||||
volatile double neg_tiny = -0.7;
|
||||
volatile float posf = 1.7f;
|
||||
volatile long double negl = -1.7L;
|
||||
volatile double negzero = -0.0;
|
||||
volatile double big = 2251799813685248.0;
|
||||
volatile double big_half = 2251799813685248.5;
|
||||
|
||||
check(trunc(neg) == -1.0, "trunc(-1.7) == -1.0");
|
||||
check(trunc(pos) == 1.0, "trunc(+1.7) == 1.0");
|
||||
check(truncf(posf) == 1.0f, "truncf(+1.7f) == 1.0f");
|
||||
check(truncl(negl) == -1.0L, "truncl(-1.7L) == -1.0L");
|
||||
check(trunc(neg_tiny) == 0.0 && signbit(trunc(neg_tiny)), "trunc(-0.7) == -0.0 (signbit)");
|
||||
check(trunc(0.7) == 0.0 && !signbit(trunc(0.7)), "trunc(+0.7) == +0.0");
|
||||
check(trunc(negzero) == 0.0 && signbit(trunc(negzero)), "trunc(-0.0) == -0.0");
|
||||
check(trunc(big_half) == big, "trunc(2^51 + 0.5) == 2^51");
|
||||
check(trunc(-big_half) == -big, "trunc(-(2^51 + 0.5)) == -2^51");
|
||||
check(truncl((long double)big + 0.5L) == (long double)big, "truncl(2^62 + 0.5L) == 2^62L");
|
||||
}
|
||||
|
||||
/* 6. NaN and ±Inf passthrough for the three rounding families, plus the
|
||||
* boundary value 4194304.5f (2^22 + 0.5, where float clearing drops a
|
||||
* single bit). */
|
||||
static void
|
||||
special_scenario(void)
|
||||
{
|
||||
volatile double nan = NAN;
|
||||
volatile double inf = HUGE_VAL;
|
||||
volatile float nanf = NAN;
|
||||
volatile long double inf_l = HUGE_VALL;
|
||||
volatile float float_bound = 4194304.5f;
|
||||
volatile double dbl_bound = 2251799813685248.5;
|
||||
|
||||
check(isnan(floor(nan)), "floor(NaN) is NaN");
|
||||
check(isnan(ceil(nan)), "ceil(NaN) is NaN");
|
||||
check(isnan(trunc(nan)), "trunc(NaN) is NaN");
|
||||
check(isnan(floorf(nanf)), "floorf(NaN) is NaN");
|
||||
check(floor(inf) == inf, "floor(+Inf) == +Inf");
|
||||
check(floor(-inf) == -inf, "floor(-Inf) == -Inf");
|
||||
check(ceil(inf) == inf, "ceil(+Inf) == +Inf");
|
||||
check(ceil(-inf) == -inf, "ceil(-Inf) == -Inf");
|
||||
check(trunc(inf) == inf, "trunc(+Inf) == +Inf");
|
||||
check(trunc(-inf) == -inf, "trunc(-Inf) == -Inf");
|
||||
check(ceill(inf_l) == inf_l, "ceill(+Inf) == +Inf");
|
||||
check(truncl(-inf_l) == -inf_l, "truncl(-Inf) == -Inf");
|
||||
check(floorf(float_bound) == 4194304.0f, "floorf(2^22 + 0.5f) == 2^22f");
|
||||
check(trunc(float_bound) == 4194304.0, "trunc(2^22 + 0.5) == 2^22");
|
||||
check(ceilf(float_bound) == 4194305.0f, "ceilf(2^22 + 0.5f) == 2^22 + 1f");
|
||||
check(trunc(dbl_bound) == 2251799813685248.0, "trunc(2^51 + 0.5) == 2^51");
|
||||
}
|
||||
|
||||
/* 7. Classification macros over the FP_* constants. */
|
||||
static void
|
||||
classify_scenario(void)
|
||||
{
|
||||
volatile double nan = NAN;
|
||||
volatile double inf = HUGE_VAL;
|
||||
volatile double zero = 0.0;
|
||||
volatile double sub = 5e-324;
|
||||
volatile double one = 1.0;
|
||||
|
||||
check(fpclassify(nan) == FP_NAN, "fpclassify(NaN) == FP_NAN");
|
||||
check(fpclassify(inf) == FP_INFINITE, "fpclassify(+Inf) == FP_INFINITE");
|
||||
check(fpclassify(zero) == FP_ZERO, "fpclassify(0.0) == FP_ZERO");
|
||||
check(fpclassify(sub) == FP_SUBNORMAL, "fpclassify(5e-324) == FP_SUBNORMAL");
|
||||
check(fpclassify(one) == FP_NORMAL, "fpclassify(1.0) == FP_NORMAL");
|
||||
check(isnan(nan) && !isnan(one), "isnan distinguishes NaN");
|
||||
check(isinf(inf) && !isinf(one), "isinf distinguishes Inf");
|
||||
check(isfinite(one) && !isfinite(inf) && !isfinite(nan), "isfinite is false for Inf/NaN");
|
||||
check(isnormal(one) && !isnormal(zero) && !isnormal(sub) && !isnormal(nan),
|
||||
"isnormal is false for zero/subnormal/NaN");
|
||||
check(signbit(-0.0) && !signbit(zero), "signbit distinguishes -0.0");
|
||||
check(fpclassify(1.0f) == FP_NORMAL, "fpclassify works on float");
|
||||
check(fpclassify(1.0L) == FP_NORMAL, "fpclassify works on long double");
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
fabs_scenario();
|
||||
copysign_scenario();
|
||||
floor_scenario();
|
||||
ceil_scenario();
|
||||
trunc_scenario();
|
||||
special_scenario();
|
||||
classify_scenario();
|
||||
|
||||
if (failures == 0)
|
||||
{
|
||||
say(1, "all math slice S1 tests passed\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAILURES\n");
|
||||
}
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* vlibc — math.h slice S2 test: the rounding family (todo 39).
|
||||
*
|
||||
* Exercises every implemented family in all three precisions:
|
||||
*
|
||||
* 1. round/roundf/roundl — half away from zero: round(-0.5) == -1.0,
|
||||
* round(2.5) == 3.0, signed zero preserved, subnormals collapse to
|
||||
* ±0;
|
||||
* 2. rint — round to nearest, ties to even, in the default (only
|
||||
* reachable) rounding mode: rint(0.5) == 0.0, rint(-0.5) == -0.0
|
||||
* (a real negative zero), rint(2.5) == 2.0;
|
||||
* 3. nearbyint — the same values as rint but through the guaranteed
|
||||
* exception-free entry points;
|
||||
* 4. lrint/lrintf/lrintl and llrint/llrintf/llrintl — the same nearest
|
||||
* rounding returned as long / long long, over 0.0, integers, ties
|
||||
* and the 2^40 scale (exact in both types);
|
||||
* 5. ilogb/ilogbf/ilogbl — the unbiased exponent with the FP_ILOGB0
|
||||
* (zero) and FP_ILOGBNAN (Inf/NaN) sentinels, including the
|
||||
* subnormal exponents -1074 (double), -149 (float) and -16445
|
||||
* (x87 long double);
|
||||
* 6. logb/logbf/logbl — the same exponent returned as a floating
|
||||
* value, with ±0 -> -Inf, ±Inf -> +Inf and NaN passing through.
|
||||
*
|
||||
* Expected values follow IEEE 754-2008 semantics by hand; they are NOT
|
||||
* print-compared against the host libm at runtime (the host glibc on
|
||||
* this machine returns FP_ILOGB0 for ilogb(NaN), deviating from C23,
|
||||
* and the host TCB makes errno unreadable in this host-linked binary).
|
||||
* Values that must survive exactly are fed through volatile objects so
|
||||
* the compiler cannot constant-fold the call away; diagnostics go
|
||||
* through raw SYS_write and no host headers are included.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "../src/internal/syscall.h"
|
||||
|
||||
/* Write a NUL-terminated string to fd via the raw syscall layer. The
|
||||
* optimize attribute keeps GCC from lowering the length loop into a
|
||||
* strlen call, which would leave a vlibc-owned symbol undefined in this
|
||||
* host-linked standalone binary (house idiom, see src/string). */
|
||||
static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void
|
||||
say(int fd, const char *s)
|
||||
{
|
||||
long n = 0;
|
||||
|
||||
while (s[n] != '\0')
|
||||
{
|
||||
n++;
|
||||
}
|
||||
__syscall3(SYS_write, fd, (long)s, n);
|
||||
}
|
||||
|
||||
static int failures;
|
||||
|
||||
static void
|
||||
check(int ok, const char *msg)
|
||||
{
|
||||
if (ok)
|
||||
{
|
||||
say(1, "ok ");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAIL ");
|
||||
failures++;
|
||||
}
|
||||
say(1, msg);
|
||||
say(1, "\n");
|
||||
}
|
||||
|
||||
/* 1. round: halfway cases away from zero, with signed zero and the
|
||||
* subnormal collapse. */
|
||||
static void
|
||||
round_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
volatile double sub = 0x1p-1074; /* smallest subnormal double */
|
||||
volatile double big = 1e300;
|
||||
volatile long double ldsub = 0x1p-16445L; /* smallest subnormal ld */
|
||||
volatile float fsub = 0x1p-149f; /* smallest subnormal float */
|
||||
|
||||
check(round(0.0) == 0.0 && !signbit(round(0.0)), "round(+0.0) == +0.0");
|
||||
check(round(negzero) == 0.0 && signbit(round(negzero)), "round(-0.0) == -0.0");
|
||||
check(round(0.5) == 1.0, "round(0.5) == 1.0");
|
||||
check(round(-0.5) == -1.0, "round(-0.5) == -1.0");
|
||||
check(round(1.5) == 2.0, "round(1.5) == 2.0");
|
||||
check(round(-1.5) == -2.0, "round(-1.5) == -2.0");
|
||||
check(round(2.5) == 3.0, "round(2.5) == 3.0");
|
||||
check(round(-2.5) == -3.0, "round(-2.5) == -3.0");
|
||||
check(round(2.4) == 2.0, "round(2.4) == 2.0");
|
||||
check(round(2.6) == 3.0, "round(2.6) == 3.0");
|
||||
check(round(-2.4) == -2.0, "round(-2.4) == -2.0");
|
||||
check(round(-2.6) == -3.0, "round(-2.6) == -3.0");
|
||||
check(round(sub) == 0.0 && !signbit(round(sub)), "round(5e-324) == +0.0");
|
||||
check(round(-sub) == 0.0 && signbit(round(-sub)), "round(-5e-324) == -0.0");
|
||||
check(round(big) == big, "round(1e300) == 1e300");
|
||||
check(roundf(0.5f) == 1.0f, "roundf(0.5f) == 1.0f");
|
||||
check(roundf(-0.5f) == -1.0f, "roundf(-0.5f) == -1.0f");
|
||||
check(roundf(1.5f) == 2.0f, "roundf(1.5f) == 2.0f");
|
||||
check(roundf(-2.5f) == -3.0f, "roundf(-2.5f) == -3.0f");
|
||||
check(roundf(fsub) == 0.0f && !signbit(roundf(fsub)), "roundf(1e-45f) == +0.0f");
|
||||
check(roundf(-fsub) == 0.0f && signbit(roundf(-fsub)), "roundf(-1e-45f) == -0.0f");
|
||||
check(roundf(4194304.5f) == 4194305.0f, "roundf(2^22 + 0.5f) == 2^22 + 1f");
|
||||
check(roundl(0.5L) == 1.0L, "roundl(0.5L) == 1.0L");
|
||||
check(roundl(-0.5L) == -1.0L, "roundl(-0.5L) == -1.0L");
|
||||
check(roundl(2.5L) == 3.0L, "roundl(2.5L) == 3.0L");
|
||||
check(roundl(-2.5L) == -3.0L, "roundl(-2.5L) == -3.0L");
|
||||
check(roundl(ldsub) == 0.0L && !signbit(roundl(ldsub)), "roundl(min subnormal) == +0.0L");
|
||||
check(roundl(-ldsub) == 0.0L && signbit(roundl(-ldsub)), "roundl(-min subnormal) == -0.0L");
|
||||
}
|
||||
|
||||
/* 2. rint: round to nearest, ties to even. */
|
||||
static void
|
||||
rint_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
volatile double sub = 0x1p-1074;
|
||||
volatile double big = 1e300;
|
||||
volatile double half_int = 0x1p52 + 0.5; /* 2^52 + 0.5, tie to even */
|
||||
volatile long double ldsub = 0x1p-16445L;
|
||||
volatile float fsub = 0x1p-149f;
|
||||
|
||||
check(rint(0.0) == 0.0 && !signbit(rint(0.0)), "rint(+0.0) == +0.0");
|
||||
check(rint(negzero) == 0.0 && signbit(rint(negzero)), "rint(-0.0) == -0.0");
|
||||
check(rint(0.5) == 0.0 && !signbit(rint(0.5)), "rint(0.5) == +0.0 (tie to even)");
|
||||
check(rint(-0.5) == 0.0 && signbit(rint(-0.5)), "rint(-0.5) == -0.0 (tie to even)");
|
||||
check(rint(1.5) == 2.0, "rint(1.5) == 2.0");
|
||||
check(rint(-1.5) == -2.0, "rint(-1.5) == -2.0");
|
||||
check(rint(2.5) == 2.0, "rint(2.5) == 2.0 (tie to even)");
|
||||
check(rint(-2.5) == -2.0, "rint(-2.5) == -2.0 (tie to even)");
|
||||
check(rint(2.4) == 2.0, "rint(2.4) == 2.0");
|
||||
check(rint(2.6) == 3.0, "rint(2.6) == 3.0");
|
||||
check(rint(-2.6) == -3.0, "rint(-2.6) == -3.0");
|
||||
check(rint(sub) == 0.0 && !signbit(rint(sub)), "rint(5e-324) == +0.0");
|
||||
check(rint(-sub) == 0.0 && signbit(rint(-sub)), "rint(-5e-324) == -0.0");
|
||||
check(rint(big) == big, "rint(1e300) == 1e300");
|
||||
check(rint(half_int) == 0x1p52, "rint(2^52 + 0.5) == 2^52 (tie to even)");
|
||||
check(rint(-half_int) == -(0x1p52), "rint(-(2^52 + 0.5)) == -2^52");
|
||||
check(rintf(0.5f) == 0.0f && !signbit(rintf(0.5f)), "rintf(0.5f) == +0.0f");
|
||||
check(rintf(-0.5f) == 0.0f && signbit(rintf(-0.5f)), "rintf(-0.5f) == -0.0f");
|
||||
check(rintf(1.5f) == 2.0f, "rintf(1.5f) == 2.0f");
|
||||
check(rintf(2.5f) == 2.0f, "rintf(2.5f) == 2.0f (tie to even)");
|
||||
check(rintf(-2.5f) == -2.0f, "rintf(-2.5f) == -2.0f");
|
||||
check(rintf(4194304.5f) == 4194304.0f, "rintf(2^22 + 0.5f) == 2^22f (tie to even)");
|
||||
check(rintf(fsub) == 0.0f && !signbit(rintf(fsub)), "rintf(1e-45f) == +0.0f");
|
||||
check(rintl(0.5L) == 0.0L && !signbit(rintl(0.5L)), "rintl(0.5L) == +0.0L");
|
||||
check(rintl(-0.5L) == 0.0L && signbit(rintl(-0.5L)), "rintl(-0.5L) == -0.0L");
|
||||
check(rintl(1.5L) == 2.0L, "rintl(1.5L) == 2.0L");
|
||||
check(rintl(2.5L) == 2.0L, "rintl(2.5L) == 2.0L (tie to even)");
|
||||
check(rintl(-2.5L) == -2.0L, "rintl(-2.5L) == -2.0L");
|
||||
check(rintl(ldsub) == 0.0L && !signbit(rintl(ldsub)), "rintl(min subnormal) == +0.0L");
|
||||
}
|
||||
|
||||
/* 3. nearbyint: same nearest-even rounding, exception-free entry. */
|
||||
static void
|
||||
nearbyint_scenario(void)
|
||||
{
|
||||
volatile double negzero = -0.0;
|
||||
volatile double sub = 0x1p-1074;
|
||||
volatile long double ldsub = 0x1p-16445L;
|
||||
volatile float fsub = 0x1p-149f;
|
||||
|
||||
check(nearbyint(0.0) == 0.0 && !signbit(nearbyint(0.0)), "nearbyint(+0.0) == +0.0");
|
||||
check(nearbyint(negzero) == 0.0 && signbit(nearbyint(negzero)), "nearbyint(-0.0) == -0.0");
|
||||
check(nearbyint(0.5) == 0.0 && !signbit(nearbyint(0.5)), "nearbyint(0.5) == +0.0");
|
||||
check(nearbyint(-0.5) == 0.0 && signbit(nearbyint(-0.5)), "nearbyint(-0.5) == -0.0");
|
||||
check(nearbyint(1.5) == 2.0, "nearbyint(1.5) == 2.0");
|
||||
check(nearbyint(2.5) == 2.0, "nearbyint(2.5) == 2.0 (tie to even)");
|
||||
check(nearbyint(-2.5) == -2.0, "nearbyint(-2.5) == -2.0");
|
||||
check(nearbyint(2.4) == 2.0, "nearbyint(2.4) == 2.0");
|
||||
check(nearbyint(2.6) == 3.0, "nearbyint(2.6) == 3.0");
|
||||
check(nearbyint(-2.6) == -3.0, "nearbyint(-2.6) == -3.0");
|
||||
check(nearbyint(sub) == 0.0 && !signbit(nearbyint(sub)), "nearbyint(5e-324) == +0.0");
|
||||
check(nearbyint(-sub) == 0.0 && signbit(nearbyint(-sub)), "nearbyint(-5e-324) == -0.0");
|
||||
check(nearbyintf(0.5f) == 0.0f, "nearbyintf(0.5f) == +0.0f");
|
||||
check(nearbyintf(2.5f) == 2.0f, "nearbyintf(2.5f) == 2.0f");
|
||||
check(nearbyintf(-0.5f) == 0.0f && signbit(nearbyintf(-0.5f)), "nearbyintf(-0.5f) == -0.0f");
|
||||
check(nearbyintf(fsub) == 0.0f, "nearbyintf(1e-45f) == +0.0f");
|
||||
check(nearbyintl(0.5L) == 0.0L, "nearbyintl(0.5L) == +0.0L");
|
||||
check(nearbyintl(-0.5L) == 0.0L && signbit(nearbyintl(-0.5L)), "nearbyintl(-0.5L) == -0.0L");
|
||||
check(nearbyintl(2.5L) == 2.0L, "nearbyintl(2.5L) == 2.0L");
|
||||
check(nearbyintl(-2.5L) == -2.0L, "nearbyintl(-2.5L) == -2.0L");
|
||||
check(nearbyintl(ldsub) == 0.0L, "nearbyintl(min subnormal) == +0.0L");
|
||||
}
|
||||
|
||||
/* 4. lrint/llrint: nearest rounding narrowed to long / long long. */
|
||||
static void
|
||||
lrint_scenario(void)
|
||||
{
|
||||
volatile double two40 = 0x1p40; /* 1099511627776.0, exact in long */
|
||||
|
||||
check(lrint(0.0) == 0L, "lrint(0.0) == 0");
|
||||
check(lrint(1.0) == 1L, "lrint(1.0) == 1");
|
||||
check(lrint(1.5) == 2L, "lrint(1.5) == 2");
|
||||
check(lrint(2.5) == 2L, "lrint(2.5) == 2 (tie to even)");
|
||||
check(lrint(-1.5) == -2L, "lrint(-1.5) == -2");
|
||||
check(lrint(-2.5) == -2L, "lrint(-2.5) == -2");
|
||||
check(lrint(0.5) == 0L, "lrint(0.5) == 0");
|
||||
check(lrint(-0.5) == 0L, "lrint(-0.5) == 0");
|
||||
check(lrint(two40) == 1099511627776L, "lrint(2^40) == 2^40");
|
||||
check(lrintf(1.5f) == 2L, "lrintf(1.5f) == 2");
|
||||
check(lrintf(2.5f) == 2L, "lrintf(2.5f) == 2");
|
||||
check(lrintf(-1.5f) == -2L, "lrintf(-1.5f) == -2");
|
||||
check(lrintf(0.0f) == 0L, "lrintf(0.0f) == 0");
|
||||
check(lrintl(1.5L) == 2L, "lrintl(1.5L) == 2");
|
||||
check(lrintl(2.5L) == 2L, "lrintl(2.5L) == 2");
|
||||
check(lrintl(-2.5L) == -2L, "lrintl(-2.5L) == -2");
|
||||
check(lrintl(0.0L) == 0L, "lrintl(0.0L) == 0");
|
||||
check(lrintl(-1.5L) == -2L, "lrintl(-1.5L) == -2");
|
||||
}
|
||||
|
||||
static void
|
||||
llrint_scenario(void)
|
||||
{
|
||||
volatile double two40 = 0x1p40;
|
||||
|
||||
check(llrint(0.0) == 0LL, "llrint(0.0) == 0");
|
||||
check(llrint(1.0) == 1LL, "llrint(1.0) == 1");
|
||||
check(llrint(1.5) == 2LL, "llrint(1.5) == 2");
|
||||
check(llrint(2.5) == 2LL, "llrint(2.5) == 2 (tie to even)");
|
||||
check(llrint(-1.5) == -2LL, "llrint(-1.5) == -2");
|
||||
check(llrint(-2.5) == -2LL, "llrint(-2.5) == -2");
|
||||
check(llrint(two40) == 1099511627776LL, "llrint(2^40) == 2^40");
|
||||
check(llrintf(1.5f) == 2LL, "llrintf(1.5f) == 2");
|
||||
check(llrintf(-2.5f) == -2LL, "llrintf(-2.5f) == -2");
|
||||
check(llrintf(0.0f) == 0LL, "llrintf(0.0f) == 0");
|
||||
check(llrintl(2.5L) == 2LL, "llrintl(2.5L) == 2");
|
||||
check(llrintl(-1.5L) == -2LL, "llrintl(-1.5L) == -2");
|
||||
check(llrintl(1.5L) == 2LL, "llrintl(1.5L) == 2");
|
||||
}
|
||||
|
||||
/* 5. ilogb: floor(log2 |x|) with the zero and Inf/NaN sentinels. */
|
||||
static void
|
||||
ilogb_scenario(void)
|
||||
{
|
||||
volatile double nan = NAN;
|
||||
volatile double inf = HUGE_VAL;
|
||||
volatile double negzero = -0.0;
|
||||
volatile float nanf = NAN;
|
||||
volatile long double nana_l = NAN;
|
||||
volatile long double ldsub = 0x1p-16445L;
|
||||
volatile float fsub = 0x1p-149f;
|
||||
|
||||
check(ilogb(0.0) == FP_ILOGB0, "ilogb(+0.0) == FP_ILOGB0");
|
||||
check(ilogb(negzero) == FP_ILOGB0, "ilogb(-0.0) == FP_ILOGB0");
|
||||
check(ilogb(nan) == FP_ILOGBNAN, "ilogb(NaN) == FP_ILOGBNAN");
|
||||
check(ilogb(inf) == FP_ILOGBNAN, "ilogb(+Inf) == FP_ILOGBNAN");
|
||||
check(ilogb(-inf) == FP_ILOGBNAN, "ilogb(-Inf) == FP_ILOGBNAN");
|
||||
check(ilogb(1.0) == 0, "ilogb(1.0) == 0");
|
||||
check(ilogb(2.0) == 1, "ilogb(2.0) == 1");
|
||||
check(ilogb(8.0) == 3, "ilogb(8.0) == 3");
|
||||
check(ilogb(-8.0) == 3, "ilogb(-8.0) == 3");
|
||||
check(ilogb(0.5) == -1, "ilogb(0.5) == -1");
|
||||
check(ilogb(0x1p-1074) == -1074, "ilogb(5e-324) == -1074");
|
||||
check(ilogb(0x1p52) == 52, "ilogb(2^52) == 52");
|
||||
check(ilogb(0x1p53) == 53, "ilogb(2^53) == 53");
|
||||
check(ilogbf(1.0f) == 0, "ilogbf(1.0f) == 0");
|
||||
check(ilogbf(8.0f) == 3, "ilogbf(8.0f) == 3");
|
||||
check(ilogbf(0.5f) == -1, "ilogbf(0.5f) == -1");
|
||||
check(ilogbf(0.0f) == FP_ILOGB0, "ilogbf(0.0f) == FP_ILOGB0");
|
||||
check(ilogbf(nanf) == FP_ILOGBNAN, "ilogbf(NaN) == FP_ILOGBNAN");
|
||||
check(ilogbf(fsub) == -149, "ilogbf(min subnormal) == -149");
|
||||
check(ilogbl(1.0L) == 0, "ilogbl(1.0L) == 0");
|
||||
check(ilogbl(8.0L) == 3, "ilogbl(8.0L) == 3");
|
||||
check(ilogbl(0.5L) == -1, "ilogbl(0.5L) == -1");
|
||||
check(ilogbl(0.0L) == FP_ILOGB0, "ilogbl(0.0L) == FP_ILOGB0");
|
||||
check(ilogbl(nana_l) == FP_ILOGBNAN, "ilogbl(NaN) == FP_ILOGBNAN");
|
||||
check(ilogbl(ldsub) == -16445, "ilogbl(min subnormal) == -16445");
|
||||
}
|
||||
|
||||
/* 6. logb: the same exponent as a floating value. */
|
||||
static void
|
||||
logb_scenario(void)
|
||||
{
|
||||
volatile double nan = NAN;
|
||||
volatile double inf = HUGE_VAL;
|
||||
volatile double negzero = -0.0;
|
||||
volatile float nanf = NAN;
|
||||
volatile long double nana_l = NAN;
|
||||
volatile long double ldsub = 0x1p-16445L;
|
||||
volatile float fsub = 0x1p-149f;
|
||||
|
||||
check(logb(8.0) == 3.0, "logb(8.0) == 3.0");
|
||||
check(logb(-8.0) == 3.0, "logb(-8.0) == 3.0");
|
||||
check(logb(0.5) == -1.0, "logb(0.5) == -1.0");
|
||||
check(logb(0x1p-1074) == -1074.0, "logb(5e-324) == -1074.0");
|
||||
check(logb(inf) == inf, "logb(+Inf) == +Inf");
|
||||
check(logb(-inf) == inf, "logb(-Inf) == +Inf");
|
||||
check(isnan(logb(nan)), "logb(NaN) is NaN");
|
||||
check(logb(0.0) == -HUGE_VAL, "logb(+0.0) == -Inf");
|
||||
check(logb(negzero) == -HUGE_VAL, "logb(-0.0) == -Inf");
|
||||
check(logbf(8.0f) == 3.0f, "logbf(8.0f) == 3.0f");
|
||||
check(logbf(0.5f) == -1.0f, "logbf(0.5f) == -1.0f");
|
||||
check(logbf(fsub) == -149.0f, "logbf(min subnormal) == -149.0f");
|
||||
check(logbf(0.0f) == -HUGE_VALF, "logbf(0.0f) == -Inf");
|
||||
check(logbf(1.0f) == 0.0f, "logbf(1.0f) == 0.0f");
|
||||
check(isnan(logbf(nanf)), "logbf(NaN) is NaN");
|
||||
check(logbl(0.5L) == -1.0L, "logbl(0.5L) == -1.0L");
|
||||
check(logbl(ldsub) == -16445.0L, "logbl(min subnormal) == -16445.0L");
|
||||
check(logbl(0.0L) == -HUGE_VALL, "logbl(0.0L) == -Inf");
|
||||
check(logbl(1.0L) == 0.0L, "logbl(1.0L) == 0.0L");
|
||||
check(logbl(8.0L) == 3.0L, "logbl(8.0L) == 3.0L");
|
||||
check(isnan(logbl(nana_l)), "logbl(NaN) is NaN");
|
||||
}
|
||||
|
||||
/* 7. NaN and ±Inf passthrough for the three rounding families. */
|
||||
static void
|
||||
special_scenario(void)
|
||||
{
|
||||
volatile double nan = NAN;
|
||||
volatile double inf = HUGE_VAL;
|
||||
volatile float nanf = NAN;
|
||||
volatile long double nana_l = NAN;
|
||||
volatile long double inf_l = HUGE_VALL;
|
||||
|
||||
check(isnan(round(nan)), "round(NaN) is NaN");
|
||||
check(isnan(rint(nan)), "rint(NaN) is NaN");
|
||||
check(isnan(nearbyint(nan)), "nearbyint(NaN) is NaN");
|
||||
check(round(inf) == inf, "round(+Inf) == +Inf");
|
||||
check(round(-inf) == -inf, "round(-Inf) == -Inf");
|
||||
check(rint(inf) == inf, "rint(+Inf) == +Inf");
|
||||
check(rint(-inf) == -inf, "rint(-Inf) == -Inf");
|
||||
check(nearbyint(inf) == inf, "nearbyint(+Inf) == +Inf");
|
||||
check(nearbyint(-inf) == -inf, "nearbyint(-Inf) == -Inf");
|
||||
check(isnan(roundf(nanf)), "roundf(NaN) is NaN");
|
||||
check(isnan(rintf(nanf)), "rintf(NaN) is NaN");
|
||||
check(isnan(nearbyintf(nanf)), "nearbyintf(NaN) is NaN");
|
||||
check(isnan(roundl(nana_l)), "roundl(NaN) is NaN");
|
||||
check(isnan(rintl(nana_l)), "rintl(NaN) is NaN");
|
||||
check(isnan(nearbyintl(nana_l)), "nearbyintl(NaN) is NaN");
|
||||
check(roundl(inf_l) == inf_l, "roundl(+Inf) == +Inf");
|
||||
check(rintl(-inf_l) == -inf_l, "rintl(-Inf) == -Inf");
|
||||
check(nearbyintl(inf_l) == inf_l, "nearbyintl(+Inf) == +Inf");
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
round_scenario();
|
||||
rint_scenario();
|
||||
nearbyint_scenario();
|
||||
lrint_scenario();
|
||||
llrint_scenario();
|
||||
ilogb_scenario();
|
||||
logb_scenario();
|
||||
special_scenario();
|
||||
|
||||
if (failures == 0)
|
||||
{
|
||||
say(1, "all math slice S2 tests passed\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAILURES\n");
|
||||
}
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user