diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2019-02-21 16:14:04 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2019-02-21 16:14:04 +0000 |
commit | e748fe448836c9e122554e2a260757858c4200f6 (patch) | |
tree | 00213154f22ad87829a781870f95e5376d6fba4b /regress/lib | |
parent | 511a45b585bf7bd6ed03dcbbd07166296d098c7f (diff) |
Copy tests for our libm implementation from FreeBSD. Only passing
tests are included, the others need some fixes in the library first.
Hopefully these tests will help us to find compiler bugs earlier.
from Moritz Buhl
Diffstat (limited to 'regress/lib')
-rw-r--r-- | regress/lib/libm/msun/Makefile | 19 | ||||
-rw-r--r-- | regress/lib/libm/msun/conj_test.c | 140 | ||||
-rw-r--r-- | regress/lib/libm/msun/fenv_test.c | 564 | ||||
-rw-r--r-- | regress/lib/libm/msun/lrint_test.c | 150 | ||||
-rw-r--r-- | regress/lib/libm/msun/test-utils.h | 183 |
5 files changed, 1056 insertions, 0 deletions
diff --git a/regress/lib/libm/msun/Makefile b/regress/lib/libm/msun/Makefile new file mode 100644 index 00000000000..f3ce5e0cf8a --- /dev/null +++ b/regress/lib/libm/msun/Makefile @@ -0,0 +1,19 @@ +# $OpenBSD: Makefile,v 1.1.1.1 2019/02/21 16:14:03 bluhm Exp $ + +TESTS = +TESTS += conj_test +TESTS += fenv_test +TESTS += lrint_test + +PROGS= ${TESTS} +LDADD= -lm +DPADD= ${LIBM} + +.for t in ${TESTS} +REGRESS_TARGETS+= run-$t +run-$t: $t + @echo "\n======== $@ ========" + ./$t +.endfor + +.include <bsd.regress.mk> diff --git a/regress/lib/libm/msun/conj_test.c b/regress/lib/libm/msun/conj_test.c new file mode 100644 index 00000000000..40395abfe52 --- /dev/null +++ b/regress/lib/libm/msun/conj_test.c @@ -0,0 +1,140 @@ +/* $OpenBSD: conj_test.c,v 1.1 2019/02/21 16:14:03 bluhm Exp $ */ +/*- + * Copyright (c) 2008 David Schultz <das@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Tests for conj{,f,l}() + */ + +#include <sys/cdefs.h> +/* $FreeBSD: head/lib/msun/tests/conj_test.c 314650 2017-03-04 10:07:46Z ngie $ */ + +#include <assert.h> +#include <complex.h> +#include <fenv.h> +#include <math.h> +#include <stdio.h> + +#include "test-utils.h" + +#pragma STDC CX_LIMITED_RANGE OFF + +/* Make sure gcc doesn't use builtin versions of these or honor __pure2. */ +static float complex (*libconjf)(float complex) = conjf; +static double complex (*libconj)(double complex) = conj; +static long double complex (*libconjl)(long double complex) = conjl; +static float (*libcrealf)(float complex) = crealf; +static double (*libcreal)(double complex) = creal; +static long double (*libcreall)(long double complex) = creall; +static float (*libcimagf)(float complex) = cimagf; +static double (*libcimag)(double complex) = cimag; +static long double (*libcimagl)(long double complex) = cimagl; + +static const double tests[] = { + /* a + bI */ + 0.0, 0.0, + 0.0, 1.0, + 1.0, 0.0, + -1.0, 0.0, + 1.0, -0.0, + 0.0, -1.0, + 2.0, 4.0, + 0.0, INFINITY, + 0.0, -INFINITY, + INFINITY, 0.0, + NAN, 1.0, + 1.0, NAN, + NAN, NAN, + -INFINITY, INFINITY, +}; + +int +main(void) +{ + static const int ntests = sizeof(tests) / sizeof(tests[0]) / 2; + complex float in; + complex long double expected; + int i; + + printf("1..%d\n", ntests * 3); + + for (i = 0; i < ntests; i++) { + __real__ expected = __real__ in = tests[2 * i]; + __imag__ in = tests[2 * i + 1]; + __imag__ expected = -cimag(in); + + assert(fpequal(libcrealf(in), __real__ in)); + assert(fpequal(libcreal(in), __real__ in)); + assert(fpequal(libcreall(in), __real__ in)); + assert(fpequal(libcimagf(in), __imag__ in)); + assert(fpequal(libcimag(in), __imag__ in)); + assert(fpequal(libcimagl(in), __imag__ in)); + + feclearexcept(FE_ALL_EXCEPT); + if (!cfpequal(libconjf(in), expected)) { + printf("not ok %d\t# conjf(%#.2g + %#.2gI): " + "wrong value\n", + 3 * i + 1, creal(in), cimag(in)); + } else if (fetestexcept(FE_ALL_EXCEPT)) { + printf("not ok %d\t# conjf(%#.2g + %#.2gI): " + "threw an exception\n", + 3 * i + 1, creal(in), cimag(in)); + } else { + printf("ok %d\t\t# conjf(%#.2g + %#.2gI)\n", + 3 * i + 1, creal(in), cimag(in)); + } + + feclearexcept(FE_ALL_EXCEPT); + if (!cfpequal(libconj(in), expected)) { + printf("not ok %d\t# conj(%#.2g + %#.2gI): " + "wrong value\n", + 3 * i + 2, creal(in), cimag(in)); + } else if (fetestexcept(FE_ALL_EXCEPT)) { + printf("not ok %d\t# conj(%#.2g + %#.2gI): " + "threw an exception\n", + 3 * i + 2, creal(in), cimag(in)); + } else { + printf("ok %d\t\t# conj(%#.2g + %#.2gI)\n", + 3 * i + 2, creal(in), cimag(in)); + } + + feclearexcept(FE_ALL_EXCEPT); + if (!cfpequal(libconjl(in), expected)) { + printf("not ok %d\t# conjl(%#.2g + %#.2gI): " + "wrong value\n", + 3 * i + 3, creal(in), cimag(in)); + } else if (fetestexcept(FE_ALL_EXCEPT)) { + printf("not ok %d\t# conjl(%#.2g + %#.2gI): " + "threw an exception\n", + 3 * i + 3, creal(in), cimag(in)); + } else { + printf("ok %d\t\t# conjl(%#.2g + %#.2gI)\n", + 3 * i + 3, creal(in), cimag(in)); + } + } + + return (0); +} diff --git a/regress/lib/libm/msun/fenv_test.c b/regress/lib/libm/msun/fenv_test.c new file mode 100644 index 00000000000..adbafa356f8 --- /dev/null +++ b/regress/lib/libm/msun/fenv_test.c @@ -0,0 +1,564 @@ +/* $OpenBSD: fenv_test.c,v 1.1 2019/02/21 16:14:03 bluhm Exp $ */ +/*- + * Copyright (c) 2004 David Schultz <das@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Test the correctness and C99-compliance of various fenv.h features. + */ + +#include <sys/cdefs.h> +/* $FreeBSD: head/lib/msun/tests/fenv_test.c 314650 2017-03-04 10:07:46Z ngie $ */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <assert.h> +#include <err.h> +#include <fenv.h> +#include <float.h> +#include <math.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +/* + * Implementations are permitted to define additional exception flags + * not specified in the standard, so it is not necessarily true that + * FE_ALL_EXCEPT == ALL_STD_EXCEPT. + */ +#define ALL_STD_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \ + FE_OVERFLOW | FE_UNDERFLOW) + +#define NEXCEPTS (sizeof(std_excepts) / sizeof(std_excepts[0])) + +static const int std_excepts[] = { + FE_INVALID, + FE_DIVBYZERO, + FE_OVERFLOW, + FE_UNDERFLOW, + FE_INEXACT, +}; + +/* init_exceptsets() initializes this to the power set of std_excepts[] */ +static int std_except_sets[1 << NEXCEPTS]; + +#pragma STDC FENV_ACCESS ON + +/* + * Initialize std_except_sets[] to the power set of std_excepts[] + */ +static void +init_exceptsets(void) +{ + unsigned i, j, sr; + + for (i = 0; i < 1 << NEXCEPTS; i++) { + for (sr = i, j = 0; sr != 0; sr >>= 1, j++) + std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1); + } +} + +/* + * Raise a floating-point exception without relying on the standard + * library routines, which we are trying to test. + * + * XXX We can't raise an {over,under}flow without also raising an + * inexact exception. + */ +static void +raiseexcept(int excepts) +{ + volatile double d; + + /* + * With a compiler that supports the FENV_ACCESS pragma + * properly, simple expressions like '0.0 / 0.0' should + * be sufficient to generate traps. Unfortunately, we + * need to bring a volatile variable into the equation + * to prevent incorrect optimizations. + */ + if (excepts & FE_INVALID) { + d = 0.0; + d = 0.0 / d; + } + if (excepts & FE_DIVBYZERO) { + d = 0.0; + d = 1.0 / d; + } + if (excepts & FE_OVERFLOW) { + d = DBL_MAX; + d *= 2.0; + } + if (excepts & FE_UNDERFLOW) { + d = DBL_MIN; + d /= DBL_MAX; + } + if (excepts & FE_INEXACT) { + d = DBL_MIN; + d += 1.0; + } + + /* + * On the x86 (and some other architectures?) the FPU and + * integer units are decoupled. We need to execute an FWAIT + * or a floating-point instruction to get synchronous exceptions. + */ + d = 1.0; + d += 1.0; +} + +/* + * Determine the current rounding mode without relying on the fenv + * routines. This function may raise an inexact exception. + */ +static int +getround(void) +{ + volatile double d; + + /* + * This test works just as well with 0.0 - 0.0, except on ia64 + * where 0.0 - 0.0 gives the wrong sign when rounding downwards. + */ + d = 1.0; + d -= 1.0; + if (copysign(1.0, d) < 0.0) + return (FE_DOWNWARD); + + d = 1.0; + if (d + (DBL_EPSILON * 3.0 / 4.0) == 1.0) + return (FE_TOWARDZERO); + if (d + (DBL_EPSILON * 1.0 / 4.0) > 1.0) + return (FE_UPWARD); + + return (FE_TONEAREST); +} + +static void +trap_handler(int sig) +{ + + assert(sig == SIGFPE); + _exit(0); +} + +/* + * This tests checks the default FP environment, so it must be first. + * The memcmp() test below may be too much to ask for, since there + * could be multiple machine-specific default environments. + */ +static void +test_dfl_env(void) +{ +#ifndef NO_STRICT_DFL_ENV + fenv_t env; + + fegetenv(&env); + +#ifdef __amd64__ + /* + * Compare the fields that the AMD [1] and Intel [2] specs say will be + * set once fnstenv returns. + * + * Not all amd64 capable processors implement the fnstenv instruction + * by zero'ing out the env.__x87.__other field (example: AMD Opteron + * 6308). The AMD64/x64 specs aren't explicit on what the + * env.__x87.__other field will contain after fnstenv is executed, so + * the values in env.__x87.__other could be filled with arbitrary + * data depending on how the CPU implements fnstenv. + * + * 1. http://support.amd.com/TechDocs/26569_APM_v5.pdf + * 2. http://www.intel.com/Assets/en_US/PDF/manual/253666.pdf + */ + assert(memcmp(&env.__mxcsr, &FE_DFL_ENV->__mxcsr, + sizeof(env.__mxcsr)) == 0); + assert(memcmp(&env.__x87.__control, &FE_DFL_ENV->__x87.__control, + sizeof(env.__x87.__control)) == 0); + assert(memcmp(&env.__x87.__status, &FE_DFL_ENV->__x87.__status, + sizeof(env.__x87.__status)) == 0); + assert(memcmp(&env.__x87.__tag, &FE_DFL_ENV->__x87.__tag, + sizeof(env.__x87.__tag)) == 0); +#else + assert(memcmp(&env, FE_DFL_ENV, sizeof(env)) == 0); +#endif + +#endif + assert(fetestexcept(FE_ALL_EXCEPT) == 0); +} + +/* + * Test fetestexcept() and feclearexcept(). + */ +static void +test_fetestclearexcept(void) +{ + int excepts, i; + + for (i = 0; i < 1 << NEXCEPTS; i++) + assert(fetestexcept(std_except_sets[i]) == 0); + for (i = 0; i < 1 << NEXCEPTS; i++) { + excepts = std_except_sets[i]; + + /* FE_ALL_EXCEPT might be special-cased, as on i386. */ + raiseexcept(excepts); + assert(fetestexcept(excepts) == excepts); + assert(feclearexcept(FE_ALL_EXCEPT) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + + raiseexcept(excepts); + assert(fetestexcept(excepts) == excepts); + if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) { + excepts |= FE_INEXACT; + assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) == + excepts); + } else { + assert(fetestexcept(ALL_STD_EXCEPT) == excepts); + } + assert(feclearexcept(excepts) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + } +} + +/* + * Test fegetexceptflag() and fesetexceptflag(). + * + * Prerequisites: fetestexcept(), feclearexcept() + */ +static void +test_fegsetexceptflag(void) +{ + fexcept_t flag; + int excepts, i; + + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + for (i = 0; i < 1 << NEXCEPTS; i++) { + excepts = std_except_sets[i]; + + assert(fegetexceptflag(&flag, excepts) == 0); + raiseexcept(ALL_STD_EXCEPT); + assert(fesetexceptflag(&flag, excepts) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == + (ALL_STD_EXCEPT ^ excepts)); + + assert(fegetexceptflag(&flag, FE_ALL_EXCEPT) == 0); + assert(feclearexcept(FE_ALL_EXCEPT) == 0); + assert(fesetexceptflag(&flag, excepts) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == 0); + assert(fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == + (ALL_STD_EXCEPT ^ excepts)); + + assert(feclearexcept(FE_ALL_EXCEPT) == 0); + } +} + +/* + * Test feraiseexcept(). + * + * Prerequisites: fetestexcept(), feclearexcept() + */ +static void +test_feraiseexcept(void) +{ + int excepts, i; + + for (i = 0; i < 1 << NEXCEPTS; i++) { + excepts = std_except_sets[i]; + + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + assert(feraiseexcept(excepts) == 0); + if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) { + excepts |= FE_INEXACT; + assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) == + excepts); + } else { + assert(fetestexcept(ALL_STD_EXCEPT) == excepts); + } + assert(feclearexcept(FE_ALL_EXCEPT) == 0); + } + assert(feraiseexcept(FE_INVALID | FE_DIVBYZERO) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == (FE_INVALID | FE_DIVBYZERO)); + assert(feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == ALL_STD_EXCEPT); + assert(feclearexcept(FE_ALL_EXCEPT) == 0); +} + +/* + * Test fegetround() and fesetround(). + */ +static void +test_fegsetround(void) +{ + + assert(fegetround() == FE_TONEAREST); + assert(getround() == FE_TONEAREST); + assert(FLT_ROUNDS == 1); + + assert(fesetround(FE_DOWNWARD) == 0); + assert(fegetround() == FE_DOWNWARD); + assert(getround() == FE_DOWNWARD); + assert(FLT_ROUNDS == 3); + + assert(fesetround(FE_UPWARD) == 0); + assert(getround() == FE_UPWARD); + assert(fegetround() == FE_UPWARD); + assert(FLT_ROUNDS == 2); + + assert(fesetround(FE_TOWARDZERO) == 0); + assert(getround() == FE_TOWARDZERO); + assert(fegetround() == FE_TOWARDZERO); + assert(FLT_ROUNDS == 0); + + assert(fesetround(FE_TONEAREST) == 0); + assert(getround() == FE_TONEAREST); + assert(FLT_ROUNDS == 1); + + assert(feclearexcept(FE_ALL_EXCEPT) == 0); +} + +/* + * Test fegetenv() and fesetenv(). + * + * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround() + */ +static void +test_fegsetenv(void) +{ + fenv_t env1, env2; + int excepts, i; + + for (i = 0; i < 1 << NEXCEPTS; i++) { + excepts = std_except_sets[i]; + + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + assert(fegetround() == FE_TONEAREST); + assert(fegetenv(&env1) == 0); + + /* + * fe[gs]etenv() should be able to save and restore + * exception flags without the spurious inexact + * exceptions that afflict raiseexcept(). + */ + raiseexcept(excepts); + if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 && + (excepts & FE_INEXACT) == 0) + assert(feclearexcept(FE_INEXACT) == 0); + + fesetround(FE_DOWNWARD); + assert(fegetenv(&env2) == 0); + assert(fesetenv(&env1) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + assert(fegetround() == FE_TONEAREST); + + assert(fesetenv(&env2) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == excepts); + assert(fegetround() == FE_DOWNWARD); + assert(fesetenv(&env1) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + assert(fegetround() == FE_TONEAREST); + } +} + +/* + * Test fegetexcept(), fedisableexcept(), and feenableexcept(). + * + * Prerequisites: fetestexcept(), feraiseexcept() + */ +static void +test_masking(void) +{ + struct sigaction act; + int except, pass, raise, status; + unsigned i; + + assert((fegetexcept() & ALL_STD_EXCEPT) == 0); + assert((feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT) == 0); + assert((feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT) == + (FE_INVALID | FE_OVERFLOW)); + assert((fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT) == + (FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW)); + assert((fegetexcept() & ALL_STD_EXCEPT) == (FE_INVALID | FE_UNDERFLOW)); + assert((fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT) == + (FE_INVALID | FE_UNDERFLOW)); + assert((fegetexcept() & ALL_STD_EXCEPT) == 0); + + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = trap_handler; + for (pass = 0; pass < 2; pass++) { + for (i = 0; i < NEXCEPTS; i++) { + except = std_excepts[i]; + /* over/underflow may also raise inexact */ + if (except == FE_INEXACT) + raise = FE_DIVBYZERO | FE_INVALID; + else + raise = ALL_STD_EXCEPT ^ except; + + /* + * We need to fork a child process because + * there isn't a portable way to recover from + * a floating-point exception. + */ + switch(fork()) { + case 0: /* child */ + assert((fegetexcept() & ALL_STD_EXCEPT) == 0); + assert((feenableexcept(except) + & ALL_STD_EXCEPT) == 0); + assert(fegetexcept() == except); + raiseexcept(raise); + assert(feraiseexcept(raise) == 0); + assert(fetestexcept(ALL_STD_EXCEPT) == raise); + + assert(sigaction(SIGFPE, &act, NULL) == 0); + switch (pass) { + case 0: + raiseexcept(except); + case 1: + feraiseexcept(except); + default: + assert(0); + } + assert(0); + default: /* parent */ + assert(wait(&status) > 0); + /* + * Avoid assert() here so that it's possible + * to examine a failed child's core dump. + */ + if (!WIFEXITED(status)) + errx(1, "child aborted\n"); + assert(WEXITSTATUS(status) == 0); + break; + case -1: /* error */ + assert(0); + } + } + } + assert(fetestexcept(FE_ALL_EXCEPT) == 0); +} + +/* + * Test feholdexcept() and feupdateenv(). + * + * Prerequisites: fetestexcept(), fegetround(), fesetround(), + * fedisableexcept(), feenableexcept() + */ +static void +test_feholdupdate(void) +{ + fenv_t env; + + struct sigaction act; + int except, pass, status, raise; + unsigned i; + + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = trap_handler; + for (pass = 0; pass < 2; pass++) { + for (i = 0; i < NEXCEPTS; i++) { + except = std_excepts[i]; + /* over/underflow may also raise inexact */ + if (except == FE_INEXACT) + raise = FE_DIVBYZERO | FE_INVALID; + else + raise = ALL_STD_EXCEPT ^ except; + + /* + * We need to fork a child process because + * there isn't a portable way to recover from + * a floating-point exception. + */ + switch(fork()) { + case 0: /* child */ + /* + * We don't want to cause a fatal exception in + * the child until the second pass, so we can + * check other properties of feupdateenv(). + */ + if (pass == 1) + assert((feenableexcept(except) & + ALL_STD_EXCEPT) == 0); + raiseexcept(raise); + assert(fesetround(FE_DOWNWARD) == 0); + assert(feholdexcept(&env) == 0); + assert(fetestexcept(FE_ALL_EXCEPT) == 0); + raiseexcept(except); + assert(fesetround(FE_UPWARD) == 0); + + if (pass == 1) + assert(sigaction(SIGFPE, &act, NULL) == + 0); + assert(feupdateenv(&env) == 0); + assert(fegetround() == FE_DOWNWARD); + assert(fetestexcept(ALL_STD_EXCEPT) == + (except | raise)); + + assert(pass == 0); + _exit(0); + default: /* parent */ + assert(wait(&status) > 0); + /* + * Avoid assert() here so that it's possible + * to examine a failed child's core dump. + */ + if (!WIFEXITED(status)) + errx(1, "child aborted\n"); + assert(WEXITSTATUS(status) == 0); + break; + case -1: /* error */ + assert(0); + } + } + } + assert(fetestexcept(FE_ALL_EXCEPT) == 0); +} + +int +main(void) +{ + + printf("1..8\n"); + init_exceptsets(); + test_dfl_env(); + printf("ok 1 - fenv\n"); + test_fetestclearexcept(); + printf("ok 2 - fenv\n"); + test_fegsetexceptflag(); + printf("ok 3 - fenv\n"); + test_feraiseexcept(); + printf("ok 4 - fenv\n"); + test_fegsetround(); + printf("ok 5 - fenv\n"); + test_fegsetenv(); + printf("ok 6 - fenv\n"); + test_masking(); + printf("ok 7 - fenv\n"); + test_feholdupdate(); + printf("ok 8 - fenv\n"); + + return (0); +} diff --git a/regress/lib/libm/msun/lrint_test.c b/regress/lib/libm/msun/lrint_test.c new file mode 100644 index 00000000000..b5ff09ac8ed --- /dev/null +++ b/regress/lib/libm/msun/lrint_test.c @@ -0,0 +1,150 @@ +/* $OpenBSD: lrint_test.c,v 1.1 2019/02/21 16:14:03 bluhm Exp $ */ +/*- + * Copyright (c) 2005-2008 David Schultz <das@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Test for lrint(), lrintf(), llrint(), and llrintf(). + */ + +#include <sys/cdefs.h> +/* $FreeBSD: head/lib/msun/tests/lrint_test.c 314650 2017-03-04 10:07:46Z ngie $ */ + +#include <assert.h> +#include <fenv.h> +#include <limits.h> +#include <math.h> +#include <stdio.h> + +#ifdef __i386__ +#include <ieeefp.h> +#endif + +/* + * XXX The volatile here is to avoid gcc's bogus constant folding and work + * around the lack of support for the FENV_ACCESS pragma. + */ +#define test(func, x, result, excepts) do { \ + volatile double _d = x; \ + assert(feclearexcept(FE_ALL_EXCEPT) == 0); \ + assert((func)(_d) == (result) || fetestexcept(FE_INVALID)); \ + assert(fetestexcept(FE_ALL_EXCEPT) == (excepts)); \ +} while (0) + +#define testall(x, result, excepts) do { \ + test(lrint, x, result, excepts); \ + test(lrintf, x, result, excepts); \ + test(lrintl, x, result, excepts); \ + test(llrint, x, result, excepts); \ + test(llrintf, x, result, excepts); \ + test(llrintl, x, result, excepts); \ +} while (0) + +#define IGNORE 0 + +#pragma STDC FENV_ACCESS ON + +static void +run_tests(void) +{ + + assert(fesetround(FE_DOWNWARD) == 0); + testall(0.75, 0, FE_INEXACT); + testall(-0.5, -1, FE_INEXACT); + + assert(fesetround(FE_TONEAREST) == 0); + testall(0.0, 0, 0); + testall(0.25, 0, FE_INEXACT); + testall(0.5, 0, FE_INEXACT); + testall(-2.5, -2, FE_INEXACT); + testall(1.0, 1, 0); + testall(0x12345000p0, 0x12345000, 0); + testall(0x1234.fp0, 0x1235, FE_INEXACT); + testall(INFINITY, IGNORE, FE_INVALID); + testall(NAN, IGNORE, FE_INVALID); + +#if (LONG_MAX == 0x7fffffffl) + assert(fesetround(FE_UPWARD) == 0); + test(lrint, 0x7fffffff.8p0, IGNORE, FE_INVALID); + test(lrint, -0x80000000.4p0, -0x80000000l, FE_INEXACT); + + assert(fesetround(FE_DOWNWARD) == 0); + test(lrint, -0x80000000.8p0, IGNORE, FE_INVALID); + test(lrint, 0x80000000.0p0, IGNORE, FE_INVALID); + test(lrint, 0x7fffffff.4p0, 0x7fffffffl, FE_INEXACT); + test(lrintf, 0x80000000.0p0f, IGNORE, FE_INVALID); + test(lrintf, 0x7fffff80.0p0f, 0x7fffff80l, 0); + + assert(fesetround(FE_TOWARDZERO) == 0); + test(lrint, 0x7fffffff.8p0, 0x7fffffffl, FE_INEXACT); + test(lrint, -0x80000000.8p0, -0x80000000l, FE_INEXACT); + test(lrint, 0x80000000.0p0, IGNORE, FE_INVALID); + test(lrintf, 0x80000000.0p0f, IGNORE, FE_INVALID); + test(lrintf, 0x7fffff80.0p0f, 0x7fffff80l, 0); +#elif (LONG_MAX == 0x7fffffffffffffffll) + assert(fesetround(FE_TONEAREST) == 0); + test(lrint, 0x8000000000000000.0p0, IGNORE, FE_INVALID); + test(lrintf, 0x8000000000000000.0p0f, IGNORE, FE_INVALID); + test(lrint, 0x7ffffffffffffc00.0p0, 0x7ffffffffffffc00l, 0); + test(lrintf, 0x7fffff8000000000.0p0f, 0x7fffff8000000000l, 0); + test(lrint, -0x8000000000000800.0p0, IGNORE, FE_INVALID); + test(lrintf, -0x8000010000000000.0p0f, IGNORE, FE_INVALID); + test(lrint, -0x8000000000000000.0p0, -0x8000000000000000l, 0); + test(lrintf, -0x8000000000000000.0p0f, -0x8000000000000000l, 0); +#else +#error "Unsupported long size" +#endif + +#if (LLONG_MAX == 0x7fffffffffffffffLL) + assert(fesetround(FE_TONEAREST) == 0); + test(llrint, 0x8000000000000000.0p0, IGNORE, FE_INVALID); + test(llrintf, 0x8000000000000000.0p0f, IGNORE, FE_INVALID); + test(llrint, 0x7ffffffffffffc00.0p0, 0x7ffffffffffffc00ll, 0); + test(llrintf, 0x7fffff8000000000.0p0f, 0x7fffff8000000000ll, 0); + test(llrint, -0x8000000000000800.0p0, IGNORE, FE_INVALID); + test(llrintf, -0x8000010000000000.0p0f, IGNORE, FE_INVALID); + test(llrint, -0x8000000000000000.0p0, -0x8000000000000000ll, 0); + test(llrintf, -0x8000000000000000.0p0f, -0x8000000000000000ll, 0); +#else +#error "Unsupported long long size" +#endif +} + +int +main(void) +{ + + printf("1..1\n"); + + run_tests(); +#ifdef __i386__ + fpsetprec(FP_PE); + run_tests(); +#endif + + printf("ok 1 - lrint\n"); + + return (0); +} diff --git a/regress/lib/libm/msun/test-utils.h b/regress/lib/libm/msun/test-utils.h new file mode 100644 index 00000000000..3284b014632 --- /dev/null +++ b/regress/lib/libm/msun/test-utils.h @@ -0,0 +1,183 @@ +/* $OpenBSD: test-utils.h,v 1.1 2019/02/21 16:14:03 bluhm Exp $ +/*- + * Copyright (c) 2005-2013 David Schultz <das@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: head/lib/msun/tests/test-utils.h 314650 2017-03-04 10:07:46Z ngie $ + */ + +#ifndef _TEST_UTILS_H_ +#define _TEST_UTILS_H_ + +#include <complex.h> +#include <fenv.h> + +/* + * Implementations are permitted to define additional exception flags + * not specified in the standard, so it is not necessarily true that + * FE_ALL_EXCEPT == ALL_STD_EXCEPT. + */ +#define ALL_STD_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \ + FE_OVERFLOW | FE_UNDERFLOW) +#define OPT_INVALID (ALL_STD_EXCEPT & ~FE_INVALID) +#define OPT_INEXACT (ALL_STD_EXCEPT & ~FE_INEXACT) +#define FLT_ULP() ldexpl(1.0, 1 - FLT_MANT_DIG) +#define DBL_ULP() ldexpl(1.0, 1 - DBL_MANT_DIG) +#define LDBL_ULP() ldexpl(1.0, 1 - LDBL_MANT_DIG) + +/* + * Flags that control the behavior of various fpequal* functions. + * XXX This is messy due to merging various notions of "close enough" + * that are best suited for different functions. + * + * CS_REAL + * CS_IMAG + * CS_BOTH + * (cfpequal_cs, fpequal_tol, cfpequal_tol) Whether to check the sign of + * the real part of the result, the imaginary part, or both. + * + * FPE_ABS_ZERO + * (fpequal_tol, cfpequal_tol) If set, treats the tolerance as an absolute + * tolerance when the expected value is 0. This is useful when there is + * round-off error in the input, e.g., cos(Pi/2) ~= 0. + */ +#define CS_REAL 0x01 +#define CS_IMAG 0x02 +#define CS_BOTH (CS_REAL | CS_IMAG) +#define FPE_ABS_ZERO 0x04 + +#ifdef DEBUG +#define debug(...) printf(__VA_ARGS__) +#else +#define debug(...) (void)0 +#endif + +/* + * XXX The ancient version of gcc in the base system doesn't support CMPLXL, + * but we can fake it most of the time. + */ +#ifndef CMPLXL +static inline long double complex +CMPLXL(long double x, long double y) +{ + long double complex z; + + __real__ z = x; + __imag__ z = y; + return (z); +} +#endif + +static int fpequal(long double, long double) __used; +static int cfpequal(long double complex, long double complex) __used; +static int cfpequal_cs(long double complex, long double complex, + int) __used; +static int cfpequal_tol(long double complex, long double complex, + long double, unsigned int) __used; + +/* + * Compare d1 and d2 using special rules: NaN == NaN and +0 != -0. + * Fail an assertion if they differ. + */ +static int +fpequal(long double d1, long double d2) +{ + + if (d1 != d2) + return (isnan(d1) && isnan(d2)); + return (copysignl(1.0, d1) == copysignl(1.0, d2)); +} + +/* + * Determine whether x and y are equal, with two special rules: + * +0.0 != -0.0 + * NaN == NaN + * If checksign is 0, we compare the absolute values instead. + */ +static int +fpequal_cs(long double x, long double y, int checksign) +{ + if (isnan(x) && isnan(y)) + return (1); + if (checksign) + return (x == y && !signbit(x) == !signbit(y)); + else + return (fabsl(x) == fabsl(y)); +} + +static int +fpequal_tol(long double x, long double y, long double tol, + unsigned int flags) +{ + fenv_t env; + int ret; + + if (isnan(x) && isnan(y)) + return (1); + if (!signbit(x) != !signbit(y) && (flags & CS_BOTH)) + return (0); + if (x == y) + return (1); + if (tol == 0) + return (0); + + /* Hard case: need to check the tolerance. */ + feholdexcept(&env); + /* + * For our purposes here, if y=0, we interpret tol as an absolute + * tolerance. This is to account for roundoff in the input, e.g., + * cos(Pi/2) ~= 0. + */ + if ((flags & FPE_ABS_ZERO) && y == 0.0) + ret = fabsl(x - y) <= fabsl(tol); + else + ret = fabsl(x - y) <= fabsl(y * tol); + fesetenv(&env); + return (ret); +} + +static int +cfpequal(long double complex d1, long double complex d2) +{ + + return (fpequal(creall(d1), creall(d2)) && + fpequal(cimagl(d1), cimagl(d2))); +} + +static int +cfpequal_cs(long double complex x, long double complex y, int checksign) +{ + return (fpequal_cs(creal(x), creal(y), checksign) + && fpequal_cs(cimag(x), cimag(y), checksign)); +} + +static int +cfpequal_tol(long double complex x, long double complex y, long double tol, + unsigned int flags) +{ + return (fpequal_tol(creal(x), creal(y), tol, flags) + && fpequal_tol(cimag(x), cimag(y), tol, flags)); +} + +#endif /* _TEST_UTILS_H_ */ |