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