1130330Sdas/*-
2130330Sdas * Copyright (c) 2004 David Schultz <das@FreeBSD.org>
3130330Sdas * All rights reserved.
4130330Sdas *
5130330Sdas * Redistribution and use in source and binary forms, with or without
6130330Sdas * modification, are permitted provided that the following conditions
7130330Sdas * are met:
8130330Sdas * 1. Redistributions of source code must retain the above copyright
9130330Sdas *    notice, this list of conditions and the following disclaimer.
10130330Sdas * 2. Redistributions in binary form must reproduce the above copyright
11130330Sdas *    notice, this list of conditions and the following disclaimer in the
12130330Sdas *    documentation and/or other materials provided with the distribution.
13130330Sdas *
14130330Sdas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15130330Sdas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16130330Sdas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17130330Sdas * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18130330Sdas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19130330Sdas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20130330Sdas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21130330Sdas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22130330Sdas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23130330Sdas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24130330Sdas * SUCH DAMAGE.
25130330Sdas */
26130330Sdas
27130330Sdas/*
28130330Sdas * Test the correctness and C99-compliance of various fenv.h features.
29130330Sdas */
30130330Sdas
31130330Sdas#include <sys/cdefs.h>
32130330Sdas__FBSDID("$FreeBSD: stable/11/lib/msun/tests/fenv_test.c 315121 2017-03-12 04:52:09Z ngie $");
33130330Sdas
34130330Sdas#include <sys/types.h>
35130330Sdas#include <sys/wait.h>
36130330Sdas#include <assert.h>
37130330Sdas#include <err.h>
38130330Sdas#include <fenv.h>
39130330Sdas#include <float.h>
40130330Sdas#include <math.h>
41130330Sdas#include <signal.h>
42130330Sdas#include <stdio.h>
43130330Sdas#include <string.h>
44130330Sdas#include <unistd.h>
45130330Sdas
46130330Sdas/*
47130330Sdas * Implementations are permitted to define additional exception flags
48130330Sdas * not specified in the standard, so it is not necessarily true that
49130330Sdas * FE_ALL_EXCEPT == ALL_STD_EXCEPT.
50130330Sdas */
51130330Sdas#define	ALL_STD_EXCEPT	(FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \
52130330Sdas			 FE_OVERFLOW | FE_UNDERFLOW)
53130330Sdas
54130330Sdas#define	NEXCEPTS	(sizeof(std_excepts) / sizeof(std_excepts[0]))
55130330Sdas
56130330Sdasstatic const int std_excepts[] = {
57130330Sdas	FE_INVALID,
58130330Sdas	FE_DIVBYZERO,
59130330Sdas	FE_OVERFLOW,
60130330Sdas	FE_UNDERFLOW,
61130330Sdas	FE_INEXACT,
62130330Sdas};
63130330Sdas
64130330Sdas/* init_exceptsets() initializes this to the power set of std_excepts[] */
65130330Sdasstatic int std_except_sets[1 << NEXCEPTS];
66130330Sdas
67130330Sdas#pragma STDC FENV_ACCESS ON
68130330Sdas
69130330Sdas/*
70130330Sdas * Initialize std_except_sets[] to the power set of std_excepts[]
71130330Sdas */
72315121Sngiestatic void
73130330Sdasinit_exceptsets(void)
74130330Sdas{
75315121Sngie	unsigned i, j, sr;
76130330Sdas
77130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
78130330Sdas		for (sr = i, j = 0; sr != 0; sr >>= 1, j++)
79130330Sdas			std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1);
80130330Sdas	}
81130330Sdas}
82130330Sdas
83130330Sdas/*
84315121Sngie * Raise a floating-point exception without relying on the standard
85315121Sngie * library routines, which we are trying to test.
86315121Sngie *
87315121Sngie * XXX We can't raise an {over,under}flow without also raising an
88315121Sngie * inexact exception.
89315121Sngie */
90315121Sngiestatic void
91315121Sngieraiseexcept(int excepts)
92315121Sngie{
93315121Sngie	volatile double d;
94315121Sngie
95315121Sngie	/*
96315121Sngie	 * With a compiler that supports the FENV_ACCESS pragma
97315121Sngie	 * properly, simple expressions like '0.0 / 0.0' should
98315121Sngie	 * be sufficient to generate traps.  Unfortunately, we
99315121Sngie	 * need to bring a volatile variable into the equation
100315121Sngie	 * to prevent incorrect optimizations.
101315121Sngie	 */
102315121Sngie	if (excepts & FE_INVALID) {
103315121Sngie		d = 0.0;
104315121Sngie		d = 0.0 / d;
105315121Sngie	}
106315121Sngie	if (excepts & FE_DIVBYZERO) {
107315121Sngie		d = 0.0;
108315121Sngie		d = 1.0 / d;
109315121Sngie	}
110315121Sngie	if (excepts & FE_OVERFLOW) {
111315121Sngie		d = DBL_MAX;
112315121Sngie		d *= 2.0;
113315121Sngie	}
114315121Sngie	if (excepts & FE_UNDERFLOW) {
115315121Sngie		d = DBL_MIN;
116315121Sngie		d /= DBL_MAX;
117315121Sngie	}
118315121Sngie	if (excepts & FE_INEXACT) {
119315121Sngie		d = DBL_MIN;
120315121Sngie		d += 1.0;
121315121Sngie	}
122315121Sngie
123315121Sngie	/*
124315121Sngie	 * On the x86 (and some other architectures?) the FPU and
125315121Sngie	 * integer units are decoupled.  We need to execute an FWAIT
126315121Sngie	 * or a floating-point instruction to get synchronous exceptions.
127315121Sngie	 */
128315121Sngie	d = 1.0;
129315121Sngie	d += 1.0;
130315121Sngie}
131315121Sngie
132315121Sngie/*
133315121Sngie * Determine the current rounding mode without relying on the fenv
134315121Sngie * routines.  This function may raise an inexact exception.
135315121Sngie */
136315121Sngiestatic int
137315121Sngiegetround(void)
138315121Sngie{
139315121Sngie	volatile double d;
140315121Sngie
141315121Sngie	/*
142315121Sngie	 * This test works just as well with 0.0 - 0.0, except on ia64
143315121Sngie	 * where 0.0 - 0.0 gives the wrong sign when rounding downwards.
144315121Sngie	 */
145315121Sngie	d = 1.0;
146315121Sngie	d -= 1.0;
147315121Sngie	if (copysign(1.0, d) < 0.0)
148315121Sngie		return (FE_DOWNWARD);
149315121Sngie
150315121Sngie	d = 1.0;
151315121Sngie	if (d + (DBL_EPSILON * 3.0 / 4.0) == 1.0)
152315121Sngie		return (FE_TOWARDZERO);
153315121Sngie	if (d + (DBL_EPSILON * 1.0 / 4.0) > 1.0)
154315121Sngie		return (FE_UPWARD);
155315121Sngie
156315121Sngie	return (FE_TONEAREST);
157315121Sngie}
158315121Sngie
159315121Sngiestatic void
160315121Sngietrap_handler(int sig)
161315121Sngie{
162315121Sngie
163315121Sngie	assert(sig == SIGFPE);
164315121Sngie	_exit(0);
165315121Sngie}
166315121Sngie
167315121Sngie/*
168130330Sdas * This tests checks the default FP environment, so it must be first.
169130330Sdas * The memcmp() test below may be too much to ask for, since there
170130330Sdas * could be multiple machine-specific default environments.
171130330Sdas */
172130330Sdasstatic void
173130330Sdastest_dfl_env(void)
174130330Sdas{
175130330Sdas#ifndef NO_STRICT_DFL_ENV
176130330Sdas	fenv_t env;
177130330Sdas
178130330Sdas	fegetenv(&env);
179289332Sngie
180289332Sngie#ifdef __amd64__
181289332Sngie	/*
182289332Sngie	 * Compare the fields that the AMD [1] and Intel [2] specs say will be
183289332Sngie	 * set once fnstenv returns.
184289332Sngie	 *
185289332Sngie	 * Not all amd64 capable processors implement the fnstenv instruction
186289332Sngie	 * by zero'ing out the env.__x87.__other field (example: AMD Opteron
187289332Sngie	 * 6308). The AMD64/x64 specs aren't explicit on what the
188289332Sngie	 * env.__x87.__other field will contain after fnstenv is executed, so
189289332Sngie	 * the values in env.__x87.__other could be filled with arbitrary
190289332Sngie	 * data depending on how the CPU implements fnstenv.
191289332Sngie	 *
192289332Sngie	 * 1. http://support.amd.com/TechDocs/26569_APM_v5.pdf
193289332Sngie	 * 2. http://www.intel.com/Assets/en_US/PDF/manual/253666.pdf
194289332Sngie	 */
195289332Sngie	assert(memcmp(&env.__mxcsr, &FE_DFL_ENV->__mxcsr,
196289332Sngie	    sizeof(env.__mxcsr)) == 0);
197289332Sngie	assert(memcmp(&env.__x87.__control, &FE_DFL_ENV->__x87.__control,
198289332Sngie	    sizeof(env.__x87.__control)) == 0);
199289332Sngie	assert(memcmp(&env.__x87.__status, &FE_DFL_ENV->__x87.__status,
200289332Sngie	    sizeof(env.__x87.__status)) == 0);
201289332Sngie	assert(memcmp(&env.__x87.__tag, &FE_DFL_ENV->__x87.__tag,
202289332Sngie	    sizeof(env.__x87.__tag)) == 0);
203289332Sngie#else
204130330Sdas	assert(memcmp(&env, FE_DFL_ENV, sizeof(env)) == 0);
205130330Sdas#endif
206289332Sngie
207289332Sngie#endif
208130330Sdas	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
209130330Sdas}
210130330Sdas
211130330Sdas/*
212130330Sdas * Test fetestexcept() and feclearexcept().
213130330Sdas */
214130330Sdasstatic void
215130330Sdastest_fetestclearexcept(void)
216130330Sdas{
217130330Sdas	int excepts, i;
218130330Sdas
219130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++)
220130330Sdas		assert(fetestexcept(std_except_sets[i]) == 0);
221130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
222130330Sdas		excepts = std_except_sets[i];
223130330Sdas
224130330Sdas		/* FE_ALL_EXCEPT might be special-cased, as on i386. */
225130330Sdas		raiseexcept(excepts);
226130330Sdas		assert(fetestexcept(excepts) == excepts);
227130330Sdas		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
228130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
229130330Sdas
230130330Sdas		raiseexcept(excepts);
231130330Sdas		assert(fetestexcept(excepts) == excepts);
232130330Sdas		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
233130330Sdas			excepts |= FE_INEXACT;
234130330Sdas			assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
235130330Sdas			    excepts);
236130330Sdas		} else {
237130330Sdas			assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
238130330Sdas		}
239130330Sdas		assert(feclearexcept(excepts) == 0);
240130330Sdas		assert(fetestexcept(ALL_STD_EXCEPT) == 0);
241130330Sdas	}
242130330Sdas}
243130330Sdas
244130330Sdas/*
245130330Sdas * Test fegetexceptflag() and fesetexceptflag().
246130330Sdas *
247130330Sdas * Prerequisites: fetestexcept(), feclearexcept()
248130330Sdas */
249130330Sdasstatic void
250130330Sdastest_fegsetexceptflag(void)
251130330Sdas{
252130330Sdas	fexcept_t flag;
253130330Sdas	int excepts, i;
254130330Sdas
255130330Sdas	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
256130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
257130330Sdas		excepts = std_except_sets[i];
258130330Sdas
259130330Sdas		assert(fegetexceptflag(&flag, excepts) == 0);
260130330Sdas		raiseexcept(ALL_STD_EXCEPT);
261130330Sdas		assert(fesetexceptflag(&flag, excepts) == 0);
262130330Sdas		assert(fetestexcept(ALL_STD_EXCEPT) ==
263130330Sdas		    (ALL_STD_EXCEPT ^ excepts));
264130330Sdas
265130330Sdas		assert(fegetexceptflag(&flag, FE_ALL_EXCEPT) == 0);
266130330Sdas		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
267130330Sdas		assert(fesetexceptflag(&flag, excepts) == 0);
268130330Sdas		assert(fetestexcept(ALL_STD_EXCEPT) == 0);
269130330Sdas		assert(fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts) == 0);
270130330Sdas		assert(fetestexcept(ALL_STD_EXCEPT) ==
271130330Sdas		    (ALL_STD_EXCEPT ^ excepts));
272130330Sdas
273130330Sdas		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
274130330Sdas	}
275130330Sdas}
276130330Sdas
277130330Sdas/*
278130330Sdas * Test feraiseexcept().
279130330Sdas *
280130330Sdas * Prerequisites: fetestexcept(), feclearexcept()
281130330Sdas */
282130330Sdasstatic void
283130330Sdastest_feraiseexcept(void)
284130330Sdas{
285130330Sdas	int excepts, i;
286130330Sdas
287130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
288130330Sdas		excepts = std_except_sets[i];
289130330Sdas
290130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
291130330Sdas		assert(feraiseexcept(excepts) == 0);
292130330Sdas		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
293130330Sdas			excepts |= FE_INEXACT;
294130330Sdas			assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
295130330Sdas			    excepts);
296130330Sdas		} else {
297130330Sdas			assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
298130330Sdas		}
299130330Sdas		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
300130330Sdas	}
301130330Sdas	assert(feraiseexcept(FE_INVALID | FE_DIVBYZERO) == 0);
302130330Sdas	assert(fetestexcept(ALL_STD_EXCEPT) == (FE_INVALID | FE_DIVBYZERO));
303130330Sdas	assert(feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) == 0);
304130330Sdas	assert(fetestexcept(ALL_STD_EXCEPT) == ALL_STD_EXCEPT);
305130330Sdas	assert(feclearexcept(FE_ALL_EXCEPT) == 0);
306130330Sdas}
307130330Sdas
308130330Sdas/*
309130330Sdas * Test fegetround() and fesetround().
310130330Sdas */
311130330Sdasstatic void
312130330Sdastest_fegsetround(void)
313130330Sdas{
314130330Sdas
315130330Sdas	assert(fegetround() == FE_TONEAREST);
316130330Sdas	assert(getround() == FE_TONEAREST);
317132384Sdas	assert(FLT_ROUNDS == 1);
318132384Sdas
319130330Sdas	assert(fesetround(FE_DOWNWARD) == 0);
320130330Sdas	assert(fegetround() == FE_DOWNWARD);
321130330Sdas	assert(getround() == FE_DOWNWARD);
322132384Sdas	assert(FLT_ROUNDS == 3);
323132384Sdas
324130330Sdas	assert(fesetround(FE_UPWARD) == 0);
325130330Sdas	assert(getround() == FE_UPWARD);
326130330Sdas	assert(fegetround() == FE_UPWARD);
327132384Sdas	assert(FLT_ROUNDS == 2);
328132384Sdas
329130330Sdas	assert(fesetround(FE_TOWARDZERO) == 0);
330130330Sdas	assert(getround() == FE_TOWARDZERO);
331130330Sdas	assert(fegetround() == FE_TOWARDZERO);
332132384Sdas	assert(FLT_ROUNDS == 0);
333132384Sdas
334130330Sdas	assert(fesetround(FE_TONEAREST) == 0);
335130330Sdas	assert(getround() == FE_TONEAREST);
336132384Sdas	assert(FLT_ROUNDS == 1);
337132384Sdas
338130330Sdas	assert(feclearexcept(FE_ALL_EXCEPT) == 0);
339130330Sdas}
340130330Sdas
341130330Sdas/*
342130330Sdas * Test fegetenv() and fesetenv().
343130330Sdas *
344130330Sdas * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround()
345130330Sdas */
346130330Sdasstatic void
347130330Sdastest_fegsetenv(void)
348130330Sdas{
349130330Sdas	fenv_t env1, env2;
350130330Sdas	int excepts, i;
351130330Sdas
352130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
353130330Sdas		excepts = std_except_sets[i];
354130330Sdas
355130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
356130330Sdas		assert(fegetround() == FE_TONEAREST);
357130330Sdas		assert(fegetenv(&env1) == 0);
358130330Sdas
359130330Sdas		/*
360130330Sdas		 * fe[gs]etenv() should be able to save and restore
361130330Sdas		 * exception flags without the spurious inexact
362130330Sdas		 * exceptions that afflict raiseexcept().
363130330Sdas		 */
364130330Sdas		raiseexcept(excepts);
365130330Sdas		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 &&
366130330Sdas		    (excepts & FE_INEXACT) == 0)
367130330Sdas			assert(feclearexcept(FE_INEXACT) == 0);
368130330Sdas
369130330Sdas		fesetround(FE_DOWNWARD);
370130330Sdas		assert(fegetenv(&env2) == 0);
371130330Sdas		assert(fesetenv(&env1) == 0);
372130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
373130330Sdas		assert(fegetround() == FE_TONEAREST);
374130330Sdas
375130330Sdas		assert(fesetenv(&env2) == 0);
376130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == excepts);
377130330Sdas		assert(fegetround() == FE_DOWNWARD);
378130330Sdas		assert(fesetenv(&env1) == 0);
379130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
380130330Sdas		assert(fegetround() == FE_TONEAREST);
381130330Sdas	}
382130330Sdas}
383130330Sdas
384130330Sdas/*
385143710Sdas * Test fegetexcept(), fedisableexcept(), and feenableexcept().
386130330Sdas *
387130330Sdas * Prerequisites: fetestexcept(), feraiseexcept()
388130330Sdas */
389130330Sdasstatic void
390143710Sdastest_masking(void)
391130330Sdas{
392130330Sdas	struct sigaction act;
393315121Sngie	int except, pass, raise, status;
394315121Sngie	unsigned i;
395130330Sdas
396143710Sdas	assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
397143710Sdas	assert((feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT) == 0);
398143710Sdas	assert((feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT) ==
399143710Sdas	    (FE_INVALID | FE_OVERFLOW));
400143710Sdas	assert((fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT) ==
401143710Sdas	    (FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW));
402143710Sdas	assert((fegetexcept() & ALL_STD_EXCEPT) == (FE_INVALID | FE_UNDERFLOW));
403143710Sdas	assert((fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT) ==
404143710Sdas	    (FE_INVALID | FE_UNDERFLOW));
405143710Sdas	assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
406143710Sdas
407130330Sdas	sigemptyset(&act.sa_mask);
408130330Sdas	act.sa_flags = 0;
409130330Sdas	act.sa_handler = trap_handler;
410130330Sdas	for (pass = 0; pass < 2; pass++) {
411130330Sdas		for (i = 0; i < NEXCEPTS; i++) {
412130330Sdas			except = std_excepts[i];
413130330Sdas			/* over/underflow may also raise inexact */
414130330Sdas			if (except == FE_INEXACT)
415130330Sdas				raise = FE_DIVBYZERO | FE_INVALID;
416130330Sdas			else
417130330Sdas				raise = ALL_STD_EXCEPT ^ except;
418130330Sdas
419130330Sdas			/*
420130330Sdas			 * We need to fork a child process because
421130330Sdas			 * there isn't a portable way to recover from
422130330Sdas			 * a floating-point exception.
423130330Sdas			 */
424130330Sdas			switch(fork()) {
425130330Sdas			case 0:		/* child */
426143710Sdas				assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
427143710Sdas				assert((feenableexcept(except)
428143710Sdas					   & ALL_STD_EXCEPT) == 0);
429143710Sdas				assert(fegetexcept() == except);
430130330Sdas				raiseexcept(raise);
431130330Sdas				assert(feraiseexcept(raise) == 0);
432130330Sdas				assert(fetestexcept(ALL_STD_EXCEPT) == raise);
433130330Sdas
434130330Sdas				assert(sigaction(SIGFPE, &act, NULL) == 0);
435130330Sdas				switch (pass) {
436130330Sdas				case 0:
437130330Sdas					raiseexcept(except);
438130330Sdas				case 1:
439130330Sdas					feraiseexcept(except);
440130330Sdas				default:
441130330Sdas					assert(0);
442130330Sdas				}
443130330Sdas				assert(0);
444130330Sdas			default:	/* parent */
445130330Sdas				assert(wait(&status) > 0);
446130330Sdas				/*
447130330Sdas				 * Avoid assert() here so that it's possible
448130330Sdas				 * to examine a failed child's core dump.
449130330Sdas				 */
450130330Sdas				if (!WIFEXITED(status))
451130330Sdas					errx(1, "child aborted\n");
452130330Sdas				assert(WEXITSTATUS(status) == 0);
453130330Sdas				break;
454130330Sdas			case -1:	/* error */
455130330Sdas				assert(0);
456130330Sdas			}
457130330Sdas		}
458130330Sdas	}
459130330Sdas	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
460130330Sdas}
461130330Sdas
462130330Sdas/*
463130330Sdas * Test feholdexcept() and feupdateenv().
464130330Sdas *
465143710Sdas * Prerequisites: fetestexcept(), fegetround(), fesetround(),
466143710Sdas *	fedisableexcept(), feenableexcept()
467130330Sdas */
468130330Sdasstatic void
469130330Sdastest_feholdupdate(void)
470130330Sdas{
471130330Sdas	fenv_t env;
472130330Sdas
473130330Sdas	struct sigaction act;
474315121Sngie	int except, pass, status, raise;
475315121Sngie	unsigned i;
476130330Sdas
477130330Sdas	sigemptyset(&act.sa_mask);
478130330Sdas	act.sa_flags = 0;
479130330Sdas	act.sa_handler = trap_handler;
480130330Sdas	for (pass = 0; pass < 2; pass++) {
481130330Sdas		for (i = 0; i < NEXCEPTS; i++) {
482130330Sdas			except = std_excepts[i];
483130330Sdas			/* over/underflow may also raise inexact */
484130330Sdas			if (except == FE_INEXACT)
485130330Sdas				raise = FE_DIVBYZERO | FE_INVALID;
486130330Sdas			else
487130330Sdas				raise = ALL_STD_EXCEPT ^ except;
488130330Sdas
489130330Sdas			/*
490130330Sdas			 * We need to fork a child process because
491130330Sdas			 * there isn't a portable way to recover from
492130330Sdas			 * a floating-point exception.
493130330Sdas			 */
494130330Sdas			switch(fork()) {
495130330Sdas			case 0:		/* child */
496130330Sdas				/*
497130330Sdas				 * We don't want to cause a fatal exception in
498130330Sdas				 * the child until the second pass, so we can
499130330Sdas				 * check other properties of feupdateenv().
500315121Sngie				 */
501130330Sdas				if (pass == 1)
502143710Sdas					assert((feenableexcept(except) &
503130330Sdas						   ALL_STD_EXCEPT) == 0);
504130330Sdas				raiseexcept(raise);
505130330Sdas				assert(fesetround(FE_DOWNWARD) == 0);
506130330Sdas				assert(feholdexcept(&env) == 0);
507130330Sdas				assert(fetestexcept(FE_ALL_EXCEPT) == 0);
508130330Sdas				raiseexcept(except);
509130330Sdas				assert(fesetround(FE_UPWARD) == 0);
510130330Sdas
511130330Sdas				if (pass == 1)
512130330Sdas					assert(sigaction(SIGFPE, &act, NULL) ==
513130330Sdas					    0);
514130330Sdas				assert(feupdateenv(&env) == 0);
515130330Sdas				assert(fegetround() == FE_DOWNWARD);
516130330Sdas				assert(fetestexcept(ALL_STD_EXCEPT) ==
517130330Sdas				    (except | raise));
518130330Sdas
519130330Sdas				assert(pass == 0);
520130330Sdas				_exit(0);
521130330Sdas			default:	/* parent */
522130330Sdas				assert(wait(&status) > 0);
523130330Sdas				/*
524130330Sdas				 * Avoid assert() here so that it's possible
525130330Sdas				 * to examine a failed child's core dump.
526130330Sdas				 */
527130330Sdas				if (!WIFEXITED(status))
528130330Sdas					errx(1, "child aborted\n");
529130330Sdas				assert(WEXITSTATUS(status) == 0);
530130330Sdas				break;
531130330Sdas			case -1:	/* error */
532130330Sdas				assert(0);
533130330Sdas			}
534130330Sdas		}
535130330Sdas	}
536130330Sdas	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
537130330Sdas}
538130330Sdas
539315121Sngieint
540315121Sngiemain(void)
541130330Sdas{
542130330Sdas
543315121Sngie	printf("1..8\n");
544315121Sngie	init_exceptsets();
545315121Sngie	test_dfl_env();
546315121Sngie	printf("ok 1 - fenv\n");
547315121Sngie	test_fetestclearexcept();
548315121Sngie	printf("ok 2 - fenv\n");
549315121Sngie	test_fegsetexceptflag();
550315121Sngie	printf("ok 3 - fenv\n");
551315121Sngie	test_feraiseexcept();
552315121Sngie	printf("ok 4 - fenv\n");
553315121Sngie	test_fegsetround();
554315121Sngie	printf("ok 5 - fenv\n");
555315121Sngie	test_fegsetenv();
556315121Sngie	printf("ok 6 - fenv\n");
557315121Sngie	test_masking();
558315121Sngie	printf("ok 7 - fenv\n");
559315121Sngie	test_feholdupdate();
560315121Sngie	printf("ok 8 - fenv\n");
561130330Sdas
562315121Sngie	return (0);
563130330Sdas}
564