fenv_test.c revision 143710
150472Speter/*-
237Srgrimes * Copyright (c) 2004 David Schultz <das@FreeBSD.org>
337Srgrimes * All rights reserved.
437Srgrimes *
537Srgrimes * Redistribution and use in source and binary forms, with or without
637Srgrimes * modification, are permitted provided that the following conditions
737Srgrimes * are met:
837Srgrimes * 1. Redistributions of source code must retain the above copyright
937Srgrimes *    notice, this list of conditions and the following disclaimer.
109306Sbde * 2. Redistributions in binary form must reproduce the above copyright
1137Srgrimes *    notice, this list of conditions and the following disclaimer in the
12646Sdg *    documentation and/or other materials provided with the distribution.
139306Sbde *
14646Sdg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
156489Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
166489Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
176489Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
186489Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
196489Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
209306Sbde * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
219306Sbde * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22119915Syar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23646Sdg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24646Sdg * SUCH DAMAGE.
25646Sdg */
26119915Syar
27646Sdg/*
28646Sdg * Test the correctness and C99-compliance of various fenv.h features.
29646Sdg */
30646Sdg
31646Sdg#include <sys/cdefs.h>
32646Sdg__FBSDID("$FreeBSD: head/tools/regression/lib/msun/test-fenv.c 143710 2005-03-16 19:04:45Z das $");
336489Sjoerg
34646Sdg#include <sys/types.h>
35119915Syar#include <sys/wait.h>
36119915Syar#include <assert.h>
37646Sdg#include <err.h>
3837Srgrimes#include <fenv.h>
3970164Sphk#include <float.h>
4070164Sphk#include <math.h>
4137Srgrimes#include <signal.h>
4237Srgrimes#include <stdio.h>
4337Srgrimes#include <string.h>
4437Srgrimes#include <unistd.h>
4537Srgrimes
4637Srgrimes/*
4737Srgrimes * Implementations are permitted to define additional exception flags
4837Srgrimes * not specified in the standard, so it is not necessarily true that
4937Srgrimes * FE_ALL_EXCEPT == ALL_STD_EXCEPT.
5037Srgrimes */
5137Srgrimes#define	ALL_STD_EXCEPT	(FE_DIVBYZERO | FE_INEXACT | FE_INVALID | \
5237Srgrimes			 FE_OVERFLOW | FE_UNDERFLOW)
53862Sache
5437Srgrimes#define	NEXCEPTS	(sizeof(std_excepts) / sizeof(std_excepts[0]))
55862Sache
5637Srgrimesstatic const int std_excepts[] = {
57862Sache	FE_INVALID,
5837Srgrimes	FE_DIVBYZERO,
59862Sache	FE_OVERFLOW,
6037Srgrimes	FE_UNDERFLOW,
61862Sache	FE_INEXACT,
6237Srgrimes};
63862Sache
6437Srgrimes/* init_exceptsets() initializes this to the power set of std_excepts[] */
65862Sachestatic int std_except_sets[1 << NEXCEPTS];
6637Srgrimes
67862Sachestatic void init_exceptsets(void);
6837Srgrimes
69862Sachestatic void test_dfl_env(void);
7037Srgrimesstatic void test_fegsetenv(void);
71862Sachestatic void test_fegsetexceptflag(void);
72154Srgrimesstatic void test_masking(void);
73862Sachestatic void test_fegsetround(void);
74154Srgrimesstatic void test_feholdupdate(void);
75862Sachestatic void test_feraiseexcept(void);
76154Srgrimesstatic void test_fetestclearexcept(void);
77862Sache
7837818Sphkstatic int getround(void);
7937818Sphkstatic void raiseexcept(int excepts);
8037Srgrimesstatic void trap_handler(int sig);
8137Srgrimes
8229610Sjoerg#pragma STDC FENV_ACCESS ON
8329610Sjoerg
8429610Sjoergint
8529610Sjoergmain(int argc, char *argv[])
8629610Sjoerg{
8729610Sjoerg
8829610Sjoerg	printf("1..8\n");
8929610Sjoerg	init_exceptsets();
9029610Sjoerg	test_dfl_env();
9129610Sjoerg	printf("ok 1 - fenv\n");
9229610Sjoerg	test_fetestclearexcept();
9329610Sjoerg	printf("ok 2 - fenv\n");
9429610Sjoerg	test_fegsetexceptflag();
9529610Sjoerg	printf("ok 3 - fenv\n");
9629610Sjoerg	test_feraiseexcept();
9729610Sjoerg	printf("ok 4 - fenv\n");
9829610Sjoerg	test_fegsetround();
9929610Sjoerg	printf("ok 5 - fenv\n");
10029610Sjoerg	test_fegsetenv();
10129610Sjoerg	printf("ok 6 - fenv\n");
10237Srgrimes	test_masking();
10337Srgrimes	printf("ok 7 - fenv\n");
10437Srgrimes	test_feholdupdate();
10537Srgrimes	printf("ok 8 - fenv\n");
10637Srgrimes
10737Srgrimes	return (0);
10837Srgrimes}
10937Srgrimes
11037Srgrimes/*
11137Srgrimes * Initialize std_except_sets[] to the power set of std_excepts[]
11237Srgrimes */
11337Srgrimesvoid
11437Srgrimesinit_exceptsets(void)
11537Srgrimes{
11637Srgrimes	int i, j, sr;
11737Srgrimes
11837Srgrimes	for (i = 0; i < 1 << NEXCEPTS; i++) {
11937Srgrimes		for (sr = i, j = 0; sr != 0; sr >>= 1, j++)
12037Srgrimes			std_except_sets[i] |= std_excepts[j] & ((~sr & 1) - 1);
12137Srgrimes	}
12237Srgrimes}
12337Srgrimes
12437Srgrimes/*
12537Srgrimes * This tests checks the default FP environment, so it must be first.
12637Srgrimes * The memcmp() test below may be too much to ask for, since there
12737Srgrimes * could be multiple machine-specific default environments.
12837Srgrimes */
12937Srgrimesstatic void
13037Srgrimestest_dfl_env(void)
13137Srgrimes{
13237Srgrimes#ifndef NO_STRICT_DFL_ENV
13337Srgrimes	fenv_t env;
13437Srgrimes
13537Srgrimes	fegetenv(&env);
13637Srgrimes	assert(memcmp(&env, FE_DFL_ENV, sizeof(env)) == 0);
137289Srgrimes#endif
13837Srgrimes	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
13937Srgrimes}
14037Srgrimes
14137Srgrimes/*
14237Srgrimes * Test fetestexcept() and feclearexcept().
14337Srgrimes */
14437Srgrimesstatic void
14537Srgrimestest_fetestclearexcept(void)
14637Srgrimes{
14737Srgrimes	int excepts, i;
14837Srgrimes
14937Srgrimes	for (i = 0; i < 1 << NEXCEPTS; i++)
15037Srgrimes		assert(fetestexcept(std_except_sets[i]) == 0);
15137Srgrimes	for (i = 0; i < 1 << NEXCEPTS; i++) {
15237Srgrimes		excepts = std_except_sets[i];
15337Srgrimes
15437Srgrimes		/* FE_ALL_EXCEPT might be special-cased, as on i386. */
15537Srgrimes		raiseexcept(excepts);
15637Srgrimes		assert(fetestexcept(excepts) == excepts);
15737Srgrimes		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
15837Srgrimes		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
15937Srgrimes
16037Srgrimes		raiseexcept(excepts);
16137Srgrimes		assert(fetestexcept(excepts) == excepts);
16237Srgrimes		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
16337Srgrimes			excepts |= FE_INEXACT;
16437Srgrimes			assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
165198229Sed			    excepts);
16637Srgrimes		} else {
16737Srgrimes			assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
168209954Sbcr		}
16937Srgrimes		assert(feclearexcept(excepts) == 0);
17037Srgrimes		assert(fetestexcept(ALL_STD_EXCEPT) == 0);
17137Srgrimes	}
17237Srgrimes}
17337Srgrimes
17437Srgrimes/*
17537Srgrimes * Test fegetexceptflag() and fesetexceptflag().
17637Srgrimes *
17737Srgrimes * Prerequisites: fetestexcept(), feclearexcept()
17837Srgrimes */
17937Srgrimesstatic void
18037Srgrimestest_fegsetexceptflag(void)
18137Srgrimes{
18237Srgrimes	fexcept_t flag;
18337Srgrimes	int excepts, i;
18437Srgrimes
18537Srgrimes	assert(fetestexcept(FE_ALL_EXCEPT) == 0);
18637Srgrimes	for (i = 0; i < 1 << NEXCEPTS; i++) {
18737Srgrimes		excepts = std_except_sets[i];
18837Srgrimes
18937Srgrimes		assert(fegetexceptflag(&flag, excepts) == 0);
19037Srgrimes		raiseexcept(ALL_STD_EXCEPT);
19137Srgrimes		assert(fesetexceptflag(&flag, excepts) == 0);
19237Srgrimes		assert(fetestexcept(ALL_STD_EXCEPT) ==
19337Srgrimes		    (ALL_STD_EXCEPT ^ excepts));
19437Srgrimes
19537Srgrimes		assert(fegetexceptflag(&flag, FE_ALL_EXCEPT) == 0);
19637Srgrimes		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
19737Srgrimes		assert(fesetexceptflag(&flag, excepts) == 0);
19837Srgrimes		assert(fetestexcept(ALL_STD_EXCEPT) == 0);
19937Srgrimes		assert(fesetexceptflag(&flag, ALL_STD_EXCEPT ^ excepts) == 0);
20061513Sphk		assert(fetestexcept(ALL_STD_EXCEPT) ==
20161513Sphk		    (ALL_STD_EXCEPT ^ excepts));
20261513Sphk
20361513Sphk		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
20461513Sphk	}
20561513Sphk}
20661513Sphk
207157658Sdes/*
208157658Sdes * Test feraiseexcept().
209157658Sdes *
210157658Sdes * Prerequisites: fetestexcept(), feclearexcept()
211157658Sdes */
212157658Sdesstatic void
213157658Sdestest_feraiseexcept(void)
214157658Sdes{
215157658Sdes	int excepts, i;
216157658Sdes
21782700Smurray	for (i = 0; i < 1 << NEXCEPTS; i++) {
218130151Sschweikh		excepts = std_except_sets[i];
21982700Smurray
22082700Smurray		assert(fetestexcept(FE_ALL_EXCEPT) == 0);
22182700Smurray		assert(feraiseexcept(excepts) == 0);
22282700Smurray		if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0) {
22382700Smurray			excepts |= FE_INEXACT;
224154685Smatteo			assert((fetestexcept(ALL_STD_EXCEPT) | FE_INEXACT) ==
225154685Smatteo			    excepts);
22682700Smurray		} else {
22782700Smurray			assert(fetestexcept(ALL_STD_EXCEPT) == excepts);
22882700Smurray		}
22982700Smurray		assert(feclearexcept(FE_ALL_EXCEPT) == 0);
23082700Smurray	}
23182700Smurray	assert(feraiseexcept(FE_INVALID | FE_DIVBYZERO) == 0);
23282700Smurray	assert(fetestexcept(ALL_STD_EXCEPT) == (FE_INVALID | FE_DIVBYZERO));
23382700Smurray	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