xref: /openbsd-src/regress/lib/libm/msun/fenv_test.c (revision 498c7b5e98c82ca5a0055f676f2a6e4214e2d9a8)
1*498c7b5eSderaadt /*	$OpenBSD: fenv_test.c,v 1.7 2021/12/13 18:04:28 deraadt Exp $	*/
24c142d94Sbluhm /*-
34c142d94Sbluhm  * Copyright (c) 2004 David Schultz <das@FreeBSD.org>
44c142d94Sbluhm  * All rights reserved.
54c142d94Sbluhm  *
64c142d94Sbluhm  * Redistribution and use in source and binary forms, with or without
74c142d94Sbluhm  * modification, are permitted provided that the following conditions
84c142d94Sbluhm  * are met:
94c142d94Sbluhm  * 1. Redistributions of source code must retain the above copyright
104c142d94Sbluhm  *    notice, this list of conditions and the following disclaimer.
114c142d94Sbluhm  * 2. Redistributions in binary form must reproduce the above copyright
124c142d94Sbluhm  *    notice, this list of conditions and the following disclaimer in the
134c142d94Sbluhm  *    documentation and/or other materials provided with the distribution.
144c142d94Sbluhm  *
154c142d94Sbluhm  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
164c142d94Sbluhm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
174c142d94Sbluhm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184c142d94Sbluhm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
194c142d94Sbluhm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204c142d94Sbluhm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214c142d94Sbluhm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224c142d94Sbluhm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234c142d94Sbluhm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244c142d94Sbluhm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254c142d94Sbluhm  * SUCH DAMAGE.
264c142d94Sbluhm  */
274c142d94Sbluhm 
28c36e572eSmbuhl #include "macros.h"
29c36e572eSmbuhl 
304c142d94Sbluhm /*
314c142d94Sbluhm  * Test the correctness and C99-compliance of various fenv.h features.
324c142d94Sbluhm  */
334c142d94Sbluhm 
344c142d94Sbluhm #include <sys/types.h>
354c142d94Sbluhm #include <sys/wait.h>
364c142d94Sbluhm #include <assert.h>
374c142d94Sbluhm #include <err.h>
384c142d94Sbluhm #include <fenv.h>
394c142d94Sbluhm #include <float.h>
40c36e572eSmbuhl #ifndef __OpenBSD__
41c36e572eSmbuhl #include <libutil.h>
42c36e572eSmbuhl #endif
434c142d94Sbluhm #include <math.h>
444c142d94Sbluhm #include <signal.h>
454c142d94Sbluhm #include <stdio.h>
464c142d94Sbluhm #include <string.h>
474c142d94Sbluhm #include <unistd.h>
484c142d94Sbluhm 
49c36e572eSmbuhl #include "test-utils.h"
504c142d94Sbluhm 
514c142d94Sbluhm #define	NEXCEPTS	(sizeof(std_excepts) / sizeof(std_excepts[0]))
524c142d94Sbluhm 
534c142d94Sbluhm static const int std_excepts[] = {
544c142d94Sbluhm 	FE_INVALID,
554c142d94Sbluhm 	FE_DIVBYZERO,
564c142d94Sbluhm 	FE_OVERFLOW,
574c142d94Sbluhm 	FE_UNDERFLOW,
584c142d94Sbluhm 	FE_INEXACT,
594c142d94Sbluhm };
604c142d94Sbluhm 
614c142d94Sbluhm /* init_exceptsets() initializes this to the power set of std_excepts[] */
624c142d94Sbluhm static int std_except_sets[1 << NEXCEPTS];
634c142d94Sbluhm 
644c142d94Sbluhm #pragma STDC FENV_ACCESS ON
654c142d94Sbluhm 
664c142d94Sbluhm /*
674c142d94Sbluhm  * Initialize std_except_sets[] to the power set of std_excepts[]
684c142d94Sbluhm  */
69c36e572eSmbuhl static __attribute__((constructor)) void
do_setup(void)70c36e572eSmbuhl do_setup(void)
714c142d94Sbluhm {
724c142d94Sbluhm 	unsigned i, j, sr;
734c142d94Sbluhm 
74c36e572eSmbuhl 	/* Avoid double output after fork() */
75c36e572eSmbuhl 	setvbuf(stdout, NULL, _IONBF, 0);
76c36e572eSmbuhl 
774c142d94Sbluhm 	for (i = 0; i < 1 << NEXCEPTS; i++) {
784c142d94Sbluhm 		for (sr = i, j = 0; sr != 0; sr >>= 1, j++)
794c142d94Sbluhm 			std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1);
804c142d94Sbluhm 	}
814c142d94Sbluhm }
824c142d94Sbluhm 
834c142d94Sbluhm /*
844c142d94Sbluhm  * Raise a floating-point exception without relying on the standard
854c142d94Sbluhm  * library routines, which we are trying to test.
864c142d94Sbluhm  *
874c142d94Sbluhm  * XXX We can't raise an {over,under}flow without also raising an
884c142d94Sbluhm  * inexact exception.
894c142d94Sbluhm  */
904c142d94Sbluhm static void
raiseexcept(int excepts)914c142d94Sbluhm raiseexcept(int excepts)
924c142d94Sbluhm {
934c142d94Sbluhm 	volatile double d;
944c142d94Sbluhm 
954c142d94Sbluhm 	/*
964c142d94Sbluhm 	 * With a compiler that supports the FENV_ACCESS pragma
974c142d94Sbluhm 	 * properly, simple expressions like '0.0 / 0.0' should
984c142d94Sbluhm 	 * be sufficient to generate traps.  Unfortunately, we
994c142d94Sbluhm 	 * need to bring a volatile variable into the equation
1004c142d94Sbluhm 	 * to prevent incorrect optimizations.
1014c142d94Sbluhm 	 */
1024c142d94Sbluhm 	if (excepts & FE_INVALID) {
1034c142d94Sbluhm 		d = 0.0;
1044c142d94Sbluhm 		d = 0.0 / d;
1054c142d94Sbluhm 	}
1064c142d94Sbluhm 	if (excepts & FE_DIVBYZERO) {
1074c142d94Sbluhm 		d = 0.0;
1084c142d94Sbluhm 		d = 1.0 / d;
1094c142d94Sbluhm 	}
1104c142d94Sbluhm 	if (excepts & FE_OVERFLOW) {
1114c142d94Sbluhm 		d = DBL_MAX;
1124c142d94Sbluhm 		d *= 2.0;
1134c142d94Sbluhm 	}
1144c142d94Sbluhm 	if (excepts & FE_UNDERFLOW) {
1154c142d94Sbluhm 		d = DBL_MIN;
1164c142d94Sbluhm 		d /= DBL_MAX;
1174c142d94Sbluhm 	}
1184c142d94Sbluhm 	if (excepts & FE_INEXACT) {
1194c142d94Sbluhm 		d = DBL_MIN;
1204c142d94Sbluhm 		d += 1.0;
1214c142d94Sbluhm 	}
1224c142d94Sbluhm 
1234c142d94Sbluhm 	/*
1244c142d94Sbluhm 	 * On the x86 (and some other architectures?) the FPU and
1254c142d94Sbluhm 	 * integer units are decoupled.  We need to execute an FWAIT
1264c142d94Sbluhm 	 * or a floating-point instruction to get synchronous exceptions.
1274c142d94Sbluhm 	 */
1284c142d94Sbluhm 	d = 1.0;
1294c142d94Sbluhm 	d += 1.0;
1304c142d94Sbluhm }
1314c142d94Sbluhm 
1324c142d94Sbluhm /*
1334c142d94Sbluhm  * Determine the current rounding mode without relying on the fenv
1344c142d94Sbluhm  * routines.  This function may raise an inexact exception.
1354c142d94Sbluhm  */
1364c142d94Sbluhm static int
getround(void)1374c142d94Sbluhm getround(void)
1384c142d94Sbluhm {
139f5ab549eSbluhm 	volatile double d, e;
1404c142d94Sbluhm 
1414c142d94Sbluhm 	/*
1424c142d94Sbluhm 	 * This test works just as well with 0.0 - 0.0, except on ia64
1434c142d94Sbluhm 	 * where 0.0 - 0.0 gives the wrong sign when rounding downwards.
144f5ab549eSbluhm 	 * For ia32 use a volatile double to force 64 bit rounding.
145f5ab549eSbluhm 	 * Otherwise the i387 would use its internal 80 bit stack.
1464c142d94Sbluhm 	 */
1474c142d94Sbluhm 	d = 1.0;
1484c142d94Sbluhm 	d -= 1.0;
1494c142d94Sbluhm 	if (copysign(1.0, d) < 0.0)
1504c142d94Sbluhm 		return (FE_DOWNWARD);
1514c142d94Sbluhm 
1524c142d94Sbluhm 	d = 1.0;
153f5ab549eSbluhm 	e = d + (DBL_EPSILON * 3.0 / 4.0);
154f5ab549eSbluhm 	if (e == 1.0)
1554c142d94Sbluhm 		return (FE_TOWARDZERO);
156f5ab549eSbluhm 	e = d + (DBL_EPSILON * 1.0 / 4.0);
157f5ab549eSbluhm 	if (e > 1.0)
1584c142d94Sbluhm 		return (FE_UPWARD);
1594c142d94Sbluhm 
1604c142d94Sbluhm 	return (FE_TONEAREST);
1614c142d94Sbluhm }
1624c142d94Sbluhm 
1634c142d94Sbluhm static void
trap_handler(int sig)1644c142d94Sbluhm trap_handler(int sig)
1654c142d94Sbluhm {
1664c142d94Sbluhm 
167c36e572eSmbuhl 	ATF_CHECK_EQ(SIGFPE, sig);
1684c142d94Sbluhm 	_exit(0);
1694c142d94Sbluhm }
1704c142d94Sbluhm 
1714c142d94Sbluhm /*
1724c142d94Sbluhm  * This tests checks the default FP environment, so it must be first.
1734c142d94Sbluhm  * The memcmp() test below may be too much to ask for, since there
1744c142d94Sbluhm  * could be multiple machine-specific default environments.
1754c142d94Sbluhm  */
176c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(dfl_env);
ATF_TC_BODY(dfl_env,tc)177c36e572eSmbuhl ATF_TC_BODY(dfl_env, tc)
1784c142d94Sbluhm {
1794c142d94Sbluhm #ifndef NO_STRICT_DFL_ENV
1804c142d94Sbluhm 	fenv_t env;
1814c142d94Sbluhm 
1824c142d94Sbluhm 	fegetenv(&env);
183c36e572eSmbuhl 	/* Print the default environment for debugging purposes. */
184c36e572eSmbuhl 	hexdump(&env, sizeof(env), "current fenv ", HD_OMIT_CHARS);
185c36e572eSmbuhl 	hexdump(FE_DFL_ENV, sizeof(env), "default fenv ", HD_OMIT_CHARS);
186c36e572eSmbuhl 	CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
1874c142d94Sbluhm #ifdef __amd64__
1884c142d94Sbluhm 	/*
1894c142d94Sbluhm 	 * Compare the fields that the AMD [1] and Intel [2] specs say will be
1904c142d94Sbluhm 	 * set once fnstenv returns.
1914c142d94Sbluhm 	 *
1924c142d94Sbluhm 	 * Not all amd64 capable processors implement the fnstenv instruction
1934c142d94Sbluhm 	 * by zero'ing out the env.__x87.__other field (example: AMD Opteron
1944c142d94Sbluhm 	 * 6308). The AMD64/x64 specs aren't explicit on what the
1954c142d94Sbluhm 	 * env.__x87.__other field will contain after fnstenv is executed, so
1964c142d94Sbluhm 	 * the values in env.__x87.__other could be filled with arbitrary
1974c142d94Sbluhm 	 * data depending on how the CPU implements fnstenv.
1984c142d94Sbluhm 	 *
1994c142d94Sbluhm 	 * 1. http://support.amd.com/TechDocs/26569_APM_v5.pdf
2004c142d94Sbluhm 	 * 2. http://www.intel.com/Assets/en_US/PDF/manual/253666.pdf
2014c142d94Sbluhm 	 */
202c36e572eSmbuhl 	ATF_CHECK(memcmp(&env.__mxcsr, &FE_DFL_ENV->__mxcsr,
2034c142d94Sbluhm 	    sizeof(env.__mxcsr)) == 0);
204c36e572eSmbuhl 	ATF_CHECK(memcmp(&env.__x87.__control, &FE_DFL_ENV->__x87.__control,
2054c142d94Sbluhm 	    sizeof(env.__x87.__control)) == 0);
206c36e572eSmbuhl 	ATF_CHECK(memcmp(&env.__x87.__status, &FE_DFL_ENV->__x87.__status,
2074c142d94Sbluhm 	    sizeof(env.__x87.__status)) == 0);
208c36e572eSmbuhl 	ATF_CHECK(memcmp(&env.__x87.__tag, &FE_DFL_ENV->__x87.__tag,
2094c142d94Sbluhm 	    sizeof(env.__x87.__tag)) == 0);
2104c142d94Sbluhm #else
211c36e572eSmbuhl 	ATF_CHECK_EQ(0, memcmp(&env, FE_DFL_ENV, sizeof(env)));
2124c142d94Sbluhm #endif
2134c142d94Sbluhm 
2144c142d94Sbluhm #endif
215c36e572eSmbuhl 	CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
2164c142d94Sbluhm }
2174c142d94Sbluhm 
2184c142d94Sbluhm /*
2194c142d94Sbluhm  * Test fetestexcept() and feclearexcept().
2204c142d94Sbluhm  */
221c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(fetestclearexcept);
ATF_TC_BODY(fetestclearexcept,tc)222c36e572eSmbuhl ATF_TC_BODY(fetestclearexcept, tc)
2234c142d94Sbluhm {
2244c142d94Sbluhm 	int excepts, i;
2254c142d94Sbluhm 
2264c142d94Sbluhm 	for (i = 0; i < 1 << NEXCEPTS; i++)
227c36e572eSmbuhl 		ATF_CHECK_EQ(0, fetestexcept(std_except_sets[i]));
2284c142d94Sbluhm 	for (i = 0; i < 1 << NEXCEPTS; i++) {
2294c142d94Sbluhm 		excepts = std_except_sets[i];
2304c142d94Sbluhm 
2314c142d94Sbluhm 		/* FE_ALL_EXCEPT might be special-cased, as on i386. */
2324c142d94Sbluhm 		raiseexcept(excepts);
233c36e572eSmbuhl 		ATF_CHECK_EQ(excepts, fetestexcept(excepts));
234c36e572eSmbuhl 		ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
235c36e572eSmbuhl 		ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
2364c142d94Sbluhm 
2374c142d94Sbluhm 		raiseexcept(excepts);
238c36e572eSmbuhl 		ATF_CHECK_EQ(excepts, fetestexcept(excepts));
2394c142d94Sbluhm 		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
2404c142d94Sbluhm 			excepts |= FE_INEXACT;
241c36e572eSmbuhl 			ATF_CHECK_EQ(excepts, (fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT));
2424c142d94Sbluhm 		} else {
243c36e572eSmbuhl 			ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
2444c142d94Sbluhm 		}
245c36e572eSmbuhl 		ATF_CHECK_EQ(0, feclearexcept(excepts));
246c36e572eSmbuhl 		ATF_CHECK_EQ(0, fetestexcept(ALL_STD_EXCEPT));
2474c142d94Sbluhm 	}
2484c142d94Sbluhm }
2494c142d94Sbluhm 
2504c142d94Sbluhm /*
2514c142d94Sbluhm  * Test fegetexceptflag() and fesetexceptflag().
2524c142d94Sbluhm  *
2534c142d94Sbluhm  * Prerequisites: fetestexcept(), feclearexcept()
2544c142d94Sbluhm  */
255c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(fegsetexceptflag);
ATF_TC_BODY(fegsetexceptflag,tc)256c36e572eSmbuhl ATF_TC_BODY(fegsetexceptflag, tc)
2574c142d94Sbluhm {
2584c142d94Sbluhm 	fexcept_t flag;
2594c142d94Sbluhm 	int excepts, i;
2604c142d94Sbluhm 
261c36e572eSmbuhl 	CHECK_FP_EXCEPTIONS(0, FE_ALL_EXCEPT);
2624c142d94Sbluhm 	for (i = 0; i < 1 << NEXCEPTS; i++) {
2634c142d94Sbluhm 		excepts = std_except_sets[i];
2644c142d94Sbluhm 
265c36e572eSmbuhl 		ATF_CHECK_EQ(0, fegetexceptflag(&flag, excepts));
2664c142d94Sbluhm 		raiseexcept(ALL_STD_EXCEPT);
267c36e572eSmbuhl 		ATF_CHECK_EQ(0, fesetexceptflag(&flag, excepts));
268c36e572eSmbuhl 		ATF_CHECK_EQ((ALL_STD_EXCEPT ^ excepts), fetestexcept(ALL_STD_EXCEPT));
2694c142d94Sbluhm 
270c36e572eSmbuhl 		ATF_CHECK_EQ(0, fegetexceptflag(&flag, FE_ALL_EXCEPT));
271c36e572eSmbuhl 		ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
272c36e572eSmbuhl 		ATF_CHECK_EQ(0, fesetexceptflag(&flag, excepts));
273c36e572eSmbuhl 		ATF_CHECK_EQ(0, fetestexcept(ALL_STD_EXCEPT));
274c36e572eSmbuhl 		ATF_CHECK_EQ(0, fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts));
275c36e572eSmbuhl 		ATF_CHECK_EQ((ALL_STD_EXCEPT ^ excepts), fetestexcept(ALL_STD_EXCEPT));
2764c142d94Sbluhm 
277c36e572eSmbuhl 		ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
2784c142d94Sbluhm 	}
2794c142d94Sbluhm }
2804c142d94Sbluhm 
2814c142d94Sbluhm /*
2824c142d94Sbluhm  * Test feraiseexcept().
2834c142d94Sbluhm  *
2844c142d94Sbluhm  * Prerequisites: fetestexcept(), feclearexcept()
2854c142d94Sbluhm  */
286c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(feraiseexcept);
ATF_TC_BODY(feraiseexcept,tc)287c36e572eSmbuhl ATF_TC_BODY(feraiseexcept, tc)
2884c142d94Sbluhm {
2894c142d94Sbluhm 	int excepts, i;
2904c142d94Sbluhm 
2914c142d94Sbluhm 	for (i = 0; i < 1 << NEXCEPTS; i++) {
2924c142d94Sbluhm 		excepts = std_except_sets[i];
2934c142d94Sbluhm 
294c36e572eSmbuhl 		ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
295c36e572eSmbuhl 		ATF_CHECK_EQ(0, feraiseexcept(excepts));
2964c142d94Sbluhm 		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
2974c142d94Sbluhm 			excepts |= FE_INEXACT;
298c36e572eSmbuhl 			ATF_CHECK_EQ(excepts, (fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT));
2994c142d94Sbluhm 		} else {
300c36e572eSmbuhl 			ATF_CHECK_EQ(excepts, fetestexcept(ALL_STD_EXCEPT));
3014c142d94Sbluhm 		}
302c36e572eSmbuhl 		ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
3034c142d94Sbluhm 	}
304c36e572eSmbuhl 	ATF_CHECK_EQ(0, feraiseexcept(FE_INVALID | FE_DIVBYZERO));
305c36e572eSmbuhl 	ATF_CHECK_EQ((FE_INVALID | FE_DIVBYZERO), fetestexcept(ALL_STD_EXCEPT));
306c36e572eSmbuhl 	ATF_CHECK_EQ(0, feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT));
307c36e572eSmbuhl 	ATF_CHECK_EQ(ALL_STD_EXCEPT, fetestexcept(ALL_STD_EXCEPT));
308c36e572eSmbuhl 	ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
3094c142d94Sbluhm }
3104c142d94Sbluhm 
3114c142d94Sbluhm /*
3124c142d94Sbluhm  * Test fegetround() and fesetround().
3134c142d94Sbluhm  */
314c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(fegsetround);
ATF_TC_BODY(fegsetround,tc)315c36e572eSmbuhl ATF_TC_BODY(fegsetround, tc)
3164c142d94Sbluhm {
3174c142d94Sbluhm 
318c36e572eSmbuhl 	ATF_CHECK_EQ(FE_TONEAREST, fegetround());
319c36e572eSmbuhl 	ATF_CHECK_EQ(FE_TONEAREST, getround());
320c36e572eSmbuhl 	ATF_CHECK_EQ(1, FLT_ROUNDS);
3214c142d94Sbluhm 
322c36e572eSmbuhl 	ATF_CHECK_EQ(0, fesetround(FE_DOWNWARD));
323c36e572eSmbuhl 	ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
324c36e572eSmbuhl 	ATF_CHECK_EQ(FE_DOWNWARD, getround());
325c36e572eSmbuhl 	ATF_CHECK_EQ(3, FLT_ROUNDS);
3264c142d94Sbluhm 
327c36e572eSmbuhl 	ATF_CHECK_EQ(0, fesetround(FE_UPWARD));
328c36e572eSmbuhl 	ATF_CHECK_EQ(FE_UPWARD, getround());
329c36e572eSmbuhl 	ATF_CHECK_EQ(FE_UPWARD, fegetround());
330c36e572eSmbuhl 	ATF_CHECK_EQ(2, FLT_ROUNDS);
3314c142d94Sbluhm 
332c36e572eSmbuhl 	ATF_CHECK_EQ(0, fesetround(FE_TOWARDZERO));
333c36e572eSmbuhl 	ATF_CHECK_EQ(FE_TOWARDZERO, getround());
334c36e572eSmbuhl 	ATF_CHECK_EQ(FE_TOWARDZERO, fegetround());
335c36e572eSmbuhl 	ATF_CHECK_EQ(0, FLT_ROUNDS);
3364c142d94Sbluhm 
337c36e572eSmbuhl 	ATF_CHECK_EQ(0, fesetround(FE_TONEAREST));
338c36e572eSmbuhl 	ATF_CHECK_EQ(FE_TONEAREST, getround());
339c36e572eSmbuhl 	ATF_CHECK_EQ(1, FLT_ROUNDS);
3404c142d94Sbluhm 
341c36e572eSmbuhl 	ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
3424c142d94Sbluhm }
3434c142d94Sbluhm 
3444c142d94Sbluhm /*
3454c142d94Sbluhm  * Test fegetenv() and fesetenv().
3464c142d94Sbluhm  *
3474c142d94Sbluhm  * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround()
3484c142d94Sbluhm  */
349c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(fegsetenv);
ATF_TC_BODY(fegsetenv,tc)350c36e572eSmbuhl ATF_TC_BODY(fegsetenv, tc)
3514c142d94Sbluhm {
3524c142d94Sbluhm 	fenv_t env1, env2;
3534c142d94Sbluhm 	int excepts, i;
3544c142d94Sbluhm 
3554c142d94Sbluhm 	for (i = 0; i < 1 << NEXCEPTS; i++) {
3564c142d94Sbluhm 		excepts = std_except_sets[i];
3574c142d94Sbluhm 
358c36e572eSmbuhl 		ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
359c36e572eSmbuhl 		ATF_CHECK_EQ(FE_TONEAREST, fegetround());
360c36e572eSmbuhl 		ATF_CHECK_EQ(0, fegetenv(&env1));
3614c142d94Sbluhm 
3624c142d94Sbluhm 		/*
3634c142d94Sbluhm 		 * fe[gs]etenv() should be able to save and restore
3644c142d94Sbluhm 		 * exception flags without the spurious inexact
3654c142d94Sbluhm 		 * exceptions that afflict raiseexcept().
3664c142d94Sbluhm 		 */
3674c142d94Sbluhm 		raiseexcept(excepts);
3684c142d94Sbluhm 		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 &&
3694c142d94Sbluhm 		    (excepts & FE_INEXACT) == 0)
370c36e572eSmbuhl 			ATF_CHECK_EQ(0, feclearexcept(FE_INEXACT));
3714c142d94Sbluhm 
3724c142d94Sbluhm 		fesetround(FE_DOWNWARD);
373c36e572eSmbuhl 		ATF_CHECK_EQ(0, fegetenv(&env2));
374c36e572eSmbuhl 		ATF_CHECK_EQ(0, fesetenv(&env1));
375c36e572eSmbuhl 		ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
376c36e572eSmbuhl 		ATF_CHECK_EQ(FE_TONEAREST, fegetround());
3774c142d94Sbluhm 
378c36e572eSmbuhl 		ATF_CHECK_EQ(0, fesetenv(&env2));
379c36e572eSmbuhl 
380c36e572eSmbuhl 		/*
381c36e572eSmbuhl 		 * Some platforms like powerpc may set extra exception bits. Since
382c36e572eSmbuhl 		 * only standard exceptions are tested, mask against ALL_STD_EXCEPT
383c36e572eSmbuhl 		 */
384c36e572eSmbuhl 		ATF_CHECK_EQ(excepts, (fetestexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT));
385c36e572eSmbuhl 
386c36e572eSmbuhl 		ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
387c36e572eSmbuhl 		ATF_CHECK_EQ(0, fesetenv(&env1));
388c36e572eSmbuhl 		ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
389c36e572eSmbuhl 		ATF_CHECK_EQ(FE_TONEAREST, fegetround());
3904c142d94Sbluhm 	}
3914c142d94Sbluhm }
3924c142d94Sbluhm 
3934c142d94Sbluhm /*
3944c142d94Sbluhm  * Test fegetexcept(), fedisableexcept(), and feenableexcept().
3954c142d94Sbluhm  *
3964c142d94Sbluhm  * Prerequisites: fetestexcept(), feraiseexcept()
3974c142d94Sbluhm  */
398c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(masking);
ATF_TC_BODY(masking,tc)399c36e572eSmbuhl ATF_TC_BODY(masking, tc)
4004c142d94Sbluhm {
401487aca86Skettenis #if !defined(__arm__) && !defined(__aarch64__) && !defined(__riscv)
4024c142d94Sbluhm 	struct sigaction act;
4034c142d94Sbluhm 	int except, pass, raise, status;
4044c142d94Sbluhm 	unsigned i;
4054c142d94Sbluhm 
406c36e572eSmbuhl 	ATF_REQUIRE_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
407c36e572eSmbuhl 
408c36e572eSmbuhl 	/*
409c36e572eSmbuhl 	 * Some CPUs, e.g. AArch64 QEMU does not support trapping on FP
410c36e572eSmbuhl 	 * exceptions. In that case the trap enable bits are all RAZ/WI, so
411c36e572eSmbuhl 	 * writing to those bits will be ignored and the the next read will
412c36e572eSmbuhl 	 * return all zeroes for those bits. Skip the test if no floating
413c36e572eSmbuhl 	 * point exceptions are supported and mark it XFAIL if some are missing.
414c36e572eSmbuhl 	 */
415c36e572eSmbuhl 	ATF_REQUIRE_EQ(0, (feenableexcept(FE_ALL_EXCEPT)));
416c36e572eSmbuhl 	except = fegetexcept();
417c36e572eSmbuhl 	if (except == 0) {
418c36e572eSmbuhl 		atf_tc_skip("CPU does not support trapping on floating point "
419c36e572eSmbuhl 		    "exceptions.");
420c36e572eSmbuhl 	} else if ((except & ALL_STD_EXCEPT) != ALL_STD_EXCEPT) {
421c36e572eSmbuhl 		atf_tc_expect_fail("Not all floating point exceptions can be "
422c36e572eSmbuhl 		    "set to trap: %#x vs %#x", except, ALL_STD_EXCEPT);
423c36e572eSmbuhl 	}
424c36e572eSmbuhl 	fedisableexcept(FE_ALL_EXCEPT);
425c36e572eSmbuhl 
426c36e572eSmbuhl 
427c36e572eSmbuhl 	ATF_CHECK_EQ(0, (feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT));
428c36e572eSmbuhl 	ATF_CHECK_EQ((FE_INVALID | FE_OVERFLOW), (feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT));
429c36e572eSmbuhl 	ATF_CHECK_EQ((FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW), (fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT));
430c36e572eSmbuhl 	ATF_CHECK_EQ((FE_INVALID | FE_UNDERFLOW), (fegetexcept() & ALL_STD_EXCEPT));
431c36e572eSmbuhl 	ATF_CHECK_EQ((FE_INVALID | FE_UNDERFLOW), (fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT));
432c36e572eSmbuhl 	ATF_CHECK_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
4334c142d94Sbluhm 
4344c142d94Sbluhm 	sigemptyset(&act.sa_mask);
4354c142d94Sbluhm 	act.sa_flags = 0;
4364c142d94Sbluhm 	act.sa_handler = trap_handler;
4374c142d94Sbluhm 	for (pass = 0; pass < 2; pass++) {
4384c142d94Sbluhm 		for (i = 0; i < NEXCEPTS; i++) {
4394c142d94Sbluhm 			except = std_excepts[i];
4404c142d94Sbluhm 			/* over/underflow may also raise inexact */
4414c142d94Sbluhm 			if (except == FE_INEXACT)
4424c142d94Sbluhm 				raise = FE_DIVBYZERO | FE_INVALID;
4434c142d94Sbluhm 			else
4444c142d94Sbluhm 				raise = ALL_STD_EXCEPT ^ except;
4454c142d94Sbluhm 
4464c142d94Sbluhm 			/*
4474c142d94Sbluhm 			 * We need to fork a child process because
4484c142d94Sbluhm 			 * there isn't a portable way to recover from
4494c142d94Sbluhm 			 * a floating-point exception.
4504c142d94Sbluhm 			 */
4514c142d94Sbluhm 			switch(fork()) {
4524c142d94Sbluhm 			case 0:		/* child */
453c36e572eSmbuhl 				ATF_CHECK_EQ(0, (fegetexcept() & ALL_STD_EXCEPT));
454c36e572eSmbuhl 				ATF_REQUIRE_EQ(0, (feenableexcept(except) & ALL_STD_EXCEPT));
455c36e572eSmbuhl 				ATF_CHECK_EQ(except, fegetexcept());
4564c142d94Sbluhm 				raiseexcept(raise);
457c36e572eSmbuhl 				ATF_CHECK_EQ(0, feraiseexcept(raise));
458c36e572eSmbuhl 				ATF_CHECK_EQ(raise, fetestexcept(ALL_STD_EXCEPT));
4594c142d94Sbluhm 
460c36e572eSmbuhl 				ATF_CHECK_EQ(0, sigaction(SIGFPE, &act, NULL));
4614c142d94Sbluhm 				switch (pass) {
4624c142d94Sbluhm 				case 0:
4634c142d94Sbluhm 					raiseexcept(except);
4644c142d94Sbluhm 				case 1:
4654c142d94Sbluhm 					feraiseexcept(except);
4664c142d94Sbluhm 				default:
467c36e572eSmbuhl 					ATF_REQUIRE(0);
4684c142d94Sbluhm 				}
469c36e572eSmbuhl 				ATF_REQUIRE(0);
4704c142d94Sbluhm 			default:	/* parent */
471c36e572eSmbuhl 				ATF_REQUIRE(wait(&status) > 0);
4724c142d94Sbluhm 				/*
4734c142d94Sbluhm 				 * Avoid assert() here so that it's possible
4744c142d94Sbluhm 				 * to examine a failed child's core dump.
4754c142d94Sbluhm 				 */
4764c142d94Sbluhm 				if (!WIFEXITED(status))
4774c142d94Sbluhm 					errx(1, "child aborted\n");
478c36e572eSmbuhl 				ATF_CHECK_EQ(0, WEXITSTATUS(status));
4794c142d94Sbluhm 				break;
4804c142d94Sbluhm 			case -1:	/* error */
481c36e572eSmbuhl 				ATF_REQUIRE(0);
4824c142d94Sbluhm 			}
4834c142d94Sbluhm 		}
4844c142d94Sbluhm 	}
485c36e572eSmbuhl 	ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
48662bcd63cSkettenis #endif
4874c142d94Sbluhm }
4884c142d94Sbluhm 
4894c142d94Sbluhm /*
4904c142d94Sbluhm  * Test feholdexcept() and feupdateenv().
4914c142d94Sbluhm  *
4924c142d94Sbluhm  * Prerequisites: fetestexcept(), fegetround(), fesetround(),
4934c142d94Sbluhm  *	fedisableexcept(), feenableexcept()
4944c142d94Sbluhm  */
495c36e572eSmbuhl ATF_TC_WITHOUT_HEAD(feholdupdate);
ATF_TC_BODY(feholdupdate,tc)496c36e572eSmbuhl ATF_TC_BODY(feholdupdate, tc)
4974c142d94Sbluhm {
4984c142d94Sbluhm 	fenv_t env;
4994c142d94Sbluhm 
5004c142d94Sbluhm 	struct sigaction act;
5014c142d94Sbluhm 	int except, pass, status, raise;
5024c142d94Sbluhm 	unsigned i;
5034c142d94Sbluhm 
5044c142d94Sbluhm 	sigemptyset(&act.sa_mask);
5054c142d94Sbluhm 	act.sa_flags = 0;
5064c142d94Sbluhm 	act.sa_handler = trap_handler;
5074c142d94Sbluhm 	for (pass = 0; pass < 2; pass++) {
5084c142d94Sbluhm 		for (i = 0; i < NEXCEPTS; i++) {
5094c142d94Sbluhm 			except = std_excepts[i];
5104c142d94Sbluhm 			/* over/underflow may also raise inexact */
5114c142d94Sbluhm 			if (except == FE_INEXACT)
5124c142d94Sbluhm 				raise = FE_DIVBYZERO | FE_INVALID;
5134c142d94Sbluhm 			else
5144c142d94Sbluhm 				raise = ALL_STD_EXCEPT ^ except;
5154c142d94Sbluhm 
5164c142d94Sbluhm 			/*
5174c142d94Sbluhm 			 * We need to fork a child process because
5184c142d94Sbluhm 			 * there isn't a portable way to recover from
5194c142d94Sbluhm 			 * a floating-point exception.
5204c142d94Sbluhm 			 */
5214c142d94Sbluhm 			switch(fork()) {
5224c142d94Sbluhm 			case 0:		/* child */
5234c142d94Sbluhm 				/*
5244c142d94Sbluhm 				 * We don't want to cause a fatal exception in
5254c142d94Sbluhm 				 * the child until the second pass, so we can
5264c142d94Sbluhm 				 * check other properties of feupdateenv().
5274c142d94Sbluhm 				 */
5284c142d94Sbluhm 				if (pass == 1)
529c36e572eSmbuhl 					ATF_REQUIRE_EQ(0,
530c36e572eSmbuhl 					    feenableexcept(except) &
531c36e572eSmbuhl 					    ALL_STD_EXCEPT);
5324c142d94Sbluhm 				raiseexcept(raise);
533c36e572eSmbuhl 				ATF_CHECK_EQ(0, fesetround(FE_DOWNWARD));
534c36e572eSmbuhl 				ATF_CHECK_EQ(0, feholdexcept(&env));
535c36e572eSmbuhl 				ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
5364c142d94Sbluhm 				raiseexcept(except);
537c36e572eSmbuhl 				ATF_CHECK_EQ(0, fesetround(FE_UPWARD));
5384c142d94Sbluhm 
5394c142d94Sbluhm 				if (pass == 1)
540c36e572eSmbuhl 					ATF_CHECK_EQ(0, sigaction(SIGFPE, &act, NULL));
541c36e572eSmbuhl 				ATF_CHECK_EQ(0, feupdateenv(&env));
542c36e572eSmbuhl 				ATF_CHECK_EQ(FE_DOWNWARD, fegetround());
543c36e572eSmbuhl 				ATF_CHECK_EQ((except | raise), fetestexcept(ALL_STD_EXCEPT));
5444c142d94Sbluhm 
545c36e572eSmbuhl 				ATF_CHECK_EQ(0, pass);
5464c142d94Sbluhm 				_exit(0);
5474c142d94Sbluhm 			default:	/* parent */
548c36e572eSmbuhl 				ATF_REQUIRE(wait(&status) > 0);
5494c142d94Sbluhm 				/*
5504c142d94Sbluhm 				 * Avoid assert() here so that it's possible
5514c142d94Sbluhm 				 * to examine a failed child's core dump.
5524c142d94Sbluhm 				 */
5534c142d94Sbluhm 				if (!WIFEXITED(status))
5544c142d94Sbluhm 					errx(1, "child aborted\n");
555c36e572eSmbuhl 				ATF_CHECK_EQ(0, WEXITSTATUS(status));
5564c142d94Sbluhm 				break;
5574c142d94Sbluhm 			case -1:	/* error */
558c36e572eSmbuhl 				ATF_REQUIRE(0);
5594c142d94Sbluhm 			}
5604c142d94Sbluhm 		}
561487aca86Skettenis #if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
56262bcd63cSkettenis 		break;
56362bcd63cSkettenis #endif
5644c142d94Sbluhm 	}
565c36e572eSmbuhl 	ATF_CHECK_EQ(0, fetestexcept(FE_ALL_EXCEPT));
5664c142d94Sbluhm }
5674c142d94Sbluhm 
ATF_TP_ADD_TCS(tp)568c36e572eSmbuhl ATF_TP_ADD_TCS(tp)
5694c142d94Sbluhm {
570c36e572eSmbuhl 	ATF_TP_ADD_TC(tp, dfl_env);
571c36e572eSmbuhl 	ATF_TP_ADD_TC(tp, fetestclearexcept);
572c36e572eSmbuhl 	ATF_TP_ADD_TC(tp, fegsetexceptflag);
573c36e572eSmbuhl 	ATF_TP_ADD_TC(tp, feraiseexcept);
574c36e572eSmbuhl 	ATF_TP_ADD_TC(tp, fegsetround);
575c36e572eSmbuhl 	ATF_TP_ADD_TC(tp, fegsetenv);
576c36e572eSmbuhl 	ATF_TP_ADD_TC(tp, masking);
577c36e572eSmbuhl 	ATF_TP_ADD_TC(tp, feholdupdate);
5784c142d94Sbluhm 
579c36e572eSmbuhl 	return (atf_no_error());
5804c142d94Sbluhm }
581