1 /* $OpenBSD: fenv_test.c,v 1.4 2020/10/19 09:35:44 kettenis 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 #if !defined(__arm__) && !defined(__aarch64__) 398 struct sigaction act; 399 int except, pass, raise, status; 400 unsigned i; 401 402 assert((fegetexcept() & ALL_STD_EXCEPT) == 0); 403 assert((feenableexcept(FE_INVALID|FE_OVERFLOW) & ALL_STD_EXCEPT) == 0); 404 assert((feenableexcept(FE_UNDERFLOW) & ALL_STD_EXCEPT) == 405 (FE_INVALID | FE_OVERFLOW)); 406 assert((fedisableexcept(FE_OVERFLOW) & ALL_STD_EXCEPT) == 407 (FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW)); 408 assert((fegetexcept() & ALL_STD_EXCEPT) == (FE_INVALID | FE_UNDERFLOW)); 409 assert((fedisableexcept(FE_ALL_EXCEPT) & ALL_STD_EXCEPT) == 410 (FE_INVALID | FE_UNDERFLOW)); 411 assert((fegetexcept() & ALL_STD_EXCEPT) == 0); 412 413 sigemptyset(&act.sa_mask); 414 act.sa_flags = 0; 415 act.sa_handler = trap_handler; 416 for (pass = 0; pass < 2; pass++) { 417 for (i = 0; i < NEXCEPTS; i++) { 418 except = std_excepts[i]; 419 /* over/underflow may also raise inexact */ 420 if (except == FE_INEXACT) 421 raise = FE_DIVBYZERO | FE_INVALID; 422 else 423 raise = ALL_STD_EXCEPT ^ except; 424 425 /* 426 * We need to fork a child process because 427 * there isn't a portable way to recover from 428 * a floating-point exception. 429 */ 430 switch(fork()) { 431 case 0: /* child */ 432 assert((fegetexcept() & ALL_STD_EXCEPT) == 0); 433 assert((feenableexcept(except) 434 & ALL_STD_EXCEPT) == 0); 435 assert(fegetexcept() == except); 436 raiseexcept(raise); 437 assert(feraiseexcept(raise) == 0); 438 assert(fetestexcept(ALL_STD_EXCEPT) == raise); 439 440 assert(sigaction(SIGFPE, &act, NULL) == 0); 441 switch (pass) { 442 case 0: 443 raiseexcept(except); 444 case 1: 445 feraiseexcept(except); 446 default: 447 assert(0); 448 } 449 assert(0); 450 default: /* parent */ 451 assert(wait(&status) > 0); 452 /* 453 * Avoid assert() here so that it's possible 454 * to examine a failed child's core dump. 455 */ 456 if (!WIFEXITED(status)) 457 errx(1, "child aborted\n"); 458 assert(WEXITSTATUS(status) == 0); 459 break; 460 case -1: /* error */ 461 assert(0); 462 } 463 } 464 } 465 assert(fetestexcept(FE_ALL_EXCEPT) == 0); 466 #endif 467 } 468 469 /* 470 * Test feholdexcept() and feupdateenv(). 471 * 472 * Prerequisites: fetestexcept(), fegetround(), fesetround(), 473 * fedisableexcept(), feenableexcept() 474 */ 475 static void 476 test_feholdupdate(void) 477 { 478 fenv_t env; 479 480 struct sigaction act; 481 int except, pass, status, raise; 482 unsigned i; 483 484 sigemptyset(&act.sa_mask); 485 act.sa_flags = 0; 486 act.sa_handler = trap_handler; 487 for (pass = 0; pass < 2; pass++) { 488 for (i = 0; i < NEXCEPTS; i++) { 489 except = std_excepts[i]; 490 /* over/underflow may also raise inexact */ 491 if (except == FE_INEXACT) 492 raise = FE_DIVBYZERO | FE_INVALID; 493 else 494 raise = ALL_STD_EXCEPT ^ except; 495 496 /* 497 * We need to fork a child process because 498 * there isn't a portable way to recover from 499 * a floating-point exception. 500 */ 501 switch(fork()) { 502 case 0: /* child */ 503 /* 504 * We don't want to cause a fatal exception in 505 * the child until the second pass, so we can 506 * check other properties of feupdateenv(). 507 */ 508 if (pass == 1) 509 assert((feenableexcept(except) & 510 ALL_STD_EXCEPT) == 0); 511 raiseexcept(raise); 512 assert(fesetround(FE_DOWNWARD) == 0); 513 assert(feholdexcept(&env) == 0); 514 assert(fetestexcept(FE_ALL_EXCEPT) == 0); 515 raiseexcept(except); 516 assert(fesetround(FE_UPWARD) == 0); 517 518 if (pass == 1) 519 assert(sigaction(SIGFPE, &act, NULL) == 520 0); 521 assert(feupdateenv(&env) == 0); 522 assert(fegetround() == FE_DOWNWARD); 523 assert(fetestexcept(ALL_STD_EXCEPT) == 524 (except | raise)); 525 526 assert(pass == 0); 527 _exit(0); 528 default: /* parent */ 529 assert(wait(&status) > 0); 530 /* 531 * Avoid assert() here so that it's possible 532 * to examine a failed child's core dump. 533 */ 534 if (!WIFEXITED(status)) 535 errx(1, "child aborted\n"); 536 assert(WEXITSTATUS(status) == 0); 537 break; 538 case -1: /* error */ 539 assert(0); 540 } 541 } 542 #if defined(__arm__) || defined(__aarch64__) 543 break; 544 #endif 545 } 546 assert(fetestexcept(FE_ALL_EXCEPT) == 0); 547 } 548 549 int 550 main(void) 551 { 552 553 printf("1..8\n"); 554 init_exceptsets(); 555 test_dfl_env(); 556 printf("ok 1 - fenv\n"); 557 test_fetestclearexcept(); 558 printf("ok 2 - fenv\n"); 559 test_fegsetexceptflag(); 560 printf("ok 3 - fenv\n"); 561 test_feraiseexcept(); 562 printf("ok 4 - fenv\n"); 563 test_fegsetround(); 564 printf("ok 5 - fenv\n"); 565 test_fegsetenv(); 566 printf("ok 6 - fenv\n"); 567 test_masking(); 568 printf("ok 7 - fenv\n"); 569 test_feholdupdate(); 570 printf("ok 8 - fenv\n"); 571 572 return (0); 573 } 574