| /* Miscellaneous support for test programs. |
| |
| Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. |
| Contributed by the Arenaire and Cacao projects, INRIA. |
| |
| This file is part of the GNU MPFR Library. |
| |
| The GNU MPFR 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 MPFR 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 MPFR Library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #ifdef HAVE_CONFIG_H |
| # if HAVE_CONFIG_H |
| # include "config.h" /* for a build within gmp */ |
| # endif |
| #endif |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <float.h> |
| #include <errno.h> |
| |
| #ifdef HAVE_LOCALE_H |
| #include <locale.h> |
| #endif |
| |
| #ifdef TIME_WITH_SYS_TIME |
| # include <sys/time.h> /* for struct timeval */ |
| # include <time.h> |
| #elif defined HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| #else |
| # include <time.h> |
| #endif |
| |
| /* <sys/fpu.h> is needed to have union fpc_csr defined under IRIX64 |
| (see below). Let's include it only if need be. */ |
| #if defined HAVE_SYS_FPU_H && defined HAVE_FPC_CSR |
| # include <sys/fpu.h> |
| #endif |
| |
| #ifdef MPFR_TESTS_TIMEOUT |
| #include <sys/resource.h> |
| #endif |
| |
| #include "mpfr-test.h" |
| |
| #ifdef MPFR_FPU_PREC |
| /* This option allows to test MPFR on x86 processors when the FPU |
| * rounding precision has been changed. As MPFR is a library, this can |
| * occur in practice, either by the calling software or by some other |
| * library or plug-in used by the calling software. This option is |
| * mainly for developers. If it is used, then the <fpu_control.h> |
| * header is assumed to exist and work like under Linux/x86. MPFR does |
| * not need to be recompiled. So, a possible usage is the following: |
| * |
| * cd tests |
| * make clean |
| * make check CFLAGS="-g -O2 -ffloat-store -DMPFR_FPU_PREC=_FPU_SINGLE" |
| * |
| * i.e. just add -DMPFR_FPU_PREC=... to the CFLAGS found in Makefile. |
| * |
| * Notes: |
| * + SSE2 (used to implement double's on x86_64, and possibly on x86 |
| * too, depending on the compiler configuration and flags) is not |
| * affected by the dynamic precision. |
| * + When the FPU is set to single precision, the behavior of MPFR |
| * functions that have a native floating-point type (float, double, |
| * long double) as argument or return value is not guaranteed. |
| */ |
| |
| #include <fpu_control.h> |
| |
| static void |
| set_fpu_prec (void) |
| { |
| fpu_control_t cw; |
| |
| _FPU_GETCW(cw); |
| cw &= ~(_FPU_EXTENDED|_FPU_DOUBLE|_FPU_SINGLE); |
| cw |= (MPFR_FPU_PREC); |
| _FPU_SETCW(cw); |
| } |
| |
| #endif |
| |
| static mp_exp_t default_emin, default_emax; |
| |
| static void tests_rand_start (void); |
| static void tests_rand_end (void); |
| static void tests_limit_start (void); |
| |
| /* We want to always import the function mpfr_dump inside the test |
| suite, so that we can use it in GDB. But it doesn't work if we build |
| a Windows DLL (initializer element is not a constant) */ |
| #if !__GMP_LIBGMP_DLL |
| extern void (*dummy_func) (mpfr_srcptr); |
| void (*dummy_func)(mpfr_srcptr) = mpfr_dump; |
| #endif |
| |
| void |
| test_version (void) |
| { |
| const char *version; |
| |
| /* VL: I get the following error on an OpenSUSE machine, and changing |
| the value of shlibpath_overrides_runpath in the libtool file from |
| 'no' to 'yes' fixes the problem. */ |
| |
| version = mpfr_get_version (); |
| if (strcmp (MPFR_VERSION_STRING, version) == 0) |
| { |
| char buffer[16]; |
| int i; |
| |
| sprintf (buffer, "%d.%d.%d", MPFR_VERSION_MAJOR, MPFR_VERSION_MINOR, |
| MPFR_VERSION_PATCHLEVEL); |
| for (i = 0; buffer[i] == version[i]; i++) |
| if (buffer[i] == '\0') |
| return; |
| if (buffer[i] == '\0' && version[i] == '-') |
| return; |
| printf ("MPFR_VERSION_MAJOR.MPFR_VERSION_MINOR.MPFR_VERSION_PATCHLEVEL" |
| " (%s)\nand MPFR_VERSION_STRING (%s) do not match!\nIt seems " |
| "that the mpfr.h file has been corrupted.\n", buffer, version); |
| exit (1); |
| } |
| |
| printf ("Incorrect MPFR version! (%s header vs %s library)\n" |
| "Nothing else has been tested since for this reason,\n" |
| "any other test may fail. Please fix this one first.\n\n" |
| "You can try to avoid this problem by changing the value of\n" |
| "shlibpath_overrides_runpath in the libtool file and rebuild\n" |
| "MPFR (make clean && make && make check).\n" |
| "Otherwise this error may be due to a corrupted mpfr.h, an\n" |
| "incomplete build (try to rebuild MPFR from scratch and/or\n" |
| "use 'make clean'), or something wrong in the system.\n", |
| MPFR_VERSION_STRING, version); |
| exit (1); |
| } |
| |
| void |
| tests_start_mpfr (void) |
| { |
| test_version (); |
| |
| /* don't buffer, so output is not lost if a test causes a segv etc */ |
| setbuf (stdout, NULL); |
| |
| #if defined HAVE_LOCALE_H && defined HAVE_SETLOCALE |
| /* Added on 2005-07-09. This allows to test MPFR under various |
| locales. New bugs will probably be found, in particular with |
| LC_ALL="tr_TR.ISO8859-9" because of the i/I character... */ |
| setlocale (LC_ALL, ""); |
| #endif |
| |
| #ifdef MPFR_FPU_PREC |
| set_fpu_prec (); |
| #endif |
| |
| tests_memory_start (); |
| tests_rand_start (); |
| tests_limit_start (); |
| |
| default_emin = mpfr_get_emin (); |
| default_emax = mpfr_get_emax (); |
| } |
| |
| void |
| tests_end_mpfr (void) |
| { |
| int err = 0; |
| |
| if (mpfr_get_emin () != default_emin) |
| { |
| printf ("Default emin value has not been restored!\n"); |
| err = 1; |
| } |
| |
| if (mpfr_get_emax () != default_emax) |
| { |
| printf ("Default emax value has not been restored!\n"); |
| err = 1; |
| } |
| |
| mpfr_free_cache (); |
| tests_rand_end (); |
| tests_memory_end (); |
| |
| if (err) |
| exit (err); |
| } |
| |
| static void |
| tests_limit_start (void) |
| { |
| #ifdef MPFR_TESTS_TIMEOUT |
| struct rlimit rlim[1]; |
| char *timeoutp; |
| int timeout; |
| |
| timeoutp = getenv ("MPFR_TESTS_TIMEOUT"); |
| timeout = timeoutp != NULL ? atoi (timeoutp) : MPFR_TESTS_TIMEOUT; |
| if (timeout > 0) |
| { |
| /* We need to call getrlimit first to initialize rlim_max to |
| an acceptable value for setrlimit. When enabled, timeouts |
| are regarded as important: we don't want to take too much |
| CPU time on machines shared with other users. So, if we |
| can't set the timeout, we exit immediately. */ |
| if (getrlimit (RLIMIT_CPU, rlim)) |
| { |
| printf ("Error: getrlimit failed\n"); |
| exit (1); |
| } |
| rlim->rlim_cur = timeout; |
| if (setrlimit (RLIMIT_CPU, rlim)) |
| { |
| printf ("Error: setrlimit failed\n"); |
| exit (1); |
| } |
| } |
| #endif |
| } |
| |
| static void |
| tests_rand_start (void) |
| { |
| gmp_randstate_ptr rands; |
| char *perform_seed; |
| unsigned long seed; |
| |
| if (__gmp_rands_initialized) |
| { |
| printf ( |
| "Please let tests_start() initialize the global __gmp_rands, i.e.\n" |
| "ensure that function is called before the first use of RANDS.\n"); |
| exit (1); |
| } |
| |
| gmp_randinit_default (__gmp_rands); |
| __gmp_rands_initialized = 1; |
| rands = __gmp_rands; |
| |
| perform_seed = getenv ("GMP_CHECK_RANDOMIZE"); |
| if (perform_seed != NULL) |
| { |
| seed = atoi (perform_seed); |
| if (! (seed == 0 || seed == 1)) |
| { |
| printf ("Re-seeding with GMP_CHECK_RANDOMIZE=%lu\n", seed); |
| gmp_randseed_ui (rands, seed); |
| } |
| else |
| { |
| #ifdef HAVE_GETTIMEOFDAY |
| struct timeval tv; |
| gettimeofday (&tv, NULL); |
| seed = tv.tv_sec + tv.tv_usec; |
| #else |
| time_t tv; |
| time (&tv); |
| seed = tv; |
| #endif |
| gmp_randseed_ui (rands, seed); |
| printf ("Seed GMP_CHECK_RANDOMIZE=%lu " |
| "(include this in bug reports)\n", seed); |
| } |
| } |
| else |
| gmp_randseed_ui (rands, 0x2143FEDC); |
| } |
| |
| static void |
| tests_rand_end (void) |
| { |
| RANDS_CLEAR (); |
| } |
| |
| /* initialization function for tests using the hardware floats |
| Not very useful now. */ |
| void |
| mpfr_test_init (void) |
| { |
| double d; |
| #ifdef HAVE_FPC_CSR |
| /* to get denormalized numbers on IRIX64 */ |
| union fpc_csr exp; |
| |
| exp.fc_word = get_fpc_csr(); |
| exp.fc_struct.flush = 0; |
| set_fpc_csr(exp.fc_word); |
| #endif |
| #ifdef HAVE_DENORMS |
| d = DBL_MIN; |
| if (2.0 * (d / 2.0) != d) |
| { |
| printf ("Error: HAVE_DENORMS defined, but no subnormals.\n"); |
| exit (1); |
| } |
| #endif |
| |
| /* generate DBL_EPSILON with a loop to avoid that the compiler |
| optimizes the code below in non-IEEE 754 mode, deciding that |
| c = d is always false. */ |
| #if 0 |
| for (eps = 1.0; eps != DBL_EPSILON; eps /= 2.0); |
| c = 1.0 + eps; |
| d = eps * (1.0 - eps) / 2.0; |
| d += c; |
| if (c != d) |
| { |
| printf ("Warning: IEEE 754 standard not fully supported\n" |
| " (maybe extended precision not disabled)\n" |
| " Some tests may fail\n"); |
| } |
| #endif |
| } |
| |
| |
| /* generate a random limb */ |
| mp_limb_t |
| randlimb (void) |
| { |
| mp_limb_t limb; |
| |
| _gmp_rand (&limb, RANDS, BITS_PER_MP_LIMB); |
| return limb; |
| } |
| |
| /* returns ulp(x) for x a 'normal' double-precision number */ |
| double |
| Ulp (double x) |
| { |
| double y, eps; |
| |
| if (x < 0) x = -x; |
| |
| y = x * 2.220446049250313080847263336181640625e-16 ; /* x / 2^52 */ |
| |
| /* as ulp(x) <= y = x/2^52 < 2*ulp(x), |
| we have x + ulp(x) <= x + y <= x + 2*ulp(x), |
| therefore o(x + y) = x + ulp(x) or x + 2*ulp(x) */ |
| |
| eps = x + y; |
| eps = eps - x; /* ulp(x) or 2*ulp(x) */ |
| |
| return (eps > y) ? 0.5 * eps : eps; |
| } |
| |
| /* returns the number of ulp's between a and b, |
| where a and b can be any floating-point number, except NaN |
| */ |
| int |
| ulp (double a, double b) |
| { |
| double twoa; |
| |
| if (a == b) return 0; /* also deals with a=b=inf or -inf */ |
| |
| twoa = a + a; |
| if (twoa == a) /* a is +/-0.0 or +/-Inf */ |
| return ((b < a) ? INT_MAX : -INT_MAX); |
| |
| return (int) ((a - b) / Ulp (a)); |
| } |
| |
| /* return double m*2^e */ |
| double |
| dbl (double m, int e) |
| { |
| if (e >=0 ) |
| while (e-- > 0) |
| m *= 2.0; |
| else |
| while (e++ < 0) |
| m /= 2.0; |
| return m; |
| } |
| |
| /* Warning: NaN values cannot be distinguished if MPFR_NANISNAN is defined. */ |
| int |
| Isnan (double d) |
| { |
| return (d) != (d); |
| } |
| |
| void |
| d_trace (const char *name, double d) |
| { |
| union { |
| double d; |
| unsigned char b[sizeof(double)]; |
| } u; |
| int i; |
| |
| if (name != NULL && name[0] != '\0') |
| printf ("%s=", name); |
| |
| u.d = d; |
| printf ("["); |
| for (i = 0; i < (int) sizeof (u.b); i++) |
| { |
| if (i != 0) |
| printf (" "); |
| printf ("%02X", (int) u.b[i]); |
| } |
| printf ("] %.20g\n", d); |
| } |
| |
| void |
| ld_trace (const char *name, long double ld) |
| { |
| union { |
| long double ld; |
| unsigned char b[sizeof(long double)]; |
| } u; |
| int i; |
| |
| if (name != NULL && name[0] != '\0') |
| printf ("%s=", name); |
| |
| u.ld = ld; |
| printf ("["); |
| for (i = 0; i < (int) sizeof (u.b); i++) |
| { |
| if (i != 0) |
| printf (" "); |
| printf ("%02X", (int) u.b[i]); |
| } |
| printf ("] %.20Lg\n", ld); |
| } |
| |
| /* Open a file in the src directory - can't use fopen directly */ |
| FILE * |
| src_fopen (const char *filename, const char *mode) |
| { |
| const char *srcdir = getenv ("srcdir"); |
| char *buffer; |
| FILE *f; |
| |
| if (srcdir == NULL) |
| return fopen (filename, mode); |
| buffer = |
| (char*) (*__gmp_allocate_func) (strlen (filename) + strlen (srcdir) + 2); |
| if (buffer == NULL) |
| { |
| printf ("src_fopen: failed to alloc memory)\n"); |
| exit (1); |
| } |
| sprintf (buffer, "%s/%s", srcdir, filename); |
| f = fopen (buffer, mode); |
| (*__gmp_free_func) (buffer, strlen (filename) + strlen (srcdir) + 2); |
| return f; |
| } |
| |
| void |
| set_emin (mp_exp_t exponent) |
| { |
| if (mpfr_set_emin (exponent)) |
| { |
| printf ("set_emin: setting emin to %ld failed\n", (long int) exponent); |
| exit (1); |
| } |
| } |
| |
| void |
| set_emax (mp_exp_t exponent) |
| { |
| if (mpfr_set_emax (exponent)) |
| { |
| printf ("set_emax: setting emax to %ld failed\n", (long int) exponent); |
| exit (1); |
| } |
| } |
| |
| /* pos is 512 times the proportion of negative numbers. |
| If pos=256, half of the numbers are negative. |
| If pos=0, all generated numbers are positive. |
| */ |
| void |
| tests_default_random (mpfr_ptr x, int pos, mp_exp_t emin, mp_exp_t emax) |
| { |
| MPFR_ASSERTN (emin <= emax); |
| MPFR_ASSERTN (emin >= MPFR_EMIN_MIN); |
| MPFR_ASSERTN (emax <= MPFR_EMAX_MAX); |
| /* but it isn't required that emin and emax are in the current |
| exponent range (see below), so that underflow/overflow checks |
| can be done on 64-bit machines. */ |
| |
| mpfr_urandomb (x, RANDS); |
| if (MPFR_IS_PURE_FP (x) && (emin >= 1 || (randlimb () & 1))) |
| { |
| mp_exp_t e; |
| e = MPFR_GET_EXP (x) + |
| (emin + (long) (randlimb () % (emax - emin + 1))); |
| /* Note: There should be no overflow here because both terms are |
| between MPFR_EMIN_MIN and MPFR_EMAX_MAX, but the sum e isn't |
| necessarily between MPFR_EMIN_MIN and MPFR_EMAX_MAX. */ |
| if (mpfr_set_exp (x, e)) |
| { |
| /* The random number doesn't fit in the current exponent range. |
| In this case, test the function in the extended exponent range, |
| which should be restored by the caller. */ |
| mpfr_set_emin (MPFR_EMIN_MIN); |
| mpfr_set_emax (MPFR_EMAX_MAX); |
| mpfr_set_exp (x, e); |
| } |
| } |
| if (randlimb () % 512 < pos) |
| mpfr_neg (x, x, GMP_RNDN); |
| } |
| |
| /* The test_one argument is a boolean. If it is true, then the function |
| is tested in only one rounding mode (the one provided in rnd) and the |
| variable rndnext is not used (due to the break). If it is false, then |
| the function is tested in the 4 rounding modes, and rnd must initially |
| be GMP_RNDZ; thus rndnext will be initialized in the first iteration. |
| gcc may give a warning about rndnext, but this is an easy and correct |
| way to implement a simple queue for the rounding modes. |
| As examples of use, see the calls to test4rm from the data_check and |
| bad_cases functions. */ |
| static void |
| test4rm (int (*fct) (FLIST), mpfr_srcptr x, mpfr_ptr y, mpfr_ptr z, |
| mp_rnd_t rnd, int test_one, char *name) |
| { |
| mp_prec_t yprec = MPFR_PREC (y); |
| mp_rnd_t rndnext = GMP_RND_MAX; /* means uninitialized */ |
| |
| MPFR_ASSERTN (test_one || rnd == GMP_RNDZ); |
| mpfr_set_prec (z, yprec); |
| while (1) |
| { |
| MPFR_ASSERTN (rnd != GMP_RND_MAX); |
| fct (z, x, rnd); |
| if (! mpfr_equal_p (y, z)) |
| { |
| printf ("Error for %s with xprec=%lu, yprec=%lu, rnd=%s\nx = ", |
| name, (unsigned long) MPFR_PREC (x), (unsigned long) yprec, |
| mpfr_print_rnd_mode (rnd)); |
| mpfr_out_str (stdout, 16, 0, x, GMP_RNDN); |
| printf ("\nexpected "); |
| mpfr_out_str (stdout, 16, 0, y, GMP_RNDN); |
| printf ("\ngot "); |
| mpfr_out_str (stdout, 16, 0, z, GMP_RNDN); |
| printf ("\n"); |
| exit (1); |
| } |
| if (test_one || rnd == GMP_RNDN) |
| break; |
| if (rnd == GMP_RNDZ) |
| { |
| if (MPFR_IS_NEG (y)) |
| { |
| rnd = GMP_RNDU; |
| rndnext = GMP_RNDD; |
| } |
| else |
| { |
| rnd = GMP_RNDD; |
| rndnext = GMP_RNDU; |
| } |
| } |
| else |
| { |
| rnd = rndnext; |
| if (rndnext != GMP_RNDN) |
| { |
| rndnext = GMP_RNDN; |
| mpfr_nexttoinf (y); |
| } |
| else |
| { |
| if (yprec == MPFR_PREC_MIN) |
| break; |
| mpfr_prec_round (y, --yprec, GMP_RNDZ); |
| mpfr_set_prec (z, yprec); |
| } |
| } |
| } |
| } |
| |
| /* Check data in file f for function foo, with name 'name'. |
| Each line consists of the file f one: |
| |
| xprec yprec rnd x y |
| |
| where: |
| |
| xprec is the input precision |
| yprec is the output precision |
| rnd is the rounding mode (n, z, u, d, Z) |
| x is the input (hexadecimal format) |
| y is the expected output (hexadecimal format) for foo(x) with rounding rnd |
| |
| If rnd is Z, y is the expected output in round-towards-zero, and the |
| three directed rounding modes are tested, then the round-to-nearest |
| mode is tested in precision yprec-1. This is useful for worst cases, |
| where yprec is the minimum value such that one has a worst case in a |
| directed rounding mode. |
| */ |
| void |
| data_check (char *f, int (*foo) (FLIST), char *name) |
| { |
| FILE *fp; |
| mp_prec_t xprec, yprec; |
| mpfr_t x, y, z; |
| mp_rnd_t rnd; |
| char r; |
| int c; |
| |
| fp = fopen (f, "r"); |
| if (fp == NULL) |
| { |
| char *v = (char *) MPFR_VERSION_STRING; |
| |
| /* In the '-dev' versions, assume that the data file exists and |
| return an error if the file cannot be opened to make sure |
| that such failures are detected. */ |
| while (*v != '\0') |
| v++; |
| if (v[-4] == '-' && v[-3] == 'd' && v[-2] == 'e' && v[-1] == 'v') |
| { |
| printf ("Error: unable to open file '%s'\n", f); |
| exit (1); |
| } |
| else |
| return; |
| } |
| |
| mpfr_init (x); |
| mpfr_init (y); |
| mpfr_init (z); |
| |
| while (!feof (fp)) |
| { |
| /* skip whitespace, for consistency */ |
| if (fscanf (fp, " ") == EOF) |
| { |
| if (ferror (fp)) |
| { |
| perror ("data_check"); |
| exit (1); |
| } |
| else |
| break; /* end of file */ |
| } |
| |
| if ((c = getc (fp)) == EOF) |
| { |
| if (ferror (fp)) |
| { |
| perror ("data_check"); |
| exit (1); |
| } |
| else |
| break; /* end of file */ |
| } |
| |
| if (c == '#') /* comment: read entire line */ |
| { |
| do |
| { |
| c = getc (fp); |
| } |
| while (!feof (fp) && c != '\n'); |
| } |
| else |
| { |
| ungetc (c, fp); |
| |
| c = fscanf (fp, "%lu %lu %c", &xprec, &yprec, &r); |
| if (c == EOF) |
| { |
| perror ("data_check"); |
| exit (1); |
| } |
| else if (c != 3) |
| { |
| printf ("Error: corrupted line in file '%s'\n", f); |
| exit (1); |
| } |
| |
| switch (r) |
| { |
| case 'n': |
| rnd = GMP_RNDN; |
| break; |
| case 'z': case 'Z': |
| rnd = GMP_RNDZ; |
| break; |
| case 'u': |
| rnd = GMP_RNDU; |
| break; |
| case 'd': |
| rnd = GMP_RNDD; |
| break; |
| default: |
| printf ("Error: unexpected rounding mode" |
| " in file '%s': %c\n", f, (int) r); |
| exit (1); |
| } |
| mpfr_set_prec (x, xprec); |
| mpfr_set_prec (y, yprec); |
| if (mpfr_inp_str (x, fp, 0, GMP_RNDN) == 0) |
| { |
| printf ("Error: corrupted argument in file '%s'\n", f); |
| exit (1); |
| } |
| if (mpfr_inp_str (y, fp, 0, GMP_RNDN) == 0) |
| { |
| printf ("Error: corrupted result in file '%s'\n", f); |
| exit (1); |
| } |
| if (getc (fp) != '\n') |
| { |
| printf ("Error: result not followed by \\n in file '%s'\n", f); |
| exit (1); |
| } |
| /* Skip whitespace, in particular at the end of the file. */ |
| if (fscanf (fp, " ") == EOF && ferror (fp)) |
| { |
| perror ("data_check"); |
| exit (1); |
| } |
| test4rm (foo, x, y, z, rnd, r != 'Z', name); |
| } |
| } |
| |
| mpfr_clear (x); |
| mpfr_clear (y); |
| mpfr_clear (z); |
| |
| fclose (fp); |
| } |
| |
| /* Test n random bad cases. A precision py in [pymin,pymax] and |
| * a number y of precision py are chosen randomly. One computes |
| * x = inv(y) in precision px = py + psup (rounded to nearest). |
| * Then (in general), y is a bad case for fct in precision py (in |
| * the directed rounding modes, but also in the rounding-to-nearest |
| * mode for some lower precision: see data_check). |
| * fct, inv, name: data related to the function. |
| * pos, emin, emax: arguments for tests_default_random. |
| */ |
| void |
| bad_cases (int (*fct)(FLIST), int (*inv)(FLIST), char *name, |
| int pos, mp_exp_t emin, mp_exp_t emax, |
| mp_prec_t pymin, mp_prec_t pymax, mp_prec_t psup, |
| int n) |
| { |
| mpfr_t x, y, z; |
| char *dbgenv; |
| int i, dbg; |
| mp_exp_t old_emin, old_emax; |
| |
| old_emin = mpfr_get_emin (); |
| old_emax = mpfr_get_emax (); |
| |
| dbgenv = getenv ("MPFR_DEBUG_BADCASES"); |
| dbg = dbgenv != 0 ? atoi (dbgenv) : 0; /* debug level */ |
| mpfr_inits (x, y, z, (mpfr_ptr) 0); |
| for (i = 0; i < n; i++) |
| { |
| mp_prec_t px, py, pz; |
| int inex; |
| |
| if (dbg) |
| printf ("bad_cases: i = %d\n", i); |
| py = pymin + (randlimb () % (pymax - pymin + 1)); |
| mpfr_set_prec (y, py); |
| tests_default_random (y, pos, emin, emax); |
| if (dbg) |
| { |
| printf ("bad_cases: yprec =%4ld, y = ", (long) py); |
| mpfr_out_str (stdout, 16, 0, y, GMP_RNDN); |
| printf ("\n"); |
| } |
| px = py + psup; |
| mpfr_set_prec (x, px); |
| mpfr_clear_flags (); |
| inv (x, y, GMP_RNDN); |
| if (mpfr_nanflag_p () || mpfr_overflow_p () || mpfr_underflow_p ()) |
| { |
| if (dbg) |
| printf ("bad_cases: no normal inverse\n"); |
| goto next_i; |
| } |
| if (dbg > 1) |
| { |
| printf ("bad_cases: x = "); |
| mpfr_out_str (stdout, 16, 0, x, GMP_RNDN); |
| printf ("\n"); |
| } |
| pz = px; |
| do |
| { |
| pz += 32; |
| mpfr_set_prec (z, pz); |
| if (fct (z, x, GMP_RNDN) == 0) |
| { |
| if (dbg) |
| printf ("bad_cases: exact case\n"); |
| goto next_i; |
| } |
| if (dbg) |
| { |
| if (dbg > 1) |
| { |
| printf ("bad_cases: %s(x) ~= ", name); |
| mpfr_out_str (stdout, 16, 0, z, GMP_RNDN); |
| } |
| else |
| { |
| printf ("bad_cases: [GMP_RNDZ] ~= "); |
| mpfr_out_str (stdout, 16, 40, z, GMP_RNDZ); |
| } |
| printf ("\n"); |
| } |
| inex = mpfr_prec_round (z, py, GMP_RNDN); |
| if (mpfr_nanflag_p () || mpfr_overflow_p () || mpfr_underflow_p () |
| || ! mpfr_equal_p (z, y)) |
| { |
| if (dbg) |
| printf ("bad_cases: inverse doesn't match\n"); |
| goto next_i; |
| } |
| } |
| while (inex == 0); |
| /* We really have a bad case. */ |
| do |
| py--; |
| while (py >= MPFR_PREC_MIN && mpfr_prec_round (z, py, GMP_RNDZ) == 0); |
| py++; |
| /* py is now the smallest output precision such that we have |
| a bad case in the directed rounding modes. */ |
| if (mpfr_prec_round (y, py, GMP_RNDZ) != 0) |
| { |
| printf ("Internal error for i = %d\n", i); |
| exit (1); |
| } |
| if ((inex > 0 && MPFR_IS_POS (z)) || |
| (inex < 0 && MPFR_IS_NEG (z))) |
| { |
| mpfr_nexttozero (y); |
| if (mpfr_zero_p (y)) |
| goto next_i; |
| } |
| if (dbg) |
| { |
| printf ("bad_cases: yprec =%4ld, y = ", (long) py); |
| mpfr_out_str (stdout, 16, 0, y, GMP_RNDN); |
| printf ("\n"); |
| } |
| /* Note: y is now the expected result rounded towards zero. */ |
| test4rm (fct, x, y, z, GMP_RNDZ, 0, name); |
| next_i: |
| /* In case the exponent range has been changed by |
| tests_default_random()... */ |
| mpfr_set_emin (old_emin); |
| mpfr_set_emax (old_emax); |
| } |
| mpfr_clears (x, y, z, (mpfr_ptr) 0); |
| } |