fenv_test.c revision 130330
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 130330 2004-06-11 03:22:34Z 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(fesetround(FE_DOWNWARD) == 0); 240 assert(fegetround() == FE_DOWNWARD); 241 assert(getround() == FE_DOWNWARD); 242 assert(fesetround(FE_UPWARD) == 0); 243 assert(getround() == FE_UPWARD); 244 assert(fegetround() == FE_UPWARD); 245 assert(fesetround(FE_TOWARDZERO) == 0); 246 assert(getround() == FE_TOWARDZERO); 247 assert(fegetround() == FE_TOWARDZERO); 248 assert(fesetround(FE_TONEAREST) == 0); 249 assert(getround() == FE_TONEAREST); 250 assert(feclearexcept(FE_ALL_EXCEPT) == 0); 251} 252 253/* 254 * Test fegetenv() and fesetenv(). 255 * 256 * Prerequisites: fetestexcept(), feclearexcept(), fegetround(), fesetround() 257 */ 258static void 259test_fegsetenv(void) 260{ 261 fenv_t env1, env2; 262 int excepts, i; 263 264 for (i = 0; i < 1 << NEXCEPTS; i++) { 265 excepts = std_except_sets[i]; 266 267 assert(fetestexcept(FE_ALL_EXCEPT) == 0); 268 assert(fegetround() == FE_TONEAREST); 269 assert(fegetenv(&env1) == 0); 270 271 /* 272 * fe[gs]etenv() should be able to save and restore 273 * exception flags without the spurious inexact 274 * exceptions that afflict raiseexcept(). 275 */ 276 raiseexcept(excepts); 277 if ((excepts & (FE_UNDERFLOW | FE_OVERFLOW)) != 0 && 278 (excepts & FE_INEXACT) == 0) 279 assert(feclearexcept(FE_INEXACT) == 0); 280 281 fesetround(FE_DOWNWARD); 282 assert(fegetenv(&env2) == 0); 283 assert(fesetenv(&env1) == 0); 284 assert(fetestexcept(FE_ALL_EXCEPT) == 0); 285 assert(fegetround() == FE_TONEAREST); 286 287 assert(fesetenv(&env2) == 0); 288 assert(fetestexcept(FE_ALL_EXCEPT) == excepts); 289 assert(fegetround() == FE_DOWNWARD); 290 assert(fesetenv(&env1) == 0); 291 assert(fetestexcept(FE_ALL_EXCEPT) == 0); 292 assert(fegetround() == FE_TONEAREST); 293 } 294} 295 296/* 297 * Test fegetmask() and fesetmask(). 298 * 299 * Prerequisites: fetestexcept(), feraiseexcept() 300 */ 301static void 302test_fegsetmask(void) 303{ 304 struct sigaction act; 305 int except, i, pass, raise, status; 306 307 sigemptyset(&act.sa_mask); 308 act.sa_flags = 0; 309 act.sa_handler = trap_handler; 310 for (pass = 0; pass < 2; pass++) { 311 for (i = 0; i < NEXCEPTS; i++) { 312 except = std_excepts[i]; 313 /* over/underflow may also raise inexact */ 314 if (except == FE_INEXACT) 315 raise = FE_DIVBYZERO | FE_INVALID; 316 else 317 raise = ALL_STD_EXCEPT ^ except; 318 319 /* 320 * We need to fork a child process because 321 * there isn't a portable way to recover from 322 * a floating-point exception. 323 */ 324 switch(fork()) { 325 case 0: /* child */ 326 assert((fegetmask() & ALL_STD_EXCEPT) == 0); 327 assert((fesetmask(except) & ALL_STD_EXCEPT) == 328 0); 329 assert(fegetmask() == except); 330 raiseexcept(raise); 331 assert(feraiseexcept(raise) == 0); 332 assert(fetestexcept(ALL_STD_EXCEPT) == raise); 333 334 assert(sigaction(SIGFPE, &act, NULL) == 0); 335 switch (pass) { 336 case 0: 337 raiseexcept(except); 338 case 1: 339 feraiseexcept(except); 340 default: 341 assert(0); 342 } 343 assert(0); 344 default: /* parent */ 345 assert(wait(&status) > 0); 346 /* 347 * Avoid assert() here so that it's possible 348 * to examine a failed child's core dump. 349 */ 350 if (!WIFEXITED(status)) 351 errx(1, "child aborted\n"); 352 assert(WEXITSTATUS(status) == 0); 353 break; 354 case -1: /* error */ 355 assert(0); 356 } 357 } 358 } 359 assert(fetestexcept(FE_ALL_EXCEPT) == 0); 360} 361 362/* 363 * Test feholdexcept() and feupdateenv(). 364 * 365 * Prerequisites: fetestexcept(), fegetround(), fesetround(), fesetmask() 366 */ 367static void 368test_feholdupdate(void) 369{ 370 fenv_t env; 371 372 struct sigaction act; 373 int except, i, pass, status, raise; 374 375 sigemptyset(&act.sa_mask); 376 act.sa_flags = 0; 377 act.sa_handler = trap_handler; 378 for (pass = 0; pass < 2; pass++) { 379 for (i = 0; i < NEXCEPTS; i++) { 380 except = std_excepts[i]; 381 /* over/underflow may also raise inexact */ 382 if (except == FE_INEXACT) 383 raise = FE_DIVBYZERO | FE_INVALID; 384 else 385 raise = ALL_STD_EXCEPT ^ except; 386 387 /* 388 * We need to fork a child process because 389 * there isn't a portable way to recover from 390 * a floating-point exception. 391 */ 392 switch(fork()) { 393 case 0: /* child */ 394 /* 395 * We don't want to cause a fatal exception in 396 * the child until the second pass, so we can 397 * check other properties of feupdateenv(). 398 */ 399 if (pass == 1) 400 assert((fesetmask(except) & 401 ALL_STD_EXCEPT) == 0); 402 raiseexcept(raise); 403 assert(fesetround(FE_DOWNWARD) == 0); 404 assert(feholdexcept(&env) == 0); 405 assert(fetestexcept(FE_ALL_EXCEPT) == 0); 406 raiseexcept(except); 407 assert(fesetround(FE_UPWARD) == 0); 408 409 if (pass == 1) 410 assert(sigaction(SIGFPE, &act, NULL) == 411 0); 412 assert(feupdateenv(&env) == 0); 413 assert(fegetround() == FE_DOWNWARD); 414 assert(fetestexcept(ALL_STD_EXCEPT) == 415 (except | raise)); 416 417 assert(pass == 0); 418 _exit(0); 419 default: /* parent */ 420 assert(wait(&status) > 0); 421 /* 422 * Avoid assert() here so that it's possible 423 * to examine a failed child's core dump. 424 */ 425 if (!WIFEXITED(status)) 426 errx(1, "child aborted\n"); 427 assert(WEXITSTATUS(status) == 0); 428 break; 429 case -1: /* error */ 430 assert(0); 431 } 432 } 433 } 434 assert(fetestexcept(FE_ALL_EXCEPT) == 0); 435} 436 437/* 438 * Raise a floating-point exception without relying on the standard 439 * library routines, which we are trying to test. 440 * 441 * XXX We can't raise an {over,under}flow without also raising an 442 * inexact exception. 443 */ 444static void 445raiseexcept(int excepts) 446{ 447 volatile double d; 448 449 /* 450 * With a compiler that supports the FENV_ACCESS pragma 451 * properly, simple expressions like '0.0 / 0.0' should 452 * be sufficient to generate traps. Unfortunately, we 453 * need to bring a volatile variable into the equation 454 * to prevent incorrect optimizations. 455 */ 456 if (excepts & FE_INVALID) { 457 d = 0.0; 458 d = 0.0 / d; 459 } 460 if (excepts & FE_DIVBYZERO) { 461 d = 0.0; 462 d = 1.0 / d; 463 } 464 if (excepts & FE_OVERFLOW) { 465 d = DBL_MAX; 466 d *= 2.0; 467 } 468 if (excepts & FE_UNDERFLOW) { 469 d = DBL_MIN; 470 d /= DBL_MAX; 471 } 472 if (excepts & FE_INEXACT) { 473 d = DBL_MIN; 474 d += 1.0; 475 } 476 477 /* 478 * On the x86 (and some other architectures?) the FPU and 479 * integer units are decoupled. We need to execute an FWAIT 480 * or a floating-point instruction to get synchronous exceptions. 481 */ 482 d = 1.0; 483 d += 1.0; 484} 485 486/* 487 * Determine the current rounding mode without relying on the fenv 488 * routines. This function may raise an inexact exception. 489 */ 490static int 491getround(void) 492{ 493 volatile double d; 494 495 /* 496 * This test works just as well with 0.0 - 0.0, except on ia64 497 * where 0.0 - 0.0 gives the wrong sign when rounding downwards. 498 */ 499 d = 1.0; 500 d -= 1.0; 501 if (copysign(1.0, d) < 0.0) 502 return (FE_DOWNWARD); 503 504 d = 1.0; 505 if (d + (DBL_EPSILON * 3.0 / 4.0) == 1.0) 506 return (FE_TOWARDZERO); 507 if (d + (DBL_EPSILON * 1.0 / 4.0) > 1.0) 508 return (FE_UPWARD); 509 510 return (FE_TONEAREST); 511} 512 513static void 514trap_handler(int sig) 515{ 516 517 assert(sig == SIGFPE); 518 _exit(0); 519} 520