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