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