/* * 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 #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; }