]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - mpfr/tests/tests.c
Imported gcc-4.4.3
[msp430-gcc.git] / mpfr / tests / tests.c
diff --git a/mpfr/tests/tests.c b/mpfr/tests/tests.c
new file mode 100644 (file)
index 0000000..c510add
--- /dev/null
@@ -0,0 +1,876 @@
+/* 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);
+}