1/*-
2 * Copyright (c) 2004 David Schultz <das@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * Test the correctness and C99-compliance of various fenv.h features.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <sys/types.h>
35#include <sys/wait.h>
36#include <assert.h>
37#include <err.h>
38#include <fenv.h>
39#include <float.h>
40#include <math.h>
41#include <signal.h>
42#include <stdio.h>
43#include <string.h>
44#include <unistd.h>
45
46/*
47 * Implementations are permitted to define additional exception flags
48 * not specified in the standard, so it is not necessarily true that
49 * FE_ALL_EXCEPT == ALL_STD_EXCEPT.
50 */
51#define	ALL_STD_EXCEPT	(FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \
52			 FE_OVERFLOW | FE_UNDERFLOW)
53
54#define	NEXCEPTS	(sizeof(std_excepts) / sizeof(std_excepts[0]))
55
56static const int std_excepts[] = {
57	FE_INVALID,
58	FE_DIVBYZERO,
59	FE_OVERFLOW,
60	FE_UNDERFLOW,
61	FE_INEXACT,
62};
63
64/* init_exceptsets() initializes this to the power set of std_excepts[] */
65static int std_except_sets[1 << NEXCEPTS];
66
67static void init_exceptsets(void);
68
69static void test_dfl_env(void);
70static void test_fegsetenv(void);
71static void test_fegsetexceptflag(void);
72static void test_masking(void);
73static void test_fegsetround(void);
74static void test_feholdupdate(void);
75static void test_feraiseexcept(void);
76static void test_fetestclearexcept(void);
77
78static int getround(void);
79static void raiseexcept(int excepts);
80static void trap_handler(int sig);
81
82#pragma STDC FENV_ACCESS ON
83
84int
85main(int argc, char *argv[])
86{
87
88	printf("1..8\n");
89	init_exceptsets();
90	test_dfl_env();
91	printf("ok 1 - fenv\n");
92	test_fetestclearexcept();
93	printf("ok 2 - fenv\n");
94	test_fegsetexceptflag();
95	printf("ok 3 - fenv\n");
96	test_feraiseexcept();
97	printf("ok 4 - fenv\n");
98	test_fegsetround();
99	printf("ok 5 - fenv\n");
100	test_fegsetenv();
101	printf("ok 6 - fenv\n");
102	test_masking();
103	printf("ok 7 - fenv\n");
104	test_feholdupdate();
105	printf("ok 8 - fenv\n");
106
107	return (0);
108}
109
110/*
111 * Initialize std_except_sets[] to the power set of std_excepts[]
112 */
113void
114init_exceptsets(void)
115{
116	int i, j, sr;
117
118	for (i = 0; i < 1 << NEXCEPTS; i++) {
119		for (sr = i, j = 0; sr != 0; sr >>= 1, j++)
120			std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1);
121	}
122}
123
124/*
125 * This tests checks the default FP environment, so it must be first.
126 * The memcmp() test below may be too much to ask for, since there
127 * could be multiple machine-specific default environments.
128 */
129static void
130test_dfl_env(void)
131{
132#ifndef NO_STRICT_DFL_ENV
133	fenv_t env;
134
135	fegetenv(&env);
136	assert(memcmp(&env, FE_DFL_ENV, sizeof(env)) == 0);
137#endif
138	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
139}
140
141/*
142 * Test fetestexcept() and feclearexcept().
143 */
144static void
145test_fetestclearexcept(void)
146{
147	int excepts, i;
148
149	for (i = 0; i < 1 << NEXCEPTS; i++)
150		assert(fetestexcept(std_except_sets[i]) == 0);
151	for (i = 0; i < 1 << NEXCEPTS; i++) {
152		excepts = std_except_sets[i];
153
154		/* FE_ALL_EXCEPT might be special-cased, as on i386. */
155		raiseexcept(excepts);
156		assert(fetestexcept(excepts) == excepts);
157		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
158		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
159
160		raiseexcept(excepts);
161		assert(fetestexcept(excepts) == excepts);
162		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
163			excepts |= FE_INEXACT;
164			assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
165			    excepts);
166		} else {
167			assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
168		}
169		assert(feclearexcept(excepts) == 0);
170		assert(fetestexcept(ALL_STD_EXCEPT) == 0);
171	}
172}
173
174/*
175 * Test fegetexceptflag() and fesetexceptflag().
176 *
177 * Prerequisites: fetestexcept(), feclearexcept()
178 */
179static void
180test_fegsetexceptflag(void)
181{
182	fexcept_t flag;
183	int excepts, i;
184
185	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
186	for (i = 0; i < 1 << NEXCEPTS; i++) {
187		excepts = std_except_sets[i];
188
189		assert(fegetexceptflag(&flag, excepts) == 0);
190		raiseexcept(ALL_STD_EXCEPT);
191		assert(fesetexceptflag(&flag, excepts) == 0);
192		assert(fetestexcept(ALL_STD_EXCEPT) ==
193		    (ALL_STD_EXCEPT ^ excepts));
194
195		assert(fegetexceptflag(&flag, FE_ALL_EXCEPT) == 0);
196		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
197		assert(fesetexceptflag(&flag, excepts) == 0);
198		assert(fetestexcept(ALL_STD_EXCEPT) == 0);
199		assert(fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts) == 0);
200		assert(fetestexcept(ALL_STD_EXCEPT) ==
201		    (ALL_STD_EXCEPT ^ excepts));
202
203		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
204	}
205}
206
207/*
208 * Test feraiseexcept().
209 *
210 * Prerequisites: fetestexcept(), feclearexcept()
211 */
212static void
213test_feraiseexcept(void)
214{
215	int excepts, i;
216
217	for (i = 0; i < 1 << NEXCEPTS; i++) {
218		excepts = std_except_sets[i];
219
220		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
221		assert(feraiseexcept(excepts) == 0);
222		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
223			excepts |= FE_INEXACT;
224			assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
225			    excepts);
226		} else {
227			assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
228		}
229		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
230	}
231	assert(feraiseexcept(FE_INVALID | FE_DIVBYZERO) == 0);
232	assert(fetestexcept(ALL_STD_EXCEPT) == (FE_INVALID | FE_DIVBYZERO));
233	assert(feraiseexcept(FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) == 0);
234	assert(fetestexcept(ALL_STD_EXCEPT) == ALL_STD_EXCEPT);
235	assert(feclearexcept(FE_ALL_EXCEPT) == 0);
236}
237
238/*
239 * Test fegetround() and fesetround().
240 */
241static void
242test_fegsetround(void)
243{
244
245	assert(fegetround() == FE_TONEAREST);
246	assert(getround() == FE_TONEAREST);
247	assert(FLT_ROUNDS == 1);
248
249	assert(fesetround(FE_DOWNWARD) == 0);
250	assert(fegetround() == FE_DOWNWARD);
251	assert(getround() == FE_DOWNWARD);
252	assert(FLT_ROUNDS == 3);
253
254	assert(fesetround(FE_UPWARD) == 0);
255	assert(getround() == FE_UPWARD);
256	assert(fegetround() == FE_UPWARD);
257	assert(FLT_ROUNDS == 2);
258
259	assert(fesetround(FE_TOWARDZERO) == 0);
260	assert(getround() == FE_TOWARDZERO);
261	assert(fegetround() == FE_TOWARDZERO);
262	assert(FLT_ROUNDS == 0);
263
264	assert(fesetround(FE_TONEAREST) == 0);
265	assert(getround() == FE_TONEAREST);
266	assert(FLT_ROUNDS == 1);
267
268	assert(feclearexcept(FE_ALL_EXCEPT) == 0);
269}
270
271/*
272 * Test fegetenv() and fesetenv().
273 *
274 * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround()
275 */
276static void
277test_fegsetenv(void)
278{
279	fenv_t env1, env2;
280	int excepts, i;
281
282	for (i = 0; i < 1 << NEXCEPTS; i++) {
283		excepts = std_except_sets[i];
284
285		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
286		assert(fegetround() == FE_TONEAREST);
287		assert(fegetenv(&env1) == 0);
288
289		/*
290		 * fe[gs]etenv() should be able to save and restore
291		 * exception flags without the spurious inexact
292		 * exceptions that afflict raiseexcept().
293		 */
294		raiseexcept(excepts);
295		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 &&
296		    (excepts & FE_INEXACT) == 0)
297			assert(feclearexcept(FE_INEXACT) == 0);
298
299		fesetround(FE_DOWNWARD);
300		assert(fegetenv(&env2) == 0);
301		assert(fesetenv(&env1) == 0);
302		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
303		assert(fegetround() == FE_TONEAREST);
304
305		assert(fesetenv(&env2) == 0);
306		assert(fetestexcept(FE_ALL_EXCEPT) == excepts);
307		assert(fegetround() == FE_DOWNWARD);
308		assert(fesetenv(&env1) == 0);
309		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
310		assert(fegetround() == FE_TONEAREST);
311	}
312}
313
314/*
315 * Test fegetexcept(), fedisableexcept(), and feenableexcept().
316 *
317 * Prerequisites: fetestexcept(), feraiseexcept()
318 */
319static void
320test_masking(void)
321{
322	struct sigaction act;
323	int except, i, pass, raise, status;
324
325	assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
326	assert((feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT) == 0);
327	assert((feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT) ==
328	    (FE_INVALID | FE_OVERFLOW));
329	assert((fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT) ==
330	    (FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW));
331	assert((fegetexcept() & ALL_STD_EXCEPT) == (FE_INVALID | FE_UNDERFLOW));
332	assert((fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT) ==
333	    (FE_INVALID | FE_UNDERFLOW));
334	assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
335
336	sigemptyset(&act.sa_mask);
337	act.sa_flags = 0;
338	act.sa_handler = trap_handler;
339	for (pass = 0; pass < 2; pass++) {
340		for (i = 0; i < NEXCEPTS; i++) {
341			except = std_excepts[i];
342			/* over/underflow may also raise inexact */
343			if (except == FE_INEXACT)
344				raise = FE_DIVBYZERO | FE_INVALID;
345			else
346				raise = ALL_STD_EXCEPT ^ except;
347
348			/*
349			 * We need to fork a child process because
350			 * there isn't a portable way to recover from
351			 * a floating-point exception.
352			 */
353			switch(fork()) {
354			case 0:		/* child */
355				assert((fegetexcept() & ALL_STD_EXCEPT) == 0);
356				assert((feenableexcept(except)
357					   & ALL_STD_EXCEPT) == 0);
358				assert(fegetexcept() == except);
359				raiseexcept(raise);
360				assert(feraiseexcept(raise) == 0);
361				assert(fetestexcept(ALL_STD_EXCEPT) == raise);
362
363				assert(sigaction(SIGFPE, &act, NULL) == 0);
364				switch (pass) {
365				case 0:
366					raiseexcept(except);
367				case 1:
368					feraiseexcept(except);
369				default:
370					assert(0);
371				}
372				assert(0);
373			default:	/* parent */
374				assert(wait(&status) > 0);
375				/*
376				 * Avoid assert() here so that it's possible
377				 * to examine a failed child's core dump.
378				 */
379				if (!WIFEXITED(status))
380					errx(1, "child aborted\n");
381				assert(WEXITSTATUS(status) == 0);
382				break;
383			case -1:	/* error */
384				assert(0);
385			}
386		}
387	}
388	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
389}
390
391/*
392 * Test feholdexcept() and feupdateenv().
393 *
394 * Prerequisites: fetestexcept(), fegetround(), fesetround(),
395 *	fedisableexcept(), feenableexcept()
396 */
397static void
398test_feholdupdate(void)
399{
400	fenv_t env;
401
402	struct sigaction act;
403	int except, i, pass, status, raise;
404
405	sigemptyset(&act.sa_mask);
406	act.sa_flags = 0;
407	act.sa_handler = trap_handler;
408	for (pass = 0; pass < 2; pass++) {
409		for (i = 0; i < NEXCEPTS; i++) {
410			except = std_excepts[i];
411			/* over/underflow may also raise inexact */
412			if (except == FE_INEXACT)
413				raise = FE_DIVBYZERO | FE_INVALID;
414			else
415				raise = ALL_STD_EXCEPT ^ except;
416
417			/*
418			 * We need to fork a child process because
419			 * there isn't a portable way to recover from
420			 * a floating-point exception.
421			 */
422			switch(fork()) {
423			case 0:		/* child */
424				/*
425				 * We don't want to cause a fatal exception in
426				 * the child until the second pass, so we can
427				 * check other properties of feupdateenv().
428				 */
429				if (pass == 1)
430					assert((feenableexcept(except) &
431						   ALL_STD_EXCEPT) == 0);
432				raiseexcept(raise);
433				assert(fesetround(FE_DOWNWARD) == 0);
434				assert(feholdexcept(&env) == 0);
435				assert(fetestexcept(FE_ALL_EXCEPT) == 0);
436				raiseexcept(except);
437				assert(fesetround(FE_UPWARD) == 0);
438
439				if (pass == 1)
440					assert(sigaction(SIGFPE, &act, NULL) ==
441					    0);
442				assert(feupdateenv(&env) == 0);
443				assert(fegetround() == FE_DOWNWARD);
444				assert(fetestexcept(ALL_STD_EXCEPT) ==
445				    (except | raise));
446
447				assert(pass == 0);
448				_exit(0);
449			default:	/* parent */
450				assert(wait(&status) > 0);
451				/*
452				 * Avoid assert() here so that it's possible
453				 * to examine a failed child's core dump.
454				 */
455				if (!WIFEXITED(status))
456					errx(1, "child aborted\n");
457				assert(WEXITSTATUS(status) == 0);
458				break;
459			case -1:	/* error */
460				assert(0);
461			}
462		}
463	}
464	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
465}
466
467/*
468 * Raise a floating-point exception without relying on the standard
469 * library routines, which we are trying to test.
470 *
471 * XXX We can't raise an {over,under}flow without also raising an
472 * inexact exception.
473 */
474static void
475raiseexcept(int excepts)
476{
477	volatile double d;
478
479	/*
480	 * With a compiler that supports the FENV_ACCESS pragma
481	 * properly, simple expressions like '0.0 / 0.0' should
482	 * be sufficient to generate traps.  Unfortunately, we
483	 * need to bring a volatile variable into the equation
484	 * to prevent incorrect optimizations.
485	 */
486	if (excepts & FE_INVALID) {
487		d = 0.0;
488		d = 0.0 / d;
489	}
490	if (excepts & FE_DIVBYZERO) {
491		d = 0.0;
492		d = 1.0 / d;
493	}
494	if (excepts & FE_OVERFLOW) {
495		d = DBL_MAX;
496		d *= 2.0;
497	}
498	if (excepts & FE_UNDERFLOW) {
499		d = DBL_MIN;
500		d /= DBL_MAX;
501	}
502	if (excepts & FE_INEXACT) {
503		d = DBL_MIN;
504		d += 1.0;
505	}
506
507	/*
508	 * On the x86 (and some other architectures?) the FPU and
509	 * integer units are decoupled.  We need to execute an FWAIT
510	 * or a floating-point instruction to get synchronous exceptions.
511	 */
512	d = 1.0;
513	d += 1.0;
514}
515
516/*
517 * Determine the current rounding mode without relying on the fenv
518 * routines.  This function may raise an inexact exception.
519 */
520static int
521getround(void)
522{
523	volatile double d;
524
525	/*
526	 * This test works just as well with 0.0 - 0.0, except on ia64
527	 * where 0.0 - 0.0 gives the wrong sign when rounding downwards.
528	 */
529	d = 1.0;
530	d -= 1.0;
531	if (copysign(1.0, d) < 0.0)
532		return (FE_DOWNWARD);
533
534	d = 1.0;
535	if (d + (DBL_EPSILON * 3.0 / 4.0) == 1.0)
536		return (FE_TOWARDZERO);
537	if (d + (DBL_EPSILON * 1.0 / 4.0) > 1.0)
538		return (FE_UPWARD);
539
540	return (FE_TONEAREST);
541}
542
543static void
544trap_handler(int sig)
545{
546
547	assert(sig == SIGFPE);
548	_exit(0);
549}
550