]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - mpfr/tests/tpow_all.c
Imported gcc-4.4.3
[msp430-gcc.git] / mpfr / tests / tpow_all.c
diff --git a/mpfr/tests/tpow_all.c b/mpfr/tests/tpow_all.c
new file mode 100644 (file)
index 0000000..181671b
--- /dev/null
@@ -0,0 +1,773 @@
+/* Test file for the various power functions
+
+Copyright 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. */
+
+/* Note: some tests of the other tpow* test files could be moved there.
+   The main goal of this test file is to test _all_ the power functions
+   on special values, to make sure that they are consistent and give the
+   expected result, in particular because such special cases are handled
+   in different ways in each function. */
+
+/* Execute with at least an argument to report all the errors found by
+   comparisons. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "mpfr-test.h"
+
+/* Behavior of cmpres (called by test_others):
+ *   0: stop as soon as an error is found.
+ *   1: report all errors found by test_others.
+ *  -1: the 1 is changed to this value as soon as an error has been found.
+ */
+static int all_cmpres_errors;
+
+/* Non-zero when extended exponent range */
+static int ext = 0;
+
+static char *val[] =
+  { "min", "min+", "max", "@NaN@", "-@Inf@", "-4", "-3", "-2", "-1.5",
+    "-1", "-0.5", "-0", "0", "0.5", "1", "1.5", "2", "3", "4", "@Inf@" };
+
+static void
+err (const char *s, int i, int j, int rnd, mpfr_srcptr z, int inex)
+{
+  puts (s);
+  if (ext)
+    puts ("extended exponent range");
+  printf ("x = %s, y = %s, %s\n", val[i], val[j],
+          mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+  printf ("z = ");
+  mpfr_out_str (stdout, 10, 0, z, GMP_RNDN);
+  printf ("\ninex = %d\n", inex);
+  exit (1);
+}
+
+/* Arguments:
+ *   spx: non-zero if px is a stringm zero if px is a MPFR number.
+ *   px: value of x (string or MPFR number).
+ *   sy: value of y (string).
+ *   rnd: rounding mode.
+ *   z1: expected result (null pointer if unknown pure FP value).
+ *   inex1: expected ternary value (if z1 is not a null pointer).
+ *   z2: computed result.
+ *   inex2: computed ternary value.
+ *   flags1: expected flags (computed flags in __gmpfr_flags).
+ *   s1, s2: strings about the context.
+ */
+static void
+cmpres (int spx, const void *px, const char *sy, mp_rnd_t rnd,
+        mpfr_srcptr z1, int inex1, mpfr_srcptr z2, int inex2,
+        unsigned int flags1, const char *s1, const char *s2)
+{
+  unsigned int flags2 = __gmpfr_flags;
+
+  if (flags1 == flags2)
+    {
+      /* Note: the test on the sign of z1 and z2 is needed
+         in case they are both zeros. */
+      if (z1 == NULL)
+        {
+          if (MPFR_IS_PURE_FP (z2))
+            return;
+        }
+      else if (SAME_SIGN (inex1, inex2) &&
+               ((MPFR_IS_NAN (z1) && MPFR_IS_NAN (z2)) ||
+                ((MPFR_IS_NEG (z1) ^ MPFR_IS_NEG (z2)) == 0 &&
+                 mpfr_equal_p (z1, z2))))
+        return;
+    }
+
+  printf ("Error in %s\nwith %s%s\nx = ", s1, s2,
+          ext ? ", extended exponent range" : "");
+  if (spx)
+    printf ("%s, ", (char *) px);
+  else
+    {
+      mpfr_out_str (stdout, 16, 0, (mpfr_ptr) px, GMP_RNDN);
+      puts (",");
+    }
+  printf ("y = %s, %s\n", sy, mpfr_print_rnd_mode (rnd));
+  printf ("Expected ");
+  if (z1 == NULL)
+    {
+      printf ("pure FP value, flags = %u\n", flags1);
+    }
+  else
+    {
+      mpfr_out_str (stdout, 16, 0, z1, GMP_RNDN);
+      printf (", inex = %d, flags = %u\n", SIGN (inex1), flags1);
+    }
+  printf ("Got      ");
+  mpfr_out_str (stdout, 16, 0, z2, GMP_RNDN);
+  printf (", inex = %d, flags = %u\n", SIGN (inex2), flags2);
+  if (all_cmpres_errors != 0)
+    all_cmpres_errors = -1;
+  else
+    exit (1);
+}
+
+static int
+is_odd (mpfr_srcptr x)
+{
+  /* works only with the values from val[] */
+  return mpfr_integer_p (x) && mpfr_fits_slong_p (x, GMP_RNDN) &&
+    (mpfr_get_si (x, GMP_RNDN) & 1);
+}
+
+/* Compare the result (z1,inex1) of mpfr_pow with all flags cleared
+   with those of mpfr_pow with all flags set and of the other power
+   functions. Arguments x and y are the input values; sx and sy are
+   their string representations (sx may be null); rnd contains the
+   rounding mode; s is a string containing the function that called
+   test_others. */
+static void
+test_others (const void *sx, const char *sy, mp_rnd_t rnd,
+             mpfr_srcptr x, mpfr_srcptr y, mpfr_srcptr z1,
+             int inex1, unsigned int flags, const char *s)
+{
+  mpfr_t z2;
+  int inex2;
+  int spx = sx != NULL;
+
+  if (!spx)
+    sx = x;
+
+  mpfr_init2 (z2, mpfr_get_prec (z1));
+
+  __gmpfr_flags = MPFR_FLAGS_ALL;
+  inex2 = mpfr_pow (z2, x, y, rnd);
+  cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+          s, "mpfr_pow, flags set");
+
+  /* If y is an integer that fits in an unsigned long and is not -0,
+     we can test mpfr_pow_ui. */
+  if (MPFR_IS_POS (y) && mpfr_integer_p (y) &&
+      mpfr_fits_ulong_p (y, GMP_RNDN))
+    {
+      unsigned long yy = mpfr_get_ui (y, GMP_RNDN);
+
+      mpfr_clear_flags ();
+      inex2 = mpfr_pow_ui (z2, x, yy, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+              s, "mpfr_pow_ui, flags cleared");
+      __gmpfr_flags = MPFR_FLAGS_ALL;
+      inex2 = mpfr_pow_ui (z2, x, yy, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+              s, "mpfr_pow_ui, flags set");
+
+      /* If x is an integer that fits in an unsigned long and is not -0,
+         we can also test mpfr_ui_pow_ui. */
+      if (MPFR_IS_POS (x) && mpfr_integer_p (x) &&
+          mpfr_fits_ulong_p (x, GMP_RNDN))
+        {
+          unsigned long xx = mpfr_get_ui (x, GMP_RNDN);
+
+          mpfr_clear_flags ();
+          inex2 = mpfr_ui_pow_ui (z2, xx, yy, rnd);
+          cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+                  s, "mpfr_ui_pow_ui, flags cleared");
+          __gmpfr_flags = MPFR_FLAGS_ALL;
+          inex2 = mpfr_ui_pow_ui (z2, xx, yy, rnd);
+          cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+                  s, "mpfr_ui_pow_ui, flags set");
+        }
+    }
+
+  /* If y is an integer but not -0 and not huge, we can test mpfr_pow_z,
+     and possibly mpfr_pow_si (and possibly mpfr_ui_div). */
+  if (MPFR_IS_ZERO (y) ? MPFR_IS_POS (y) :
+      (mpfr_integer_p (y) && MPFR_GET_EXP (y) < 256))
+    {
+      mpz_t yyy;
+
+      /* If y fits in a long, we can test mpfr_pow_si. */
+      if (mpfr_fits_slong_p (y, GMP_RNDN))
+        {
+          long yy = mpfr_get_si (y, GMP_RNDN);
+
+          mpfr_clear_flags ();
+          inex2 = mpfr_pow_si (z2, x, yy, rnd);
+          cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+                  s, "mpfr_pow_si, flags cleared");
+          __gmpfr_flags = MPFR_FLAGS_ALL;
+          inex2 = mpfr_pow_si (z2, x, yy, rnd);
+          cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+                  s, "mpfr_pow_si, flags set");
+
+          /* If y = -1, we can test mpfr_ui_div. */
+          if (yy == -1)
+            {
+              mpfr_clear_flags ();
+              inex2 = mpfr_ui_div (z2, 1, x, rnd);
+              cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+                      s, "mpfr_ui_div, flags cleared");
+              __gmpfr_flags = MPFR_FLAGS_ALL;
+              inex2 = mpfr_ui_div (z2, 1, x, rnd);
+              cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+                      s, "mpfr_ui_div, flags set");
+            }
+
+          /* If y = 2, we can test mpfr_sqr. */
+          if (yy == 2)
+            {
+              mpfr_clear_flags ();
+              inex2 = mpfr_sqr (z2, x, rnd);
+              cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+                      s, "mpfr_sqr, flags cleared");
+              __gmpfr_flags = MPFR_FLAGS_ALL;
+              inex2 = mpfr_sqr (z2, x, rnd);
+              cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+                      s, "mpfr_sqr, flags set");
+            }
+        }
+
+      /* Test mpfr_pow_z. */
+      mpz_init (yyy);
+      mpfr_get_z (yyy, y, GMP_RNDN);
+      mpfr_clear_flags ();
+      inex2 = mpfr_pow_z (z2, x, yyy, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+              s, "mpfr_pow_z, flags cleared");
+      __gmpfr_flags = MPFR_FLAGS_ALL;
+      inex2 = mpfr_pow_z (z2, x, yyy, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+              s, "mpfr_pow_z, flags set");
+      mpz_clear (yyy);
+    }
+
+  /* If y = 0.5, we can test mpfr_sqrt, except if x is -0 or -Inf (because
+     the rule for mpfr_pow on these special values is different). */
+  if (MPFR_IS_PURE_FP (y) && mpfr_cmp_str1 (y, "0.5") == 0 &&
+      ! ((MPFR_IS_ZERO (x) || MPFR_IS_INF (x)) && MPFR_IS_NEG (x)))
+    {
+      mpfr_clear_flags ();
+      inex2 = mpfr_sqrt (z2, x, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+              s, "mpfr_sqrt, flags cleared");
+      __gmpfr_flags = MPFR_FLAGS_ALL;
+      inex2 = mpfr_sqrt (z2, x, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+              s, "mpfr_sqrt, flags set");
+    }
+
+#if MPFR_VERSION >= MPFR_VERSION_NUM(2,4,0)
+  /* If y = -0.5, we can test mpfr_rec_sqrt, except if x = -Inf
+     (because the rule for mpfr_pow on -Inf is different). */
+  if (MPFR_IS_PURE_FP (y) && mpfr_cmp_str1 (y, "-0.5") == 0 &&
+      ! (MPFR_IS_INF (x) && MPFR_IS_NEG (x)))
+    {
+      mpfr_clear_flags ();
+      inex2 = mpfr_rec_sqrt (z2, x, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+              s, "mpfr_rec_sqrt, flags cleared");
+      __gmpfr_flags = MPFR_FLAGS_ALL;
+      inex2 = mpfr_rec_sqrt (z2, x, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+              s, "mpfr_rec_sqrt, flags set");
+    }
+#endif
+
+  /* If x is an integer that fits in an unsigned long and is not -0,
+     we can test mpfr_ui_pow. */
+  if (MPFR_IS_POS (x) && mpfr_integer_p (x) &&
+      mpfr_fits_ulong_p (x, GMP_RNDN))
+    {
+      unsigned long xx = mpfr_get_ui (x, GMP_RNDN);
+
+      mpfr_clear_flags ();
+      inex2 = mpfr_ui_pow (z2, xx, y, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+              s, "mpfr_ui_pow, flags cleared");
+      __gmpfr_flags = MPFR_FLAGS_ALL;
+      inex2 = mpfr_ui_pow (z2, xx, y, rnd);
+      cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+              s, "mpfr_ui_pow, flags set");
+
+      /* If x = 2, we can test mpfr_exp2. */
+      if (xx == 2)
+        {
+          mpfr_clear_flags ();
+          inex2 = mpfr_exp2 (z2, y, rnd);
+          cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+                  s, "mpfr_exp2, flags cleared");
+          __gmpfr_flags = MPFR_FLAGS_ALL;
+          inex2 = mpfr_exp2 (z2, y, rnd);
+          cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+                  s, "mpfr_exp2, flags set");
+        }
+
+      /* If x = 10, we can test mpfr_exp10. */
+      if (xx == 10)
+        {
+          mpfr_clear_flags ();
+          inex2 = mpfr_exp10 (z2, y, rnd);
+          cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, flags,
+                  s, "mpfr_exp10, flags cleared");
+          __gmpfr_flags = MPFR_FLAGS_ALL;
+          inex2 = mpfr_exp10 (z2, y, rnd);
+          cmpres (spx, sx, sy, rnd, z1, inex1, z2, inex2, MPFR_FLAGS_ALL,
+                  s, "mpfr_exp10, flags set");
+        }
+    }
+
+  mpfr_clear (z2);
+}
+
+static int
+my_setstr (mpfr_ptr t, const char *s)
+{
+  if (strcmp (s, "min") == 0)
+    {
+      mpfr_setmin (t, mpfr_get_emin ());
+      MPFR_SET_POS (t);
+      return 0;
+    }
+  if (strcmp (s, "min+") == 0)
+    {
+      mpfr_setmin (t, mpfr_get_emin ());
+      MPFR_SET_POS (t);
+      mpfr_nextabove (t);
+      return 0;
+    }
+  if (strcmp (s, "max") == 0)
+    {
+      mpfr_setmax (t, mpfr_get_emax ());
+      MPFR_SET_POS (t);
+      return 0;
+    }
+  return mpfr_set_str (t, s, 10, GMP_RNDN);
+}
+
+static void
+tst (void)
+{
+  int sv = sizeof (val) / sizeof (*val);
+  int i, j;
+  int rnd;
+  mpfr_t x, y, z, tmp;
+
+  mpfr_inits2 (53, x, y, z, tmp, (mpfr_ptr) 0);
+
+  for (i = 0; i < sv; i++)
+    for (j = 0; j < sv; j++)
+      RND_LOOP (rnd)
+        {
+          int exact, inex;
+          unsigned int flags;
+
+          if (my_setstr (x, val[i]) || my_setstr (y, val[j]))
+            {
+              printf ("internal error for (%d,%d,%d)\n", i, j, rnd);
+              exit (1);
+            }
+          mpfr_clear_flags ();
+          inex = mpfr_pow (z, x, y, (mp_rnd_t) rnd);
+          flags = __gmpfr_flags;
+          if (! MPFR_IS_NAN (z) && mpfr_nanflag_p ())
+            err ("got NaN flag without NaN value", i, j, rnd, z, inex);
+          if (MPFR_IS_NAN (z) && ! mpfr_nanflag_p ())
+            err ("got NaN value without NaN flag", i, j, rnd, z, inex);
+          if (inex != 0 && ! mpfr_inexflag_p ())
+            err ("got non-zero ternary value without inexact flag",
+                 i, j, rnd, z, inex);
+          if (inex == 0 && mpfr_inexflag_p ())
+            err ("got null ternary value with inexact flag",
+                 i, j, rnd, z, inex);
+          if (i >= 3 && j >= 3)
+            {
+              if (mpfr_underflow_p ())
+                err ("got underflow", i, j, rnd, z, inex);
+              if (mpfr_overflow_p ())
+                err ("got overflow", i, j, rnd, z, inex);
+              exact = MPFR_IS_SINGULAR (z) ||
+                (mpfr_mul_2ui (tmp, z, 16, GMP_RNDN), mpfr_integer_p (tmp));
+              if (exact && inex != 0)
+                err ("got exact value with ternary flag different from 0",
+                     i, j, rnd, z, inex);
+              if (! exact && inex == 0)
+                err ("got inexact value with ternary flag equal to 0",
+                     i, j, rnd, z, inex);
+            }
+          if (MPFR_IS_ZERO (x) && ! MPFR_IS_NAN (y) && MPFR_NOTZERO (y))
+            {
+              if (MPFR_IS_NEG (y) && ! MPFR_IS_INF (z))
+                err ("expected an infinity", i, j, rnd, z, inex);
+              if (MPFR_IS_POS (y) && ! MPFR_IS_ZERO (z))
+                err ("expected a zero", i, j, rnd, z, inex);
+              if ((MPFR_IS_NEG (x) && is_odd (y)) ^ MPFR_IS_NEG (z))
+                err ("wrong sign", i, j, rnd, z, inex);
+            }
+          if (! MPFR_IS_NAN (x) && mpfr_cmp_si (x, -1) == 0)
+            {
+              /* x = -1 */
+              if (! (MPFR_IS_INF (y) || mpfr_integer_p (y)) &&
+                  ! MPFR_IS_NAN (z))
+                err ("expected NaN", i, j, rnd, z, inex);
+              if ((MPFR_IS_INF (y) || (mpfr_integer_p (y) && ! is_odd (y)))
+                  && ! mpfr_equal_p (z, __gmpfr_one))
+                err ("expected 1", i, j, rnd, z, inex);
+              if (is_odd (y) &&
+                  (MPFR_IS_NAN (z) || mpfr_cmp_si (z, -1) != 0))
+                err ("expected -1", i, j, rnd, z, inex);
+            }
+          if ((mpfr_equal_p (x, __gmpfr_one) || MPFR_IS_ZERO (y)) &&
+              ! mpfr_equal_p (z, __gmpfr_one))
+            err ("expected 1", i, j, rnd, z, inex);
+          if (MPFR_IS_PURE_FP (x) && MPFR_IS_NEG (x) &&
+              MPFR_IS_FP (y) && ! mpfr_integer_p (y) &&
+              ! MPFR_IS_NAN (z))
+            err ("expected NaN", i, j, rnd, z, inex);
+          if (MPFR_IS_INF (y) && MPFR_NOTZERO (x))
+            {
+              int cmpabs1 = mpfr_cmpabs (x, __gmpfr_one);
+
+              if ((MPFR_IS_NEG (y) ? (cmpabs1 < 0) : (cmpabs1 > 0)) &&
+                  ! (MPFR_IS_POS (z) && MPFR_IS_INF (z)))
+                err ("expected +Inf", i, j, rnd, z, inex);
+              if ((MPFR_IS_NEG (y) ? (cmpabs1 > 0) : (cmpabs1 < 0)) &&
+                  ! (MPFR_IS_POS (z) && MPFR_IS_ZERO (z)))
+                err ("expected +0", i, j, rnd, z, inex);
+            }
+          if (MPFR_IS_INF (x) && ! MPFR_IS_NAN (y) && MPFR_NOTZERO (y))
+            {
+              if (MPFR_IS_POS (y) && ! MPFR_IS_INF (z))
+                err ("expected an infinity", i, j, rnd, z, inex);
+              if (MPFR_IS_NEG (y) && ! MPFR_IS_ZERO (z))
+                err ("expected a zero", i, j, rnd, z, inex);
+              if ((MPFR_IS_NEG (x) && is_odd (y)) ^ MPFR_IS_NEG (z))
+                err ("wrong sign", i, j, rnd, z, inex);
+            }
+          test_others (val[i], val[j], (mp_rnd_t) rnd, x, y, z, inex, flags,
+                       "tst");
+        }
+  mpfr_clears (x, y, z, tmp, (mpfr_ptr) 0);
+}
+
+static void
+underflow_up1 (void)
+{
+  mpfr_t delta, x, y, z, z0;
+  mp_exp_t n;
+  int inex;
+  int rnd;
+  int i;
+
+  n = mpfr_get_emin ();
+  if (n < LONG_MIN)
+    return;
+
+  mpfr_init2 (delta, 2);
+  inex = mpfr_set_ui_2exp (delta, 1, -2, GMP_RNDN);
+  MPFR_ASSERTN (inex == 0);
+
+  mpfr_init2 (x, 8);
+  inex = mpfr_set_ui (x, 2, GMP_RNDN);
+  MPFR_ASSERTN (inex == 0);
+
+  mpfr_init2 (y, sizeof (long) * CHAR_BIT + 4);
+  inex = mpfr_set_si (y, n, GMP_RNDN);
+  MPFR_ASSERTN (inex == 0);
+
+  mpfr_init2 (z0, 2);
+  mpfr_set_ui (z0, 0, GMP_RNDN);
+
+  mpfr_init2 (z, 32);
+
+  for (i = 0; i <= 12; i++)
+    {
+      unsigned int flags = 0;
+      char sy[16];
+
+      /* Test 2^(emin - i/4).
+       * --> Underflow iff i > 4.
+       * --> Zero in GMP_RNDN iff i >= 8.
+       */
+
+      if (i != 0 && i != 4)
+        flags |= MPFR_FLAGS_INEXACT;
+      if (i > 4)
+        flags |= MPFR_FLAGS_UNDERFLOW;
+
+      sprintf (sy, "emin - %d/4", i);
+
+      RND_LOOP (rnd)
+        {
+          int zero;
+
+          zero = (i > 4 && (rnd == GMP_RNDZ || rnd == GMP_RNDD)) ||
+            (i >= 8 && rnd == GMP_RNDN);
+
+          mpfr_clear_flags ();
+          inex = mpfr_pow (z, x, y, (mp_rnd_t) rnd);
+          cmpres (1, "2", sy, (mp_rnd_t) rnd, zero ? z0 : (mpfr_ptr) NULL,
+                  -1, z, inex, flags, "underflow_up1", "mpfr_pow");
+          test_others ("2", sy, (mp_rnd_t) rnd, x, y, z, inex, flags,
+                       "underflow_up1");
+        }
+
+      inex = mpfr_sub (y, y, delta, GMP_RNDN);
+      MPFR_ASSERTN (inex == 0);
+    }
+
+  mpfr_clears (delta, x, y, z, z0, (mpfr_ptr) 0);
+}
+
+/* With pow.c r5497, the following test fails on a 64-bit Linux machine
+ * due to a double-rounding problem when rescaling the result:
+ *   Error with underflow_up2 and extended exponent range
+ *   x = 7.fffffffffffffff0@-1,
+ *   y = 4611686018427387904, GMP_RNDN
+ *   Expected 1.0000000000000000@-1152921504606846976, inex = 1, flags = 9
+ *   Got      0, inex = -1, flags = 9
+ * With pow_ui.c r5423, the following test fails on a 64-bit Linux machine
+ * as underflows and overflows are not handled correctly (the approximation
+ * error is ignored):
+ *   Error with mpfr_pow_ui, flags cleared
+ *   x = 7.fffffffffffffff0@-1,
+ *   y = 4611686018427387904, GMP_RNDN
+ *   Expected 1.0000000000000000@-1152921504606846976, inex = 1, flags = 9
+ *   Got      0, inex = -1, flags = 9
+ */
+static void
+underflow_up2 (void)
+{
+  mpfr_t x, y, z, z0, eps;
+  mp_exp_t n;
+  int inex;
+  int rnd;
+
+  n = 1 - mpfr_get_emin ();
+  MPFR_ASSERTN (n > 1);
+  if (n > ULONG_MAX)
+    return;
+
+  mpfr_init2 (eps, 2);
+  mpfr_set_ui_2exp (eps, 1, -1, GMP_RNDN);  /* 1/2 */
+  mpfr_div_ui (eps, eps, n, GMP_RNDZ);      /* 1/(2n) rounded toward zero */
+
+  mpfr_init2 (x, sizeof (unsigned long) * CHAR_BIT + 1);
+  inex = mpfr_ui_sub (x, 1, eps, GMP_RNDN);
+  MPFR_ASSERTN (inex == 0);  /* since n < 2^(size_of_long_in_bits) */
+  inex = mpfr_div_2ui (x, x, 1, GMP_RNDN);  /* 1/2 - eps/2 exactly */
+  MPFR_ASSERTN (inex == 0);
+
+  mpfr_init2 (y, sizeof (unsigned long) * CHAR_BIT);
+  inex = mpfr_set_ui (y, n, GMP_RNDN);
+  MPFR_ASSERTN (inex == 0);
+
+  /* 0 < eps < 1 / (2n), thus (1 - eps)^n > 1/2,
+     and 1/2 (1/2)^n < (1/2 - eps/2)^n < (1/2)^n. */
+  mpfr_inits2 (64, z, z0, (mpfr_ptr) 0);
+  RND_LOOP (rnd)
+    {
+      unsigned int ufinex = MPFR_FLAGS_UNDERFLOW | MPFR_FLAGS_INEXACT;
+      int expected_inex;
+      char sy[256];
+
+      mpfr_set_ui (z0, 0, GMP_RNDN);
+      expected_inex = rnd == GMP_RNDN || rnd == GMP_RNDU ?
+        (mpfr_nextabove (z0), 1) : -1;
+      sprintf (sy, "%lu", (unsigned long) n);
+
+      mpfr_clear_flags ();
+      inex = mpfr_pow (z, x, y, (mp_rnd_t) rnd);
+      cmpres (0, x, sy, (mp_rnd_t) rnd, z0, expected_inex, z, inex, ufinex,
+              "underflow_up2", "mpfr_pow");
+      test_others (NULL, sy, (mp_rnd_t) rnd, x, y, z, inex, ufinex,
+                   "underflow_up2");
+    }
+
+  mpfr_clears (x, y, z, z0, eps, (mpfr_ptr) 0);
+}
+
+static void
+underflow_up3 (void)
+{
+  mpfr_t x, y, z, z0;
+  int inex;
+  int rnd;
+  int i;
+
+  mpfr_init2 (x, 64);
+  mpfr_init2 (y, sizeof (mp_exp_t) * CHAR_BIT);
+  mpfr_init2 (z, 32);
+  mpfr_init2 (z0, 2);
+
+  inex = mpfr_set_exp_t (y, mpfr_get_emin () - 2, GMP_RNDN);
+  MPFR_ASSERTN (inex == 0);
+  for (i = -1; i <= 1; i++)
+    RND_LOOP (rnd)
+      {
+        unsigned int ufinex = MPFR_FLAGS_UNDERFLOW | MPFR_FLAGS_INEXACT;
+        int expected_inex;
+
+        mpfr_set_ui (x, 2, GMP_RNDN);
+        if (i < 0)
+          mpfr_nextbelow (x);
+        if (i > 0)
+          mpfr_nextabove (x);
+        /* x = 2 + i * eps, y = emin - 2, x^y ~= 2^(emin - 2) */
+
+        expected_inex = rnd == GMP_RNDU || (rnd == GMP_RNDN && i < 0) ?
+          1 : -1;
+
+        mpfr_set_ui (z0, 0, GMP_RNDN);
+        if (expected_inex > 0)
+          mpfr_nextabove (z0);
+
+        mpfr_clear_flags ();
+        inex = mpfr_pow (z, x, y, (mp_rnd_t) rnd);
+        cmpres (0, x, "emin - 2", (mp_rnd_t) rnd, z0, expected_inex, z, inex,
+                ufinex, "underflow_up3", "mpfr_pow");
+        test_others (NULL, "emin - 2", (mp_rnd_t) rnd, x, y, z, inex, ufinex,
+                     "underflow_up3");
+      }
+
+  mpfr_clears (x, y, z, z0, (mpfr_ptr) 0);
+}
+
+static void
+underflow_up (void)
+{
+  underflow_up1 ();
+  underflow_up2 ();
+  underflow_up3 ();
+}
+
+static void
+overflow_inv (void)
+{
+  mpfr_t x, y, z;
+  int precx;
+  int s, t;
+  int inex;
+  int rnd;
+
+  mpfr_init2 (y, 2);
+  mpfr_init2 (z, 8);
+
+  mpfr_set_si (y, -1, GMP_RNDN);
+  for (precx = 10; precx <= 100; precx += 90)
+    {
+      const char *sp = precx == 10 ?
+        "overflow_inv (precx = 10)" : "overflow_inv (precx = 100)";
+
+      mpfr_init2 (x, precx);
+      for (s = -1; s <= 1; s += 2)
+        {
+          inex = mpfr_set_si_2exp (x, s, - mpfr_get_emax (), GMP_RNDN);
+          MPFR_ASSERTN (inex == 0);
+          for (t = 0; t <= 5; t++)
+            {
+              /* If precx = 10:
+               * x = s * 2^(-emax) * (1 + t * 2^(-9)), so that
+               * 1/x = s * 2^emax * (1 - t * 2^(-9) + eps) with eps > 0.
+               * Values of (1/x) / 2^emax and overflow condition for x > 0:
+               * t = 0: 1                           o: always
+               * t = 1: 0.11111111 100000000011...  o: GMP_RNDN and GMP_RNDU
+               * t = 2: 0.11111111 000000001111...  o: GMP_RNDU
+               * t = 3: 0.11111110 100000100011...  o: never
+               *
+               * If precx = 100:
+               * t = 0: always overflow
+               * t > 0: overflow for GMP_RNDN and GMP_RNDU.
+               */
+              RND_LOOP (rnd)
+                {
+                  int inf, overflow;
+
+                  overflow = t == 0 ||
+                    ((mp_rnd_t) rnd == GMP_RNDN && (precx > 10 || t == 1)) ||
+                    ((mp_rnd_t) rnd == (s < 0 ? GMP_RNDD : GMP_RNDU) &&
+                     (precx > 10 || t <= 2));
+                  inf = overflow &&
+                    ((mp_rnd_t) rnd == GMP_RNDN ||
+                     (mp_rnd_t) rnd == (s < 0 ? GMP_RNDD : GMP_RNDU));
+                  mpfr_clear_flags ();
+                  inex = mpfr_pow (z, x, y, (mp_rnd_t) rnd);
+                  if (overflow ^ !! mpfr_overflow_p ())
+                    {
+                      printf ("Bad overflow flag in %s\nfor mpfr_pow%s\n"
+                              "s = %d, t = %d, %s\n", sp,
+                              ext ? ", extended exponent range" : "",
+                              s, t, mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                      exit (1);
+                    }
+                  if (overflow && (inf ^ !! MPFR_IS_INF (z)))
+                    {
+                      printf ("Bad value in %s\nfor mpfr_pow%s\n"
+                              "s = %d, t = %d, %s\nGot ", sp,
+                              ext ? ", extended exponent range" : "",
+                              s, t, mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                      mpfr_out_str (stdout, 16, 0, z, GMP_RNDN);
+                      printf (" instead of %s value.\n",
+                              inf ? "infinite" : "finite");
+                      exit (1);
+                    }
+                  test_others (NULL, "-1", (mp_rnd_t) rnd, x, y, z,
+                               inex, __gmpfr_flags, sp);
+                }  /* RND_LOOP */
+              mpfr_nexttoinf (x);
+            }  /* for (t = ...) */
+        }  /* for (s = ...) */
+      mpfr_clear (x);
+    }  /* for (precx = ...) */
+
+  mpfr_clears (y, z, (mpfr_ptr) 0);
+}
+
+static void
+alltst (void)
+{
+  mp_exp_t emin, emax;
+
+  ext = 0;
+  tst ();
+  underflow_up ();
+  overflow_inv ();
+
+  emin = mpfr_get_emin ();
+  emax = mpfr_get_emax ();
+  set_emin (MPFR_EMIN_MIN);
+  set_emax (MPFR_EMAX_MAX);
+  if (mpfr_get_emin () != emin || mpfr_get_emax () != emax)
+    {
+      ext = 1;
+      tst ();
+      underflow_up ();
+      overflow_inv ();
+      set_emin (emin);
+      set_emax (emax);
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  tests_start_mpfr ();
+  all_cmpres_errors = argc > 1;
+  alltst ();
+  tests_end_mpfr ();
+  return all_cmpres_errors < 0;
+}