xref: /openbsd/regress/lib/libm/fenv/fenv.c (revision 487aca86)
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