feat(math): abs/round/trunc/frexp/ldexp/scalbn/copysign/fmin/fmax/fmod
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user