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