diff options
author | Waldemar Brodkorb <wbx@uclibc-ng.org> | 2016-11-03 13:03:55 +0100 |
---|---|---|
committer | Waldemar Brodkorb <wbx@uclibc-ng.org> | 2016-11-03 13:03:55 +0100 |
commit | 3384c45e66ddf18f235654b67ae34ac7dcb07534 (patch) | |
tree | edbd0f03b72acb5961f7919e13da34a34a84fc23 /test/math/test-fenv.c | |
parent | 29d1023614fcfef8520edec8b64e9f8953d9a121 (diff) |
math: sync with GNU libc
The format of the ULPS files have changed, non-glibc architecture
files needs to be updated later.
Add all math tests from latest GNU libc and allow to compile
and run them on uClibc-ng and GNU libc systems.
Diffstat (limited to 'test/math/test-fenv.c')
-rw-r--r-- | test/math/test-fenv.c | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/test/math/test-fenv.c b/test/math/test-fenv.c new file mode 100644 index 0000000..d4ef587 --- /dev/null +++ b/test/math/test-fenv.c @@ -0,0 +1,788 @@ +/* Copyright (C) 1997-2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger <aj@suse.de> and + Ulrich Drepper <drepper@cygnus.com>, 1997. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* Tests for ISO C99 7.6: Floating-point environment */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include <complex.h> +#include <math.h> +#include <float.h> +#ifdef __UCLIBC_HAS_FENV__ +#include <fenv.h> +#endif + +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/resource.h> +#include "math-tests.h" + +#ifdef __UCLIBC_HAS_FENV__ + +/* + Since not all architectures might define all exceptions, we define + a private set and map accordingly. +*/ +#define NO_EXC 0 +#define INEXACT_EXC 0x1 +#define DIVBYZERO_EXC 0x2 +#define UNDERFLOW_EXC 0x04 +#define OVERFLOW_EXC 0x08 +#define INVALID_EXC 0x10 +#define ALL_EXC \ + (INEXACT_EXC | DIVBYZERO_EXC | UNDERFLOW_EXC | OVERFLOW_EXC | \ + INVALID_EXC) + +static int count_errors; + +#if FE_ALL_EXCEPT +/* Test whether a given exception was raised. */ +static void +test_single_exception (short int exception, + short int exc_flag, + fexcept_t fe_flag, + const char *flag_name) +{ + if (exception & exc_flag) + { + if (fetestexcept (fe_flag)) + printf (" Pass: Exception \"%s\" is set\n", flag_name); + else + { + printf (" Fail: Exception \"%s\" is not set\n", flag_name); + ++count_errors; + } + } + else + { + if (fetestexcept (fe_flag)) + { + printf (" Fail: Exception \"%s\" is set\n", flag_name); + ++count_errors; + } + else + { + printf (" Pass: Exception \"%s\" is not set\n", flag_name); + } + } +} +#endif + +static void +test_exceptions (const char *test_name, short int exception, + int ignore_inexact) +{ + printf ("Test: %s\n", test_name); +#ifdef FE_DIVBYZERO + test_single_exception (exception, DIVBYZERO_EXC, FE_DIVBYZERO, + "DIVBYZERO"); +#endif +#ifdef FE_INVALID + test_single_exception (exception, INVALID_EXC, FE_INVALID, + "INVALID"); +#endif +#ifdef FE_INEXACT + if (!ignore_inexact) + test_single_exception (exception, INEXACT_EXC, FE_INEXACT, + "INEXACT"); +#endif +#ifdef FE_UNDERFLOW + test_single_exception (exception, UNDERFLOW_EXC, FE_UNDERFLOW, + "UNDERFLOW"); +#endif +#ifdef FE_OVERFLOW + test_single_exception (exception, OVERFLOW_EXC, FE_OVERFLOW, + "OVERFLOW"); +#endif +} + +static void +print_rounding (int rounding) +{ + + switch (rounding) + { +#ifdef FE_TONEAREST + case FE_TONEAREST: + printf ("TONEAREST"); + break; +#endif +#ifdef FE_UPWARD + case FE_UPWARD: + printf ("UPWARD"); + break; +#endif +#ifdef FE_DOWNWARD + case FE_DOWNWARD: + printf ("DOWNWARD"); + break; +#endif +#ifdef FE_TOWARDZERO + case FE_TOWARDZERO: + printf ("TOWARDZERO"); + break; +#endif + } + printf (".\n"); +} + + +static void +test_rounding (const char *test_name, int rounding_mode) +{ + int curr_rounding = fegetround (); + + printf ("Test: %s\n", test_name); + if (curr_rounding == rounding_mode) + { + printf (" Pass: Rounding mode is "); + print_rounding (curr_rounding); + } + else + { + ++count_errors; + printf (" Fail: Rounding mode is "); + print_rounding (curr_rounding); + } +} + + +#if FE_ALL_EXCEPT +static void +set_single_exc (const char *test_name, int fe_exc, fexcept_t exception) +{ + char str[200]; + /* The standard allows the inexact exception to be set together with the + underflow and overflow exceptions. So ignore the inexact flag if the + others are raised. */ + int ignore_inexact = (fe_exc & (UNDERFLOW_EXC | OVERFLOW_EXC)) != 0; + + strcpy (str, test_name); + strcat (str, ": set flag, with rest not set"); + feclearexcept (FE_ALL_EXCEPT); + feraiseexcept (exception); + test_exceptions (str, fe_exc, ignore_inexact); + + strcpy (str, test_name); + strcat (str, ": clear flag, rest also unset"); + feclearexcept (exception); + test_exceptions (str, NO_EXC, ignore_inexact); + + strcpy (str, test_name); + strcat (str, ": set flag, with rest set"); + feraiseexcept (FE_ALL_EXCEPT ^ exception); + feraiseexcept (exception); + test_exceptions (str, ALL_EXC, 0); + + strcpy (str, test_name); + strcat (str, ": clear flag, leave rest set"); + feclearexcept (exception); + test_exceptions (str, ALL_EXC ^ fe_exc, 0); +} +#endif + +static void +fe_tests (void) +{ + /* clear all exceptions and test if all are cleared */ + feclearexcept (FE_ALL_EXCEPT); + test_exceptions ("feclearexcept (FE_ALL_EXCEPT) clears all exceptions", + NO_EXC, 0); + + /* raise all exceptions and test if all are raised */ + feraiseexcept (FE_ALL_EXCEPT); + test_exceptions ("feraiseexcept (FE_ALL_EXCEPT) raises all exceptions", + ALL_EXC, 0); + feclearexcept (FE_ALL_EXCEPT); + +#ifdef FE_DIVBYZERO + set_single_exc ("Set/Clear FE_DIVBYZERO", DIVBYZERO_EXC, FE_DIVBYZERO); +#endif +#ifdef FE_INVALID + set_single_exc ("Set/Clear FE_INVALID", INVALID_EXC, FE_INVALID); +#endif +#ifdef FE_INEXACT + set_single_exc ("Set/Clear FE_INEXACT", INEXACT_EXC, FE_INEXACT); +#endif +#ifdef FE_UNDERFLOW + set_single_exc ("Set/Clear FE_UNDERFLOW", UNDERFLOW_EXC, FE_UNDERFLOW); +#endif +#ifdef FE_OVERFLOW + set_single_exc ("Set/Clear FE_OVERFLOW", OVERFLOW_EXC, FE_OVERFLOW); +#endif +} + +#if FE_ALL_EXCEPT +/* Test that program aborts with no masked interrupts */ +static void +feenv_nomask_test (const char *flag_name, int fe_exc) +{ +# if defined FE_NOMASK_ENV + int status; + pid_t pid; + + if (!EXCEPTION_ENABLE_SUPPORTED (FE_ALL_EXCEPT) + && fesetenv (FE_NOMASK_ENV) != 0) + { + printf ("Test: not testing FE_NOMASK_ENV, it isn't implemented.\n"); + return; + } + + printf ("Test: after fesetenv (FE_NOMASK_ENV) processes will abort\n"); + printf (" when feraiseexcept (%s) is called.\n", flag_name); + pid = fork (); + if (pid == 0) + { +# ifdef RLIMIT_CORE + /* Try to avoid dumping core. */ + struct rlimit core_limit; + core_limit.rlim_cur = 0; + core_limit.rlim_max = 0; + setrlimit (RLIMIT_CORE, &core_limit); +# endif + + fesetenv (FE_NOMASK_ENV); + feraiseexcept (fe_exc); + exit (2); + } + else if (pid < 0) + { + if (errno != ENOSYS) + { + printf (" Fail: Could not fork.\n"); + ++count_errors; + } + else + printf (" `fork' not implemented, test ignored.\n"); + } + else { + if (waitpid (pid, &status, 0) != pid) + { + printf (" Fail: waitpid call failed.\n"); + ++count_errors; + } + else if (WIFSIGNALED (status) && WTERMSIG (status) == SIGFPE) + printf (" Pass: Process received SIGFPE.\n"); + else + { + printf (" Fail: Process didn't receive signal and exited with status %d.\n", + status); + ++count_errors; + } + } +# endif +} + +/* Test that program doesn't abort with default environment */ +static void +feenv_mask_test (const char *flag_name, int fe_exc) +{ + int status; + pid_t pid; + + printf ("Test: after fesetenv (FE_DFL_ENV) processes will not abort\n"); + printf (" when feraiseexcept (%s) is called.\n", flag_name); + pid = fork (); + if (pid == 0) + { +#ifdef RLIMIT_CORE + /* Try to avoid dumping core. */ + struct rlimit core_limit; + core_limit.rlim_cur = 0; + core_limit.rlim_max = 0; + setrlimit (RLIMIT_CORE, &core_limit); +#endif + + fesetenv (FE_DFL_ENV); + feraiseexcept (fe_exc); + exit (2); + } + else if (pid < 0) + { + if (errno != ENOSYS) + { + printf (" Fail: Could not fork.\n"); + ++count_errors; + } + else + printf (" `fork' not implemented, test ignored.\n"); + } + else { + if (waitpid (pid, &status, 0) != pid) + { + printf (" Fail: waitpid call failed.\n"); + ++count_errors; + } + else if (WIFEXITED (status) && WEXITSTATUS (status) == 2) + printf (" Pass: Process exited normally.\n"); + else + { + printf (" Fail: Process exited abnormally with status %d.\n", + status); + ++count_errors; + } + } +} + +/* Test that program aborts with no masked interrupts */ +static void +feexcp_nomask_test (const char *flag_name, int fe_exc) +{ + int status; + pid_t pid; + + if (!EXCEPTION_ENABLE_SUPPORTED (fe_exc) && feenableexcept (fe_exc) == -1) + { + printf ("Test: not testing feenableexcept, it isn't implemented.\n"); + return; + } + + printf ("Test: after feenableexcept (%s) processes will abort\n", + flag_name); + printf (" when feraiseexcept (%s) is called.\n", flag_name); + pid = fork (); + if (pid == 0) + { +#ifdef RLIMIT_CORE + /* Try to avoid dumping core. */ + struct rlimit core_limit; + core_limit.rlim_cur = 0; + core_limit.rlim_max = 0; + setrlimit (RLIMIT_CORE, &core_limit); +#endif + + fedisableexcept (FE_ALL_EXCEPT); + feenableexcept (fe_exc); + feraiseexcept (fe_exc); + exit (2); + } + else if (pid < 0) + { + if (errno != ENOSYS) + { + printf (" Fail: Could not fork.\n"); + ++count_errors; + } + else + printf (" `fork' not implemented, test ignored.\n"); + } + else { + if (waitpid (pid, &status, 0) != pid) + { + printf (" Fail: waitpid call failed.\n"); + ++count_errors; + } + else if (WIFSIGNALED (status) && WTERMSIG (status) == SIGFPE) + printf (" Pass: Process received SIGFPE.\n"); + else + { + printf (" Fail: Process didn't receive signal and exited with status %d.\n", + status); + ++count_errors; + } + } +} + +/* Test that program doesn't abort with exception. */ +static void +feexcp_mask_test (const char *flag_name, int fe_exc) +{ + int status; + int exception; + pid_t pid; + + printf ("Test: after fedisableexcept (%s) processes will not abort\n", + flag_name); + printf (" when feraiseexcept (%s) is called.\n", flag_name); + pid = fork (); + if (pid == 0) + { +#ifdef RLIMIT_CORE + /* Try to avoid dumping core. */ + struct rlimit core_limit; + core_limit.rlim_cur = 0; + core_limit.rlim_max = 0; + setrlimit (RLIMIT_CORE, &core_limit); +#endif + feenableexcept (FE_ALL_EXCEPT); + exception = fe_exc; +#ifdef FE_INEXACT + /* The standard allows the inexact exception to be set together with the + underflow and overflow exceptions. So add FE_INEXACT to the set of + exceptions to be disabled if we will be raising underflow or + overflow. */ +# ifdef FE_OVERFLOW + if (fe_exc & FE_OVERFLOW) + exception |= FE_INEXACT; +# endif +# ifdef FE_UNDERFLOW + if (fe_exc & FE_UNDERFLOW) + exception |= FE_INEXACT; +# endif +#endif + fedisableexcept (exception); + feraiseexcept (fe_exc); + exit (2); + } + else if (pid < 0) + { + if (errno != ENOSYS) + { + printf (" Fail: Could not fork.\n"); + ++count_errors; + } + else + printf (" `fork' not implemented, test ignored.\n"); + } + else { + if (waitpid (pid, &status, 0) != pid) + { + printf (" Fail: waitpid call failed.\n"); + ++count_errors; + } + else if (WIFEXITED (status) && WEXITSTATUS (status) == 2) + printf (" Pass: Process exited normally.\n"); + else + { + printf (" Fail: Process exited abnormally with status %d.\n", + status); + ++count_errors; + } + } +} + + +/* Tests for feenableexcept/fedisableexcept/fegetexcept. */ +static void +feenable_test (const char *flag_name, int fe_exc) +{ + int excepts; + + printf ("Tests for feenableexcepts etc. with flag %s\n", flag_name); + + /* First disable all exceptions. */ + if (fedisableexcept (FE_ALL_EXCEPT) == -1) + { + printf ("Test: fedisableexcept (FE_ALL_EXCEPT) failed\n"); + ++count_errors; + /* If this fails, the other tests don't make sense. */ + return; + } + excepts = fegetexcept (); + if (excepts != 0) + { + printf ("Test: fegetexcept (%s) failed, return should be 0, is %d\n", + flag_name, excepts); + ++count_errors; + } + excepts = feenableexcept (fe_exc); + if (!EXCEPTION_ENABLE_SUPPORTED (fe_exc) && excepts == -1) + { + printf ("Test: not testing feenableexcept, it isn't implemented.\n"); + return; + } + if (excepts == -1) + { + printf ("Test: feenableexcept (%s) failed\n", flag_name); + ++count_errors; + return; + } + if (excepts != 0) + { + printf ("Test: feenableexcept (%s) failed, return should be 0, is %x\n", + flag_name, excepts); + ++count_errors; + } + + excepts = fegetexcept (); + if (excepts != fe_exc) + { + printf ("Test: fegetexcept (%s) failed, return should be 0x%x, is 0x%x\n", + flag_name, fe_exc, excepts); + ++count_errors; + } + + /* And now disable the exception again. */ + excepts = fedisableexcept (fe_exc); + if (excepts == -1) + { + printf ("Test: fedisableexcept (%s) failed\n", flag_name); + ++count_errors; + return; + } + if (excepts != fe_exc) + { + printf ("Test: fedisableexcept (%s) failed, return should be 0x%x, is 0x%x\n", + flag_name, fe_exc, excepts); + ++count_errors; + } + + excepts = fegetexcept (); + if (excepts != 0) + { + printf ("Test: fegetexcept (%s) failed, return should be 0, is 0x%x\n", + flag_name, excepts); + ++count_errors; + } + + /* Now the other way round: Enable all exceptions and disable just this one. */ + if (feenableexcept (FE_ALL_EXCEPT) == -1) + { + printf ("Test: feenableexcept (FE_ALL_EXCEPT) failed\n"); + ++count_errors; + /* If this fails, the other tests don't make sense. */ + return; + } + + excepts = fegetexcept (); + if (excepts != FE_ALL_EXCEPT) + { + printf ("Test: fegetexcept (%s) failed, return should be 0x%x, is 0x%x\n", + flag_name, FE_ALL_EXCEPT, excepts); + ++count_errors; + } + + excepts = fedisableexcept (fe_exc); + if (excepts == -1) + { + printf ("Test: fedisableexcept (%s) failed\n", flag_name); + ++count_errors; + return; + } + if (excepts != FE_ALL_EXCEPT) + { + printf ("Test: fedisableexcept (%s) failed, return should be 0, is 0x%x\n", + flag_name, excepts); + ++count_errors; + } + + excepts = fegetexcept (); + if (excepts != (FE_ALL_EXCEPT & ~fe_exc)) + { + printf ("Test: fegetexcept (%s) failed, return should be 0x%x, is 0x%x\n", + flag_name, (FE_ALL_EXCEPT & ~fe_exc), excepts); + ++count_errors; + } + + /* And now enable the exception again. */ + excepts = feenableexcept (fe_exc); + if (excepts == -1) + { + printf ("Test: feenableexcept (%s) failed\n", flag_name); + ++count_errors; + return; + } + if (excepts != (FE_ALL_EXCEPT & ~fe_exc)) + { + printf ("Test: feenableexcept (%s) failed, return should be 0, is 0x%x\n", + flag_name, excepts); + ++count_errors; + } + + excepts = fegetexcept (); + if (excepts != FE_ALL_EXCEPT) + { + printf ("Test: fegetexcept (%s) failed, return should be 0x%x, is 0x%x\n", + flag_name, FE_ALL_EXCEPT, excepts); + ++count_errors; + } + feexcp_nomask_test (flag_name, fe_exc); + feexcp_mask_test (flag_name, fe_exc); + +} + + +static void +fe_single_test (const char *flag_name, int fe_exc) +{ + feenv_nomask_test (flag_name, fe_exc); + feenv_mask_test (flag_name, fe_exc); + feenable_test (flag_name, fe_exc); +} +#endif + + +static void +feenv_tests (void) +{ + /* We might have some exceptions still set. */ + feclearexcept (FE_ALL_EXCEPT); + +#ifdef FE_DIVBYZERO + fe_single_test ("FE_DIVBYZERO", FE_DIVBYZERO); +#endif +#ifdef FE_INVALID + fe_single_test ("FE_INVALID", FE_INVALID); +#endif +#ifdef FE_INEXACT + fe_single_test ("FE_INEXACT", FE_INEXACT); +#endif +#ifdef FE_UNDERFLOW + fe_single_test ("FE_UNDERFLOW", FE_UNDERFLOW); +#endif +#ifdef FE_OVERFLOW + fe_single_test ("FE_OVERFLOW", FE_OVERFLOW); +#endif + fesetenv (FE_DFL_ENV); +} + + +static void +feholdexcept_tests (void) +{ + fenv_t saved, saved2; + int res; + + feclearexcept (FE_ALL_EXCEPT); + fedisableexcept (FE_ALL_EXCEPT); +#ifdef FE_DIVBYZERO + feraiseexcept (FE_DIVBYZERO); +#endif + test_exceptions ("feholdexcept_tests FE_DIVBYZERO test", + DIVBYZERO_EXC, 0); + res = feholdexcept (&saved); + if (res != 0) + { + printf ("feholdexcept failed: %d\n", res); + ++count_errors; + } +#if defined FE_TONEAREST && defined FE_TOWARDZERO + res = fesetround (FE_TOWARDZERO); + if (res != 0) + { + printf ("fesetround failed: %d\n", res); + ++count_errors; + } +#endif + test_exceptions ("feholdexcept_tests 0 test", NO_EXC, 0); +#ifdef FE_INVALID + feraiseexcept (FE_INVALID); + test_exceptions ("feholdexcept_tests FE_INVALID test", + INVALID_EXC, 0); +#endif + res = feupdateenv (&saved); + if (res != 0) + { + printf ("feupdateenv failed: %d\n", res); + ++count_errors; + } +#if defined FE_TONEAREST && defined FE_TOWARDZERO + res = fegetround (); + if (res != FE_TONEAREST) + { + printf ("feupdateenv didn't restore rounding mode: %d\n", res); + ++count_errors; + } +#endif + test_exceptions ("feholdexcept_tests FE_DIVBYZERO|FE_INVALID test", + DIVBYZERO_EXC | INVALID_EXC, 0); + feclearexcept (FE_ALL_EXCEPT); +#ifdef FE_INVALID + feraiseexcept (FE_INVALID); +#endif +#if defined FE_TONEAREST && defined FE_UPWARD + res = fesetround (FE_UPWARD); + if (res != 0) + { + printf ("fesetround failed: %d\n", res); + ++count_errors; + } +#endif + res = feholdexcept (&saved2); + if (res != 0) + { + printf ("feholdexcept failed: %d\n", res); + ++count_errors; + } +#if defined FE_TONEAREST && defined FE_UPWARD + res = fesetround (FE_TONEAREST); + if (res != 0) + { + printf ("fesetround failed: %d\n", res); + ++count_errors; + } +#endif + test_exceptions ("feholdexcept_tests 0 2nd test", NO_EXC, 0); +#ifdef FE_INEXACT + feraiseexcept (FE_INEXACT); + test_exceptions ("feholdexcept_tests FE_INEXACT test", + INEXACT_EXC, 0); +#endif + res = feupdateenv (&saved2); + if (res != 0) + { + printf ("feupdateenv failed: %d\n", res); + ++count_errors; + } +#if defined FE_TONEAREST && defined FE_UPWARD + res = fegetround (); + if (res != FE_UPWARD) + { + printf ("feupdateenv didn't restore rounding mode: %d\n", res); + ++count_errors; + } + fesetround (FE_TONEAREST); +#endif + test_exceptions ("feholdexcept_tests FE_INEXACT|FE_INVALID test", + INVALID_EXC | INEXACT_EXC, 0); + feclearexcept (FE_ALL_EXCEPT); +} + + +/* IEC 559 and ISO C99 define a default startup environment */ +static void +initial_tests (void) +{ + test_exceptions ("Initially all exceptions should be cleared", + NO_EXC, 0); +#ifdef FE_TONEAREST + test_rounding ("Rounding direction should be initalized to nearest", + FE_TONEAREST); +#endif +} +#endif + + +int +main (void) +{ +#ifdef __UCLIBC_HAS_FENV__ + initial_tests (); + fe_tests (); + feenv_tests (); + feholdexcept_tests (); + + if (count_errors) + { + printf ("\n%d errors occurred.\n", count_errors); + exit (1); + } + printf ("\n All tests passed successfully.\n"); + return 0; +#else + return 23; +#endif +} |