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$");
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
67130330Sdasstatic void init_exceptsets(void);
68130330Sdas
69130330Sdasstatic void test_dfl_env(void);
70130330Sdasstatic void test_fegsetenv(void);
71130330Sdasstatic void test_fegsetexceptflag(void);
72143710Sdasstatic void test_masking(void);
73130330Sdasstatic void test_fegsetround(void);
74130330Sdasstatic void test_feholdupdate(void);
75130330Sdasstatic void test_feraiseexcept(void);
76130330Sdasstatic void test_fetestclearexcept(void);
77130330Sdas
78130330Sdasstatic int getround(void);
79130330Sdasstatic void raiseexcept(int excepts);
80130330Sdasstatic void trap_handler(int sig);
81130330Sdas
82130330Sdas#pragma STDC FENV_ACCESS ON
83130330Sdas
84130330Sdasint
85130330Sdasmain(int argc, char *argv[])
86130330Sdas{
87130330Sdas
88143710Sdas	printf("1..8\n");
89130330Sdas	init_exceptsets();
90130330Sdas	test_dfl_env();
91143710Sdas	printf("ok 1 - fenv\n");
92130330Sdas	test_fetestclearexcept();
93143710Sdas	printf("ok 2 - fenv\n");
94130330Sdas	test_fegsetexceptflag();
95143710Sdas	printf("ok 3 - fenv\n");
96130330Sdas	test_feraiseexcept();
97143710Sdas	printf("ok 4 - fenv\n");
98130330Sdas	test_fegsetround();
99143710Sdas	printf("ok 5 - fenv\n");
100130330Sdas	test_fegsetenv();
101143710Sdas	printf("ok 6 - fenv\n");
102143710Sdas	test_masking();
103143710Sdas	printf("ok 7 - fenv\n");
104130330Sdas	test_feholdupdate();
105143710Sdas	printf("ok 8 - fenv\n");
106130330Sdas
107130330Sdas	return (0);
108130330Sdas}
109130330Sdas
110130330Sdas/*
111130330Sdas * Initialize std_except_sets[] to the power set of std_excepts[]
112130330Sdas */
113130330Sdasvoid
114130330Sdasinit_exceptsets(void)
115130330Sdas{
116130330Sdas	int i, j, sr;
117130330Sdas
118130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
119130330Sdas		for (sr = i, j = 0; sr != 0; sr >>= 1, j++)
120130330Sdas			std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1);
121130330Sdas	}
122130330Sdas}
123130330Sdas
124130330Sdas/*
125130330Sdas * This tests checks the default FP environment, so it must be first.
126130330Sdas * The memcmp() test below may be too much to ask for, since there
127130330Sdas * could be multiple machine-specific default environments.
128130330Sdas */
129130330Sdasstatic void
130130330Sdastest_dfl_env(void)
131130330Sdas{
132130330Sdas#ifndef NO_STRICT_DFL_ENV
133130330Sdas	fenv_t env;
134130330Sdas
135130330Sdas	fegetenv(&env);
136130330Sdas	assert(memcmp(&env, FE_DFL_ENV, sizeof(env)) == 0);
137130330Sdas#endif
138130330Sdas	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
139130330Sdas}
140130330Sdas
141130330Sdas/*
142130330Sdas * Test fetestexcept() and feclearexcept().
143130330Sdas */
144130330Sdasstatic void
145130330Sdastest_fetestclearexcept(void)
146130330Sdas{
147130330Sdas	int excepts, i;
148130330Sdas
149130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++)
150130330Sdas		assert(fetestexcept(std_except_sets[i]) == 0);
151130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
152130330Sdas		excepts = std_except_sets[i];
153130330Sdas
154130330Sdas		/* FE_ALL_EXCEPT might be special-cased, as on i386. */
155130330Sdas		raiseexcept(excepts);
156130330Sdas		assert(fetestexcept(excepts) == excepts);
157130330Sdas		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
158130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
159130330Sdas
160130330Sdas		raiseexcept(excepts);
161130330Sdas		assert(fetestexcept(excepts) == excepts);
162130330Sdas		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
163130330Sdas			excepts |= FE_INEXACT;
164130330Sdas			assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
165130330Sdas			    excepts);
166130330Sdas		} else {
167130330Sdas			assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
168130330Sdas		}
169130330Sdas		assert(feclearexcept(excepts) == 0);
170130330Sdas		assert(fetestexcept(ALL_STD_EXCEPT) == 0);
171130330Sdas	}
172130330Sdas}
173130330Sdas
174130330Sdas/*
175130330Sdas * Test fegetexceptflag() and fesetexceptflag().
176130330Sdas *
177130330Sdas * Prerequisites: fetestexcept(), feclearexcept()
178130330Sdas */
179130330Sdasstatic void
180130330Sdastest_fegsetexceptflag(void)
181130330Sdas{
182130330Sdas	fexcept_t flag;
183130330Sdas	int excepts, i;
184130330Sdas
185130330Sdas	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
186130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
187130330Sdas		excepts = std_except_sets[i];
188130330Sdas
189130330Sdas		assert(fegetexceptflag(&flag, excepts) == 0);
190130330Sdas		raiseexcept(ALL_STD_EXCEPT);
191130330Sdas		assert(fesetexceptflag(&flag, excepts) == 0);
192130330Sdas		assert(fetestexcept(ALL_STD_EXCEPT) ==
193130330Sdas		    (ALL_STD_EXCEPT ^ excepts));
194130330Sdas
195130330Sdas		assert(fegetexceptflag(&flag, FE_ALL_EXCEPT) == 0);
196130330Sdas		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
197130330Sdas		assert(fesetexceptflag(&flag, excepts) == 0);
198130330Sdas		assert(fetestexcept(ALL_STD_EXCEPT) == 0);
199130330Sdas		assert(fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts) == 0);
200130330Sdas		assert(fetestexcept(ALL_STD_EXCEPT) ==
201130330Sdas		    (ALL_STD_EXCEPT ^ excepts));
202130330Sdas
203130330Sdas		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
204130330Sdas	}
205130330Sdas}
206130330Sdas
207130330Sdas/*
208130330Sdas * Test feraiseexcept().
209130330Sdas *
210130330Sdas * Prerequisites: fetestexcept(), feclearexcept()
211130330Sdas */
212130330Sdasstatic void
213130330Sdastest_feraiseexcept(void)
214130330Sdas{
215130330Sdas	int excepts, i;
216130330Sdas
217130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
218130330Sdas		excepts = std_except_sets[i];
219130330Sdas
220130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
221130330Sdas		assert(feraiseexcept(excepts) == 0);
222130330Sdas		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
223130330Sdas			excepts |= FE_INEXACT;
224130330Sdas			assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
225130330Sdas			    excepts);
226130330Sdas		} else {
227130330Sdas			assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
228130330Sdas		}
229130330Sdas		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
230130330Sdas	}
231130330Sdas	assert(feraiseexcept(FE_INVALID | FE_DIVBYZERO) == 0);
232130330Sdas	assert(fetestexcept(ALL_STD_EXCEPT) == (FE_INVALID | FE_DIVBYZERO));
233130330Sdas	assert(feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) == 0);
234130330Sdas	assert(fetestexcept(ALL_STD_EXCEPT) == ALL_STD_EXCEPT);
235130330Sdas	assert(feclearexcept(FE_ALL_EXCEPT) == 0);
236130330Sdas}
237130330Sdas
238130330Sdas/*
239130330Sdas * Test fegetround() and fesetround().
240130330Sdas */
241130330Sdasstatic void
242130330Sdastest_fegsetround(void)
243130330Sdas{
244130330Sdas
245130330Sdas	assert(fegetround() == FE_TONEAREST);
246130330Sdas	assert(getround() == FE_TONEAREST);
247132384Sdas	assert(FLT_ROUNDS == 1);
248132384Sdas
249130330Sdas	assert(fesetround(FE_DOWNWARD) == 0);
250130330Sdas	assert(fegetround() == FE_DOWNWARD);
251130330Sdas	assert(getround() == FE_DOWNWARD);
252132384Sdas	assert(FLT_ROUNDS == 3);
253132384Sdas
254130330Sdas	assert(fesetround(FE_UPWARD) == 0);
255130330Sdas	assert(getround() == FE_UPWARD);
256130330Sdas	assert(fegetround() == FE_UPWARD);
257132384Sdas	assert(FLT_ROUNDS == 2);
258132384Sdas
259130330Sdas	assert(fesetround(FE_TOWARDZERO) == 0);
260130330Sdas	assert(getround() == FE_TOWARDZERO);
261130330Sdas	assert(fegetround() == FE_TOWARDZERO);
262132384Sdas	assert(FLT_ROUNDS == 0);
263132384Sdas
264130330Sdas	assert(fesetround(FE_TONEAREST) == 0);
265130330Sdas	assert(getround() == FE_TONEAREST);
266132384Sdas	assert(FLT_ROUNDS == 1);
267132384Sdas
268130330Sdas	assert(feclearexcept(FE_ALL_EXCEPT) == 0);
269130330Sdas}
270130330Sdas
271130330Sdas/*
272130330Sdas * Test fegetenv() and fesetenv().
273130330Sdas *
274130330Sdas * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround()
275130330Sdas */
276130330Sdasstatic void
277130330Sdastest_fegsetenv(void)
278130330Sdas{
279130330Sdas	fenv_t env1, env2;
280130330Sdas	int excepts, i;
281130330Sdas
282130330Sdas	for (i = 0; i < 1 << NEXCEPTS; i++) {
283130330Sdas		excepts = std_except_sets[i];
284130330Sdas
285130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
286130330Sdas		assert(fegetround() == FE_TONEAREST);
287130330Sdas		assert(fegetenv(&env1) == 0);
288130330Sdas
289130330Sdas		/*
290130330Sdas		 * fe[gs]etenv() should be able to save and restore
291130330Sdas		 * exception flags without the spurious inexact
292130330Sdas		 * exceptions that afflict raiseexcept().
293130330Sdas		 */
294130330Sdas		raiseexcept(excepts);
295130330Sdas		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 &&
296130330Sdas		    (excepts & FE_INEXACT) == 0)
297130330Sdas			assert(feclearexcept(FE_INEXACT) == 0);
298130330Sdas
299130330Sdas		fesetround(FE_DOWNWARD);
300130330Sdas		assert(fegetenv(&env2) == 0);
301130330Sdas		assert(fesetenv(&env1) == 0);
302130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
303130330Sdas		assert(fegetround() == FE_TONEAREST);
304130330Sdas
305130330Sdas		assert(fesetenv(&env2) == 0);
306130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == excepts);
307130330Sdas		assert(fegetround() == FE_DOWNWARD);
308130330Sdas		assert(fesetenv(&env1) == 0);
309130330Sdas		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
310130330Sdas		assert(fegetround() == FE_TONEAREST);
311130330Sdas	}
312130330Sdas}
313130330Sdas
314130330Sdas/*
315143710Sdas * Test fegetexcept(), fedisableexcept(), and feenableexcept().
316130330Sdas *
317130330Sdas * Prerequisites: fetestexcept(), feraiseexcept()
318130330Sdas */
319130330Sdasstatic void
320143710Sdastest_masking(void)
321130330Sdas{
322130330Sdas	struct sigaction act;
323130330Sdas	int except, i, pass, raise, status;
324130330Sdas
325143710Sdas	assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
326143710Sdas	assert((feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT) == 0);
327143710Sdas	assert((feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT) ==
328143710Sdas	    (FE_INVALID | FE_OVERFLOW));
329143710Sdas	assert((fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT) ==
330143710Sdas	    (FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW));
331143710Sdas	assert((fegetexcept() & ALL_STD_EXCEPT) == (FE_INVALID | FE_UNDERFLOW));
332143710Sdas	assert((fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT) ==
333143710Sdas	    (FE_INVALID | FE_UNDERFLOW));
334143710Sdas	assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
335143710Sdas
336130330Sdas	sigemptyset(&act.sa_mask);
337130330Sdas	act.sa_flags = 0;
338130330Sdas	act.sa_handler = trap_handler;
339130330Sdas	for (pass = 0; pass < 2; pass++) {
340130330Sdas		for (i = 0; i < NEXCEPTS; i++) {
341130330Sdas			except = std_excepts[i];
342130330Sdas			/* over/underflow may also raise inexact */
343130330Sdas			if (except == FE_INEXACT)
344130330Sdas				raise = FE_DIVBYZERO | FE_INVALID;
345130330Sdas			else
346130330Sdas				raise = ALL_STD_EXCEPT ^ except;
347130330Sdas
348130330Sdas			/*
349130330Sdas			 * We need to fork a child process because
350130330Sdas			 * there isn't a portable way to recover from
351130330Sdas			 * a floating-point exception.
352130330Sdas			 */
353130330Sdas			switch(fork()) {
354130330Sdas			case 0:		/* child */
355143710Sdas				assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
356143710Sdas				assert((feenableexcept(except)
357143710Sdas					   & ALL_STD_EXCEPT) == 0);
358143710Sdas				assert(fegetexcept() == except);
359130330Sdas				raiseexcept(raise);
360130330Sdas				assert(feraiseexcept(raise) == 0);
361130330Sdas				assert(fetestexcept(ALL_STD_EXCEPT) == raise);
362130330Sdas
363130330Sdas				assert(sigaction(SIGFPE, &act, NULL) == 0);
364130330Sdas				switch (pass) {
365130330Sdas				case 0:
366130330Sdas					raiseexcept(except);
367130330Sdas				case 1:
368130330Sdas					feraiseexcept(except);
369130330Sdas				default:
370130330Sdas					assert(0);
371130330Sdas				}
372130330Sdas				assert(0);
373130330Sdas			default:	/* parent */
374130330Sdas				assert(wait(&status) > 0);
375130330Sdas				/*
376130330Sdas				 * Avoid assert() here so that it's possible
377130330Sdas				 * to examine a failed child's core dump.
378130330Sdas				 */
379130330Sdas				if (!WIFEXITED(status))
380130330Sdas					errx(1, "child aborted\n");
381130330Sdas				assert(WEXITSTATUS(status) == 0);
382130330Sdas				break;
383130330Sdas			case -1:	/* error */
384130330Sdas				assert(0);
385130330Sdas			}
386130330Sdas		}
387130330Sdas	}
388130330Sdas	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
389130330Sdas}
390130330Sdas
391130330Sdas/*
392130330Sdas * Test feholdexcept() and feupdateenv().
393130330Sdas *
394143710Sdas * Prerequisites: fetestexcept(), fegetround(), fesetround(),
395143710Sdas *	fedisableexcept(), feenableexcept()
396130330Sdas */
397130330Sdasstatic void
398130330Sdastest_feholdupdate(void)
399130330Sdas{
400130330Sdas	fenv_t env;
401130330Sdas
402130330Sdas	struct sigaction act;
403130330Sdas	int except, i, pass, status, raise;
404130330Sdas
405130330Sdas	sigemptyset(&act.sa_mask);
406130330Sdas	act.sa_flags = 0;
407130330Sdas	act.sa_handler = trap_handler;
408130330Sdas	for (pass = 0; pass < 2; pass++) {
409130330Sdas		for (i = 0; i < NEXCEPTS; i++) {
410130330Sdas			except = std_excepts[i];
411130330Sdas			/* over/underflow may also raise inexact */
412130330Sdas			if (except == FE_INEXACT)
413130330Sdas				raise = FE_DIVBYZERO | FE_INVALID;
414130330Sdas			else
415130330Sdas				raise = ALL_STD_EXCEPT ^ except;
416130330Sdas
417130330Sdas			/*
418130330Sdas			 * We need to fork a child process because
419130330Sdas			 * there isn't a portable way to recover from
420130330Sdas			 * a floating-point exception.
421130330Sdas			 */
422130330Sdas			switch(fork()) {
423130330Sdas			case 0:		/* child */
424130330Sdas				/*
425130330Sdas				 * We don't want to cause a fatal exception in
426130330Sdas				 * the child until the second pass, so we can
427130330Sdas				 * check other properties of feupdateenv().
428130330Sdas				 */
429130330Sdas				if (pass == 1)
430143710Sdas					assert((feenableexcept(except) &
431130330Sdas						   ALL_STD_EXCEPT) == 0);
432130330Sdas				raiseexcept(raise);
433130330Sdas				assert(fesetround(FE_DOWNWARD) == 0);
434130330Sdas				assert(feholdexcept(&env) == 0);
435130330Sdas				assert(fetestexcept(FE_ALL_EXCEPT) == 0);
436130330Sdas				raiseexcept(except);
437130330Sdas				assert(fesetround(FE_UPWARD) == 0);
438130330Sdas
439130330Sdas				if (pass == 1)
440130330Sdas					assert(sigaction(SIGFPE, &act, NULL) ==
441130330Sdas					    0);
442130330Sdas				assert(feupdateenv(&env) == 0);
443130330Sdas				assert(fegetround() == FE_DOWNWARD);
444130330Sdas				assert(fetestexcept(ALL_STD_EXCEPT) ==
445130330Sdas				    (except | raise));
446130330Sdas
447130330Sdas				assert(pass == 0);
448130330Sdas				_exit(0);
449130330Sdas			default:	/* parent */
450130330Sdas				assert(wait(&status) > 0);
451130330Sdas				/*
452130330Sdas				 * Avoid assert() here so that it's possible
453130330Sdas				 * to examine a failed child's core dump.
454130330Sdas				 */
455130330Sdas				if (!WIFEXITED(status))
456130330Sdas					errx(1, "child aborted\n");
457130330Sdas				assert(WEXITSTATUS(status) == 0);
458130330Sdas				break;
459130330Sdas			case -1:	/* error */
460130330Sdas				assert(0);
461130330Sdas			}
462130330Sdas		}
463130330Sdas	}
464130330Sdas	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
465130330Sdas}
466130330Sdas
467130330Sdas/*
468130330Sdas * Raise a floating-point exception without relying on the standard
469130330Sdas * library routines, which we are trying to test.
470130330Sdas *
471130330Sdas * XXX We can't raise an {over,under}flow without also raising an
472130330Sdas * inexact exception.
473130330Sdas */
474130330Sdasstatic void
475130330Sdasraiseexcept(int excepts)
476130330Sdas{
477130330Sdas	volatile double d;
478130330Sdas
479130330Sdas	/*
480130330Sdas	 * With a compiler that supports the FENV_ACCESS pragma
481130330Sdas	 * properly, simple expressions like '0.0 / 0.0' should
482130330Sdas	 * be sufficient to generate traps.  Unfortunately, we
483130330Sdas	 * need to bring a volatile variable into the equation
484130330Sdas	 * to prevent incorrect optimizations.
485130330Sdas	 */
486130330Sdas	if (excepts & FE_INVALID) {
487130330Sdas		d = 0.0;
488130330Sdas		d = 0.0 / d;
489130330Sdas	}
490130330Sdas	if (excepts & FE_DIVBYZERO) {
491130330Sdas		d = 0.0;
492130330Sdas		d = 1.0 / d;
493130330Sdas	}
494130330Sdas	if (excepts & FE_OVERFLOW) {
495130330Sdas		d = DBL_MAX;
496130330Sdas		d *= 2.0;
497130330Sdas	}
498130330Sdas	if (excepts & FE_UNDERFLOW) {
499130330Sdas		d = DBL_MIN;
500130330Sdas		d /= DBL_MAX;
501130330Sdas	}
502130330Sdas	if (excepts & FE_INEXACT) {
503130330Sdas		d = DBL_MIN;
504130330Sdas		d += 1.0;
505130330Sdas	}
506130330Sdas
507130330Sdas	/*
508130330Sdas	 * On the x86 (and some other architectures?) the FPU and
509130330Sdas	 * integer units are decoupled.  We need to execute an FWAIT
510130330Sdas	 * or a floating-point instruction to get synchronous exceptions.
511130330Sdas	 */
512130330Sdas	d = 1.0;
513130330Sdas	d += 1.0;
514130330Sdas}
515130330Sdas
516130330Sdas/*
517130330Sdas * Determine the current rounding mode without relying on the fenv
518130330Sdas * routines.  This function may raise an inexact exception.
519130330Sdas */
520130330Sdasstatic int
521130330Sdasgetround(void)
522130330Sdas{
523130330Sdas	volatile double d;
524130330Sdas
525130330Sdas	/*
526130330Sdas	 * This test works just as well with 0.0 - 0.0, except on ia64
527130330Sdas	 * where 0.0 - 0.0 gives the wrong sign when rounding downwards.
528130330Sdas	 */
529130330Sdas	d = 1.0;
530130330Sdas	d -= 1.0;
531130330Sdas	if (copysign(1.0, d) < 0.0)
532130330Sdas		return (FE_DOWNWARD);
533130330Sdas
534130330Sdas	d = 1.0;
535130330Sdas	if (d + (DBL_EPSILON * 3.0 / 4.0) == 1.0)
536130330Sdas		return (FE_TOWARDZERO);
537130330Sdas	if (d + (DBL_EPSILON * 1.0 / 4.0) > 1.0)
538130330Sdas		return (FE_UPWARD);
539130330Sdas
540130330Sdas	return (FE_TONEAREST);
541130330Sdas}
542130330Sdas
543130330Sdasstatic void
544130330Sdastrap_handler(int sig)
545130330Sdas{
546130330Sdas
547130330Sdas	assert(sig == SIGFPE);
548130330Sdas	_exit(0);
549130330Sdas}
550