1*487aca86Skettenis /* $OpenBSD: fenv.c,v 1.8 2021/06/17 12:55:38 kettenis Exp $ */
21924edffSmartynas
31924edffSmartynas /*-
41924edffSmartynas * Copyright (c) 2004 David Schultz <das@FreeBSD.org>
51924edffSmartynas * All rights reserved.
61924edffSmartynas *
71924edffSmartynas * Redistribution and use in source and binary forms, with or without
81924edffSmartynas * modification, are permitted provided that the following conditions
91924edffSmartynas * are met:
101924edffSmartynas * 1. Redistributions of source code must retain the above copyright
111924edffSmartynas * notice, this list of conditions and the following disclaimer.
121924edffSmartynas * 2. Redistributions in binary form must reproduce the above copyright
131924edffSmartynas * notice, this list of conditions and the following disclaimer in the
141924edffSmartynas * documentation and/or other materials provided with the distribution.
151924edffSmartynas *
161924edffSmartynas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
171924edffSmartynas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181924edffSmartynas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191924edffSmartynas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
201924edffSmartynas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211924edffSmartynas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221924edffSmartynas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231924edffSmartynas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241924edffSmartynas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251924edffSmartynas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261924edffSmartynas * SUCH DAMAGE.
271924edffSmartynas */
281924edffSmartynas
291924edffSmartynas /*
301924edffSmartynas * Test the correctness and C99-compliance of various fenv.h features.
311924edffSmartynas */
321924edffSmartynas
331924edffSmartynas #include <sys/types.h>
341924edffSmartynas #include <sys/wait.h>
351924edffSmartynas #include <assert.h>
361924edffSmartynas #include <err.h>
371924edffSmartynas #include <fenv.h>
381924edffSmartynas #include <float.h>
391924edffSmartynas #include <math.h>
401924edffSmartynas #include <signal.h>
411924edffSmartynas #include <stdio.h>
421924edffSmartynas #include <string.h>
431924edffSmartynas #include <unistd.h>
441924edffSmartynas
451924edffSmartynas /*
461924edffSmartynas * Implementations are permitted to define additional exception flags
471924edffSmartynas * not specified in the standard, so it is not necessarily true that
481924edffSmartynas * FE_ALL_EXCEPT == ALL_STD_EXCEPT.
491924edffSmartynas */
501924edffSmartynas #define ALL_STD_EXCEPT (FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \
511924edffSmartynas FE_OVERFLOW | FE_UNDERFLOW)
521924edffSmartynas
531924edffSmartynas #define NEXCEPTS (sizeof(std_excepts) / sizeof(std_excepts[0]))
541924edffSmartynas
551924edffSmartynas static const int std_excepts[] = {
561924edffSmartynas FE_INVALID,
571924edffSmartynas FE_DIVBYZERO,
581924edffSmartynas FE_OVERFLOW,
591924edffSmartynas FE_UNDERFLOW,
601924edffSmartynas FE_INEXACT,
611924edffSmartynas };
621924edffSmartynas
631924edffSmartynas /* init_exceptsets() initializes this to the power set of std_excepts[] */
641924edffSmartynas static int std_except_sets[1 << NEXCEPTS];
651924edffSmartynas
661924edffSmartynas static void init_exceptsets(void);
671924edffSmartynas
681924edffSmartynas static void test_dfl_env(void);
691924edffSmartynas static void test_fegsetenv(void);
701924edffSmartynas static void test_fegsetexceptflag(void);
711924edffSmartynas static void test_masking(void);
721924edffSmartynas static void test_fegsetround(void);
731924edffSmartynas static void test_feholdupdate(void);
741924edffSmartynas static void test_feraiseexcept(void);
751924edffSmartynas static void test_fetestclearexcept(void);
761924edffSmartynas
771924edffSmartynas static int getround(void);
781924edffSmartynas static void raiseexcept(int excepts);
791924edffSmartynas static void trap_handler(int sig);
801924edffSmartynas
811924edffSmartynas #pragma STDC FENV_ACCESS ON
821924edffSmartynas
831924edffSmartynas int
main(int argc,char * argv[])841924edffSmartynas main(int argc, char *argv[])
851924edffSmartynas {
861924edffSmartynas
871924edffSmartynas printf("1..8\n");
881924edffSmartynas init_exceptsets();
891924edffSmartynas test_dfl_env();
901924edffSmartynas printf("ok 1 - fenv\n");
911924edffSmartynas test_fetestclearexcept();
921924edffSmartynas printf("ok 2 - fenv\n");
931924edffSmartynas test_fegsetexceptflag();
941924edffSmartynas printf("ok 3 - fenv\n");
951924edffSmartynas test_feraiseexcept();
961924edffSmartynas printf("ok 4 - fenv\n");
971924edffSmartynas test_fegsetround();
981924edffSmartynas printf("ok 5 - fenv\n");
991924edffSmartynas test_fegsetenv();
1001924edffSmartynas printf("ok 6 - fenv\n");
1011924edffSmartynas test_masking();
1021924edffSmartynas printf("ok 7 - fenv\n");
1031924edffSmartynas test_feholdupdate();
1041924edffSmartynas printf("ok 8 - fenv\n");
1051924edffSmartynas
1061924edffSmartynas return (0);
1071924edffSmartynas }
1081924edffSmartynas
1091924edffSmartynas /*
1101924edffSmartynas * Initialize std_except_sets[] to the power set of std_excepts[]
1111924edffSmartynas */
1121924edffSmartynas void
init_exceptsets(void)1131924edffSmartynas init_exceptsets(void)
1141924edffSmartynas {
1151924edffSmartynas int i, j, sr;
1161924edffSmartynas
1171924edffSmartynas for (i = 0; i < 1 << NEXCEPTS; i++) {
1181924edffSmartynas for (sr = i, j = 0; sr != 0; sr >>= 1, j++)
1191924edffSmartynas std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1);
1201924edffSmartynas }
1211924edffSmartynas }
1221924edffSmartynas
1231924edffSmartynas /*
1241924edffSmartynas * This tests checks the default FP environment, so it must be first.
1251924edffSmartynas * The memcmp() test below may be too much to ask for, since there
1261924edffSmartynas * could be multiple machine-specific default environments.
1271924edffSmartynas */
1281924edffSmartynas static void
test_dfl_env(void)1291924edffSmartynas test_dfl_env(void)
1301924edffSmartynas {
1311924edffSmartynas #ifndef NO_STRICT_DFL_ENV
1321924edffSmartynas fenv_t env;
1331924edffSmartynas
1341924edffSmartynas fegetenv(&env);
1356d6884daSbluhm #ifdef __amd64
1366d6884daSbluhm /* Some early amd64 CPUs set fip+fdp for non-x87 instructions */
1376d6884daSbluhm memset(&env.__x87.__others[0], 0, 14);
1386d6884daSbluhm #endif /* __amd64 */
1391924edffSmartynas assert(memcmp(&env, FE_DFL_ENV, sizeof(env)) == 0);
1401924edffSmartynas #endif
1411924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
1421924edffSmartynas }
1431924edffSmartynas
1441924edffSmartynas /*
1451924edffSmartynas * Test fetestexcept() and feclearexcept().
1461924edffSmartynas */
1471924edffSmartynas static void
test_fetestclearexcept(void)1481924edffSmartynas test_fetestclearexcept(void)
1491924edffSmartynas {
1501924edffSmartynas int excepts, i;
1511924edffSmartynas
1521924edffSmartynas for (i = 0; i < 1 << NEXCEPTS; i++)
1531924edffSmartynas assert(fetestexcept(std_except_sets[i]) == 0);
1541924edffSmartynas for (i = 0; i < 1 << NEXCEPTS; i++) {
1551924edffSmartynas excepts = std_except_sets[i];
1561924edffSmartynas
1571924edffSmartynas /* FE_ALL_EXCEPT might be special-cased, as on i386. */
1581924edffSmartynas raiseexcept(excepts);
1591924edffSmartynas assert(fetestexcept(excepts) == excepts);
1601924edffSmartynas assert(feclearexcept(FE_ALL_EXCEPT) == 0);
1611924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
1621924edffSmartynas
1631924edffSmartynas raiseexcept(excepts);
1641924edffSmartynas assert(fetestexcept(excepts) == excepts);
1651924edffSmartynas if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
1661924edffSmartynas excepts |= FE_INEXACT;
1671924edffSmartynas assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
1681924edffSmartynas excepts);
1691924edffSmartynas } else {
1701924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
1711924edffSmartynas }
1721924edffSmartynas assert(feclearexcept(excepts) == 0);
1731924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) == 0);
1741924edffSmartynas }
1751924edffSmartynas }
1761924edffSmartynas
1771924edffSmartynas /*
1781924edffSmartynas * Test fegetexceptflag() and fesetexceptflag().
1791924edffSmartynas *
1801924edffSmartynas * Prerequisites: fetestexcept(), feclearexcept()
1811924edffSmartynas */
1821924edffSmartynas static void
test_fegsetexceptflag(void)1831924edffSmartynas test_fegsetexceptflag(void)
1841924edffSmartynas {
1851924edffSmartynas fexcept_t flag;
1861924edffSmartynas int excepts, i;
1871924edffSmartynas
1881924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
1891924edffSmartynas for (i = 0; i < 1 << NEXCEPTS; i++) {
1901924edffSmartynas excepts = std_except_sets[i];
1911924edffSmartynas
1921924edffSmartynas assert(fegetexceptflag(&flag, excepts) == 0);
1931924edffSmartynas raiseexcept(ALL_STD_EXCEPT);
1941924edffSmartynas assert(fesetexceptflag(&flag, excepts) == 0);
1951924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) ==
1961924edffSmartynas (ALL_STD_EXCEPT ^ excepts));
1971924edffSmartynas
1981924edffSmartynas assert(fegetexceptflag(&flag, FE_ALL_EXCEPT) == 0);
1991924edffSmartynas assert(feclearexcept(FE_ALL_EXCEPT) == 0);
2001924edffSmartynas assert(fesetexceptflag(&flag, excepts) == 0);
2011924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) == 0);
2021924edffSmartynas assert(fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts) == 0);
2031924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) ==
2041924edffSmartynas (ALL_STD_EXCEPT ^ excepts));
2051924edffSmartynas
2061924edffSmartynas assert(feclearexcept(FE_ALL_EXCEPT) == 0);
2071924edffSmartynas }
2081924edffSmartynas }
2091924edffSmartynas
2101924edffSmartynas /*
2111924edffSmartynas * Test feraiseexcept().
2121924edffSmartynas *
2131924edffSmartynas * Prerequisites: fetestexcept(), feclearexcept()
2141924edffSmartynas */
2151924edffSmartynas static void
test_feraiseexcept(void)2161924edffSmartynas test_feraiseexcept(void)
2171924edffSmartynas {
2181924edffSmartynas int excepts, i;
2191924edffSmartynas
2201924edffSmartynas for (i = 0; i < 1 << NEXCEPTS; i++) {
2211924edffSmartynas excepts = std_except_sets[i];
2221924edffSmartynas
2231924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
2241924edffSmartynas assert(feraiseexcept(excepts) == 0);
2251924edffSmartynas if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
2261924edffSmartynas excepts |= FE_INEXACT;
2271924edffSmartynas assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
2281924edffSmartynas excepts);
2291924edffSmartynas } else {
2301924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
2311924edffSmartynas }
2321924edffSmartynas assert(feclearexcept(FE_ALL_EXCEPT) == 0);
2331924edffSmartynas }
2341924edffSmartynas assert(feraiseexcept(FE_INVALID | FE_DIVBYZERO) == 0);
2351924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) == (FE_INVALID | FE_DIVBYZERO));
2361924edffSmartynas assert(feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) == 0);
2371924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) == ALL_STD_EXCEPT);
2381924edffSmartynas assert(feclearexcept(FE_ALL_EXCEPT) == 0);
2391924edffSmartynas }
2401924edffSmartynas
2411924edffSmartynas /*
2421924edffSmartynas * Test fegetround() and fesetround().
2431924edffSmartynas */
2441924edffSmartynas static void
test_fegsetround(void)2451924edffSmartynas test_fegsetround(void)
2461924edffSmartynas {
2471924edffSmartynas
2481924edffSmartynas assert(fegetround() == FE_TONEAREST);
2491924edffSmartynas assert(getround() == FE_TONEAREST);
2501924edffSmartynas assert(FLT_ROUNDS == 1);
2511924edffSmartynas
2521924edffSmartynas assert(fesetround(FE_DOWNWARD) == 0);
2531924edffSmartynas assert(fegetround() == FE_DOWNWARD);
2541924edffSmartynas assert(getround() == FE_DOWNWARD);
2551924edffSmartynas assert(FLT_ROUNDS == 3);
2561924edffSmartynas
2571924edffSmartynas assert(fesetround(FE_UPWARD) == 0);
2581924edffSmartynas assert(getround() == FE_UPWARD);
2591924edffSmartynas assert(fegetround() == FE_UPWARD);
2601924edffSmartynas assert(FLT_ROUNDS == 2);
2611924edffSmartynas
2621924edffSmartynas assert(fesetround(FE_TOWARDZERO) == 0);
2631924edffSmartynas assert(getround() == FE_TOWARDZERO);
2641924edffSmartynas assert(fegetround() == FE_TOWARDZERO);
2651924edffSmartynas assert(FLT_ROUNDS == 0);
2661924edffSmartynas
2671924edffSmartynas assert(fesetround(FE_TONEAREST) == 0);
2681924edffSmartynas assert(getround() == FE_TONEAREST);
2691924edffSmartynas assert(FLT_ROUNDS == 1);
2701924edffSmartynas
2711924edffSmartynas assert(feclearexcept(FE_ALL_EXCEPT) == 0);
2721924edffSmartynas }
2731924edffSmartynas
2741924edffSmartynas /*
2751924edffSmartynas * Test fegetenv() and fesetenv().
2761924edffSmartynas *
2771924edffSmartynas * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround()
2781924edffSmartynas */
2791924edffSmartynas static void
test_fegsetenv(void)2801924edffSmartynas test_fegsetenv(void)
2811924edffSmartynas {
2821924edffSmartynas fenv_t env1, env2;
2831924edffSmartynas int excepts, i;
2841924edffSmartynas
2851924edffSmartynas for (i = 0; i < 1 << NEXCEPTS; i++) {
2861924edffSmartynas excepts = std_except_sets[i];
2871924edffSmartynas
2881924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
2891924edffSmartynas assert(fegetround() == FE_TONEAREST);
2901924edffSmartynas assert(fegetenv(&env1) == 0);
2911924edffSmartynas
2921924edffSmartynas /*
2931924edffSmartynas * fe[gs]etenv() should be able to save and restore
2941924edffSmartynas * exception flags without the spurious inexact
2951924edffSmartynas * exceptions that afflict raiseexcept().
2961924edffSmartynas */
2971924edffSmartynas raiseexcept(excepts);
2981924edffSmartynas if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 &&
2991924edffSmartynas (excepts & FE_INEXACT) == 0)
3001924edffSmartynas assert(feclearexcept(FE_INEXACT) == 0);
3011924edffSmartynas
3021924edffSmartynas fesetround(FE_DOWNWARD);
3031924edffSmartynas assert(fegetenv(&env2) == 0);
3041924edffSmartynas assert(fesetenv(&env1) == 0);
3051924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
3061924edffSmartynas assert(fegetround() == FE_TONEAREST);
3071924edffSmartynas
3081924edffSmartynas assert(fesetenv(&env2) == 0);
3091924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == excepts);
3101924edffSmartynas assert(fegetround() == FE_DOWNWARD);
3111924edffSmartynas assert(fesetenv(&env1) == 0);
3121924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
3131924edffSmartynas assert(fegetround() == FE_TONEAREST);
3141924edffSmartynas }
3151924edffSmartynas }
3161924edffSmartynas
3171924edffSmartynas /*
3181924edffSmartynas * Test fegetexcept(), fedisableexcept(), and feenableexcept().
3191924edffSmartynas *
3201924edffSmartynas * Prerequisites: fetestexcept(), feraiseexcept()
3211924edffSmartynas */
3221924edffSmartynas static void
test_masking(void)3231924edffSmartynas test_masking(void)
3241924edffSmartynas {
325*487aca86Skettenis #if !defined(__arm__) && !defined(__aarch64__) && !defined(__riscv)
3261924edffSmartynas struct sigaction act;
3271924edffSmartynas int except, i, pass, raise, status;
3281924edffSmartynas
3291924edffSmartynas assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
3301924edffSmartynas assert((feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT) == 0);
3311924edffSmartynas assert((feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT) ==
3321924edffSmartynas (FE_INVALID | FE_OVERFLOW));
3331924edffSmartynas assert((fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT) ==
3341924edffSmartynas (FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW));
3351924edffSmartynas assert((fegetexcept() & ALL_STD_EXCEPT) == (FE_INVALID | FE_UNDERFLOW));
3361924edffSmartynas assert((fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT) ==
3371924edffSmartynas (FE_INVALID | FE_UNDERFLOW));
3381924edffSmartynas assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
3391924edffSmartynas
3401924edffSmartynas sigemptyset(&act.sa_mask);
3411924edffSmartynas act.sa_flags = 0;
3421924edffSmartynas act.sa_handler = trap_handler;
3431924edffSmartynas for (pass = 0; pass < 2; pass++) {
3441924edffSmartynas for (i = 0; i < NEXCEPTS; i++) {
3451924edffSmartynas except = std_excepts[i];
3461924edffSmartynas /* over/underflow may also raise inexact */
3471924edffSmartynas if (except == FE_INEXACT)
3481924edffSmartynas raise = FE_DIVBYZERO | FE_INVALID;
3491924edffSmartynas else
3501924edffSmartynas raise = ALL_STD_EXCEPT ^ except;
3511924edffSmartynas
3521924edffSmartynas /*
3531924edffSmartynas * We need to fork a child process because
3541924edffSmartynas * there isn't a portable way to recover from
3551924edffSmartynas * a floating-point exception.
3561924edffSmartynas */
3571924edffSmartynas switch(fork()) {
3581924edffSmartynas case 0: /* child */
3591924edffSmartynas assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
3601924edffSmartynas assert((feenableexcept(except)
3611924edffSmartynas & ALL_STD_EXCEPT) == 0);
3621924edffSmartynas assert(fegetexcept() == except);
3631924edffSmartynas raiseexcept(raise);
3641924edffSmartynas assert(feraiseexcept(raise) == 0);
3651924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) == raise);
3661924edffSmartynas
3671924edffSmartynas assert(sigaction(SIGFPE, &act, NULL) == 0);
3681924edffSmartynas switch (pass) {
3691924edffSmartynas case 0:
3701924edffSmartynas raiseexcept(except);
3711924edffSmartynas case 1:
3721924edffSmartynas feraiseexcept(except);
3731924edffSmartynas default:
3741924edffSmartynas assert(0);
3751924edffSmartynas }
3761924edffSmartynas assert(0);
3771924edffSmartynas default: /* parent */
3781924edffSmartynas assert(wait(&status) > 0);
3791924edffSmartynas /*
3801924edffSmartynas * Avoid assert() here so that it's possible
3811924edffSmartynas * to examine a failed child's core dump.
3821924edffSmartynas */
3831924edffSmartynas if (!WIFEXITED(status))
3841924edffSmartynas errx(1, "child aborted\n");
3851924edffSmartynas assert(WEXITSTATUS(status) == 0);
3861924edffSmartynas break;
3871924edffSmartynas case -1: /* error */
3881924edffSmartynas assert(0);
3891924edffSmartynas }
3901924edffSmartynas }
3911924edffSmartynas }
3921924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
39383dbc4bdSkettenis #endif
3941924edffSmartynas }
3951924edffSmartynas
3961924edffSmartynas /*
3971924edffSmartynas * Test feholdexcept() and feupdateenv().
3981924edffSmartynas *
3991924edffSmartynas * Prerequisites: fetestexcept(), fegetround(), fesetround(),
4001924edffSmartynas * fedisableexcept(), feenableexcept()
4011924edffSmartynas */
4021924edffSmartynas static void
test_feholdupdate(void)4031924edffSmartynas test_feholdupdate(void)
4041924edffSmartynas {
4051924edffSmartynas fenv_t env;
4061924edffSmartynas
4071924edffSmartynas struct sigaction act;
4081924edffSmartynas int except, i, pass, status, raise;
4091924edffSmartynas
4101924edffSmartynas sigemptyset(&act.sa_mask);
4111924edffSmartynas act.sa_flags = 0;
4121924edffSmartynas act.sa_handler = trap_handler;
4131924edffSmartynas for (pass = 0; pass < 2; pass++) {
4141924edffSmartynas for (i = 0; i < NEXCEPTS; i++) {
4151924edffSmartynas except = std_excepts[i];
4161924edffSmartynas /* over/underflow may also raise inexact */
4171924edffSmartynas if (except == FE_INEXACT)
4181924edffSmartynas raise = FE_DIVBYZERO | FE_INVALID;
4191924edffSmartynas else
4201924edffSmartynas raise = ALL_STD_EXCEPT ^ except;
4211924edffSmartynas
4221924edffSmartynas /*
4231924edffSmartynas * We need to fork a child process because
4241924edffSmartynas * there isn't a portable way to recover from
4251924edffSmartynas * a floating-point exception.
4261924edffSmartynas */
4271924edffSmartynas switch(fork()) {
4281924edffSmartynas case 0: /* child */
4291924edffSmartynas /*
4301924edffSmartynas * We don't want to cause a fatal exception in
4311924edffSmartynas * the child until the second pass, so we can
4321924edffSmartynas * check other properties of feupdateenv().
4331924edffSmartynas */
4341924edffSmartynas if (pass == 1)
4351924edffSmartynas assert((feenableexcept(except) &
4361924edffSmartynas ALL_STD_EXCEPT) == 0);
4371924edffSmartynas raiseexcept(raise);
4381924edffSmartynas assert(fesetround(FE_DOWNWARD) == 0);
4391924edffSmartynas assert(feholdexcept(&env) == 0);
4401924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
4411924edffSmartynas raiseexcept(except);
4421924edffSmartynas assert(fesetround(FE_UPWARD) == 0);
4431924edffSmartynas
4441924edffSmartynas if (pass == 1)
4451924edffSmartynas assert(sigaction(SIGFPE, &act, NULL) ==
4461924edffSmartynas 0);
4471924edffSmartynas assert(feupdateenv(&env) == 0);
4481924edffSmartynas assert(fegetround() == FE_DOWNWARD);
4491924edffSmartynas assert(fetestexcept(ALL_STD_EXCEPT) ==
4501924edffSmartynas (except | raise));
4511924edffSmartynas
4521924edffSmartynas assert(pass == 0);
4531924edffSmartynas _exit(0);
4541924edffSmartynas default: /* parent */
4551924edffSmartynas assert(wait(&status) > 0);
4561924edffSmartynas /*
4571924edffSmartynas * Avoid assert() here so that it's possible
4581924edffSmartynas * to examine a failed child's core dump.
4591924edffSmartynas */
4601924edffSmartynas if (!WIFEXITED(status))
4611924edffSmartynas errx(1, "child aborted\n");
4621924edffSmartynas assert(WEXITSTATUS(status) == 0);
4631924edffSmartynas break;
4641924edffSmartynas case -1: /* error */
4651924edffSmartynas assert(0);
4661924edffSmartynas }
4671924edffSmartynas }
468*487aca86Skettenis #if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
46983dbc4bdSkettenis break;
47083dbc4bdSkettenis #endif
4711924edffSmartynas }
4721924edffSmartynas assert(fetestexcept(FE_ALL_EXCEPT) == 0);
4731924edffSmartynas }
4741924edffSmartynas
4751924edffSmartynas /*
4761924edffSmartynas * Raise a floating-point exception without relying on the standard
4771924edffSmartynas * library routines, which we are trying to test.
4781924edffSmartynas *
4791924edffSmartynas * XXX We can't raise an {over,under}flow without also raising an
4801924edffSmartynas * inexact exception.
4811924edffSmartynas */
4821924edffSmartynas static void
raiseexcept(int excepts)4831924edffSmartynas raiseexcept(int excepts)
4841924edffSmartynas {
4851924edffSmartynas volatile double d;
4861924edffSmartynas
4871924edffSmartynas /*
4881924edffSmartynas * With a compiler that supports the FENV_ACCESS pragma
4891924edffSmartynas * properly, simple expressions like '0.0 / 0.0' should
4901924edffSmartynas * be sufficient to generate traps. Unfortunately, we
4911924edffSmartynas * need to bring a volatile variable into the equation
4921924edffSmartynas * to prevent incorrect optimizations.
4931924edffSmartynas */
4941924edffSmartynas if (excepts & FE_INVALID) {
4951924edffSmartynas d = 0.0;
4961924edffSmartynas d = 0.0 / d;
4971924edffSmartynas }
4981924edffSmartynas if (excepts & FE_DIVBYZERO) {
4991924edffSmartynas d = 0.0;
5001924edffSmartynas d = 1.0 / d;
5011924edffSmartynas }
5021924edffSmartynas if (excepts & FE_OVERFLOW) {
5031924edffSmartynas d = DBL_MAX;
5041924edffSmartynas d *= 2.0;
5051924edffSmartynas }
5061924edffSmartynas if (excepts & FE_UNDERFLOW) {
5071924edffSmartynas d = DBL_MIN;
5081924edffSmartynas d /= DBL_MAX;
5091924edffSmartynas }
5101924edffSmartynas if (excepts & FE_INEXACT) {
5111924edffSmartynas d = DBL_MIN;
5121924edffSmartynas d += 1.0;
5131924edffSmartynas }
5141924edffSmartynas
5151924edffSmartynas /*
5161924edffSmartynas * On the x86 (and some other architectures?) the FPU and
5171924edffSmartynas * integer units are decoupled. We need to execute an FWAIT
5181924edffSmartynas * or a floating-point instruction to get synchronous exceptions.
5191924edffSmartynas */
5201924edffSmartynas d = 1.0;
5211924edffSmartynas d += 1.0;
5221924edffSmartynas }
5231924edffSmartynas
5241924edffSmartynas /*
5251924edffSmartynas * Determine the current rounding mode without relying on the fenv
5261924edffSmartynas * routines. This function may raise an inexact exception.
5271924edffSmartynas */
5281924edffSmartynas static int
getround(void)5291924edffSmartynas getround(void)
5301924edffSmartynas {
5311d8d17b8Sbluhm volatile double d, e;
5321924edffSmartynas
5331924edffSmartynas /*
5341924edffSmartynas * This test works just as well with 0.0 - 0.0, except on ia64
5351924edffSmartynas * where 0.0 - 0.0 gives the wrong sign when rounding downwards.
5361d8d17b8Sbluhm * On i386 use two volatile variables d and e to retrieve the
5371d8d17b8Sbluhm * value out of the x87 and force rounding.
5381d8d17b8Sbluhm * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=204671
5391924edffSmartynas */
5401924edffSmartynas d = 1.0;
5411924edffSmartynas d -= 1.0;
5421d8d17b8Sbluhm e = copysign(1.0, d);
5431d8d17b8Sbluhm if (e < 0.0)
5441924edffSmartynas return (FE_DOWNWARD);
5451924edffSmartynas
5461924edffSmartynas d = 1.0;
5471d8d17b8Sbluhm e = d + (DBL_EPSILON * 3.0 / 4.0);
5481d8d17b8Sbluhm if (e == 1.0)
5491924edffSmartynas return (FE_TOWARDZERO);
5501d8d17b8Sbluhm e = d + (DBL_EPSILON * 1.0 / 4.0);
5511d8d17b8Sbluhm if (e > 1.0)
5521924edffSmartynas return (FE_UPWARD);
5531924edffSmartynas
5541924edffSmartynas return (FE_TONEAREST);
5551924edffSmartynas }
5561924edffSmartynas
5571924edffSmartynas static void
trap_handler(int sig)5581924edffSmartynas trap_handler(int sig)
5591924edffSmartynas {
5601924edffSmartynas
5611924edffSmartynas assert(sig == SIGFPE);
5621924edffSmartynas _exit(0);
5631924edffSmartynas }
564