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