]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - mpfr/tests/texp.c
Imported gcc-4.4.3
[msp430-gcc.git] / mpfr / tests / texp.c
diff --git a/mpfr/tests/texp.c b/mpfr/tests/texp.c
new file mode 100644 (file)
index 0000000..47d2c93
--- /dev/null
@@ -0,0 +1,1016 @@
+/* Test file for mpfr_exp.
+
+Copyright 1999, 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. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "mpfr-test.h"
+
+#ifdef CHECK_EXTERNAL
+static int
+test_exp (mpfr_ptr a, mpfr_srcptr b, mp_rnd_t rnd_mode)
+{
+  int res;
+  int ok = rnd_mode == GMP_RNDN && mpfr_number_p (b) && mpfr_get_prec (a)>=53;
+  if (ok)
+    {
+      mpfr_print_raw (b);
+    }
+  res = mpfr_exp (a, b, rnd_mode);
+  if (ok)
+    {
+      printf (" ");
+      mpfr_print_raw (a);
+      printf ("\n");
+    }
+  return res;
+}
+#else
+#define test_exp mpfr_exp
+#endif
+
+/* returns the number of ulp of error */
+static void
+check3 (const char *op, mp_rnd_t rnd, const char *res)
+{
+  mpfr_t x, y;
+
+  mpfr_inits2 (53, x, y, (mpfr_ptr) 0);
+  /* y negative. If we forget to set the sign in mpfr_exp, we'll see it. */
+  mpfr_set_si (y, -1, GMP_RNDN);
+  mpfr_set_str1 (x, op);
+  test_exp (y, x, rnd);
+  if (mpfr_cmp_str1 (y, res) )
+    {
+      printf ("mpfr_exp failed for x=%s, rnd=%s\n",
+              op, mpfr_print_rnd_mode (rnd));
+      printf ("expected result is %s, got ", res);
+      mpfr_out_str (stdout, 10, 0, y, GMP_RNDN);
+      putchar('\n');
+      exit (1);
+    }
+  mpfr_clears (x, y, (mpfr_ptr) 0);
+}
+
+/* expx is the value of exp(X) rounded towards -infinity */
+static void
+check_worst_case (const char *Xs, const char *expxs)
+{
+  mpfr_t x, y;
+
+  mpfr_inits2 (53, x, y, (mpfr_ptr) 0);
+  mpfr_set_str1(x, Xs);
+  test_exp(y, x, GMP_RNDD);
+  if (mpfr_cmp_str1 (y, expxs))
+    {
+      printf ("exp(x) rounded towards -infinity is wrong\n");
+      exit(1);
+    }
+  mpfr_set_str1(x, Xs);
+  test_exp(x, x, GMP_RNDU);
+  mpfr_nexttoinf (y);
+  if (mpfr_cmp(x,y))
+    {
+      printf ("exp(x) rounded towards +infinity is wrong\n");
+      exit(1);
+    }
+  mpfr_clears (x, y, (mpfr_ptr) 0);
+}
+
+/* worst cases communicated by Jean-Michel Muller and Vincent Lefevre */
+static int
+check_worst_cases (void)
+{
+  mpfr_t x; mpfr_t y;
+
+  mpfr_init(x);
+  mpfr_set_prec (x, 53);
+
+  check_worst_case("4.44089209850062517562e-16", "1.00000000000000022204");
+  check_worst_case("6.39488462184069720009e-14", "1.00000000000006372680");
+  check_worst_case("1.84741111297455401935e-12", "1.00000000000184718907");
+  check_worst_case("1.76177628026265550074e-10", "1.00000000017617751702");
+  check3("1.76177628026265550074e-10", GMP_RNDN, "1.00000000017617773906");
+  check_worst_case("7.54175277499595900852e-10", "1.00000000075417516676");
+  check3("7.54175277499595900852e-10", GMP_RNDN, "1.00000000075417538881");
+  /* bug found by Vincent Lefe`vre on December 8, 1999 */
+  check3("-5.42410311287441459172e+02", GMP_RNDN, "2.7176584868845723e-236");
+  /* further cases communicated by Vincent Lefe`vre on January 27, 2000 */
+  check3("-1.32920285897904911589e-10", GMP_RNDN, "0.999999999867079769622");
+  check3("-1.44037948245738330735e-10", GMP_RNDN, "0.9999999998559621072757");
+  check3("-1.66795910430705305937e-10", GMP_RNDZ, "0.9999999998332040895832");
+  check3("-1.64310953745426656203e-10", GMP_RNDN, "0.9999999998356891017792");
+  check3("-1.38323574826034659172e-10", GMP_RNDZ, "0.9999999998616764251835");
+  check3("-1.23621668465115401498e-10", GMP_RNDZ, "0.9999999998763783315425");
+
+  mpfr_set_prec (x, 601);
+  mpfr_set_str (x, "0.88b6ba510e10450edc258748bc9dfdd466f21b47ed264cdf24aa8f64af1f3fad9ec2301d43c0743f534b5aa20091ff6d352df458ef1ba519811ef6f5b11853534fd8fa32764a0a6d2d0dd20@0", 16, GMP_RNDZ);
+  mpfr_init2 (y, 601);
+  mpfr_exp_2 (y, x, GMP_RNDD);
+  mpfr_exp_3 (x, x, GMP_RNDD);
+  if (mpfr_cmp (x, y))
+    {
+      printf ("mpfr_exp_2 and mpfr_exp_3 differ for prec=601\n");
+      printf ("mpfr_exp_2 gives ");
+      mpfr_out_str (stdout, 2, 0, y, GMP_RNDN);
+      printf ("\nmpfr_exp_3 gives ");
+      mpfr_out_str (stdout, 2, 0, x, GMP_RNDN);
+      printf ("\n");
+      exit (1);
+    }
+
+  mpfr_set_prec (x, 13001);
+  mpfr_set_prec (y, 13001);
+  mpfr_urandomb (x, RANDS);
+  mpfr_exp_3 (y, x, GMP_RNDN);
+  mpfr_exp_2 (x, x, GMP_RNDN);
+  if (mpfr_cmp (x, y))
+    {
+      printf ("mpfr_exp_2 and mpfr_exp_3 differ for prec=13001\n");
+      exit (1);
+    }
+
+  mpfr_clear (x);
+  mpfr_clear (y);
+  return 0;
+}
+
+static void
+compare_exp2_exp3 (mp_prec_t p0, mp_prec_t p1)
+{
+  mpfr_t x, y, z;
+  mp_prec_t prec;
+  mp_rnd_t rnd;
+
+  mpfr_init (x);
+  mpfr_init (y);
+  mpfr_init (z);
+  for (prec = p0; prec <= p1; prec ++)
+    {
+      mpfr_set_prec (x, prec);
+      mpfr_set_prec (y, prec);
+      mpfr_set_prec (z, prec);
+      mpfr_urandomb (x, RANDS);
+      rnd = RND_RAND ();
+      mpfr_exp_2 (y, x, rnd);
+      mpfr_exp_3 (z, x, rnd);
+      if (mpfr_cmp (y,z))
+        {
+          printf ("mpfr_exp_2 and mpfr_exp_3 disagree for rnd=%s and\nx=",
+                  mpfr_print_rnd_mode (rnd));
+          mpfr_print_binary (x);
+          puts ("");
+          printf ("mpfr_exp_2 gives ");
+          mpfr_print_binary (y);
+          puts ("");
+          printf ("mpfr_exp_3 gives ");
+          mpfr_print_binary (z);
+          puts ("");
+          exit (1);
+        }
+  }
+
+  mpfr_clear (x);
+  mpfr_clear (y);
+  mpfr_clear (z);
+}
+
+static void
+check_large (void)
+{
+  mpfr_t x, z;
+  mp_prec_t prec;
+
+  /* bug found by Patrick Pe'lissier on 7 Jun 2004 */
+  prec = 203780;
+  mpfr_init2 (x, prec);
+  mpfr_init2 (z, prec);
+  mpfr_set_ui (x, 3, GMP_RNDN);
+  mpfr_sqrt (x, x, GMP_RNDN);
+  mpfr_sub_ui (x, x, 1, GMP_RNDN);
+  mpfr_exp_3 (z, x, GMP_RNDN);
+  mpfr_clear (x);
+  mpfr_clear (z);
+}
+
+#define TEST_FUNCTION test_exp
+#define TEST_RANDOM_EMIN -36
+#define TEST_RANDOM_EMAX 36
+#include "tgeneric.c"
+
+static void
+check_special (void)
+{
+  mpfr_t x, y, z;
+  mp_exp_t emin, emax;
+
+  emin = mpfr_get_emin ();
+  emax = mpfr_get_emax ();
+
+  mpfr_init (x);
+  mpfr_init (y);
+  mpfr_init (z);
+
+  /* check exp(NaN) = NaN */
+  mpfr_set_nan (x);
+  test_exp (y, x, GMP_RNDN);
+  if (!mpfr_nan_p (y))
+    {
+      printf ("Error for exp(NaN)\n");
+      exit (1);
+    }
+
+  /* check exp(+inf) = +inf */
+  mpfr_set_inf (x, 1);
+  test_exp (y, x, GMP_RNDN);
+  if (!mpfr_inf_p (y) || mpfr_sgn (y) < 0)
+    {
+      printf ("Error for exp(+inf)\n");
+      exit (1);
+    }
+
+  /* check exp(-inf) = +0 */
+  mpfr_set_inf (x, -1);
+  test_exp (y, x, GMP_RNDN);
+  if (mpfr_cmp_ui (y, 0) || mpfr_sgn (y) < 0)
+    {
+      printf ("Error for exp(-inf)\n");
+      exit (1);
+    }
+
+  /* Check overflow. Corner case of mpfr_exp_2 */
+  mpfr_set_prec (x, 64);
+  mpfr_set_emax (MPFR_EMAX_DEFAULT);
+  mpfr_set_emin (MPFR_EMIN_DEFAULT);
+  mpfr_set_str (x,
+    "0.1011000101110010000101111111010100001100000001110001100111001101E30",
+                2, GMP_RNDN);
+  mpfr_exp (x, x, GMP_RNDD);
+  if (mpfr_cmp_str (x,
+".1111111111111111111111111111111111111111111111111111111111111111E1073741823",
+                    2, GMP_RNDN) != 0)
+    {
+      printf ("Wrong overflow detection in mpfr_exp\n");
+      mpfr_dump (x);
+      exit (1);
+    }
+  /* Check underflow. Corner case of mpfr_exp_2 */
+  mpfr_set_str (x,
+"-0.1011000101110010000101111111011111010001110011110111100110101100E30",
+                2, GMP_RNDN);
+  mpfr_exp (x, x, GMP_RNDN);
+  if (mpfr_cmp_str (x, "0.1E-1073741823", 2, GMP_RNDN) != 0)
+    {
+      printf ("Wrong underflow (1) detection in mpfr_exp\n");
+      mpfr_dump (x);
+      exit (1);
+    }
+  mpfr_set_str (x,
+"-0.1011001101110010000101111111011111010001110011110111100110111101E30",
+                2, GMP_RNDN);
+  mpfr_exp (x, x, GMP_RNDN);
+  if (mpfr_cmp_ui (x, 0) != 0)
+    {
+      printf ("Wrong underflow (2) detection in mpfr_exp\n");
+      mpfr_dump (x);
+      exit (1);
+    }
+  /* Check overflow. Corner case of mpfr_exp_3 */
+  if (MPFR_PREC_MAX > MPFR_EXP_THRESHOLD + 10)
+    {
+      mpfr_set_prec (x, MPFR_EXP_THRESHOLD + 10);
+      mpfr_set_str (x,
+       "0.1011000101110010000101111111010100001100000001110001100111001101E30",
+                    2, GMP_RNDN);
+      mpfr_clear_overflow ();
+      mpfr_exp (x, x, GMP_RNDD);
+      if (!mpfr_overflow_p ())
+        {
+          printf ("Wrong overflow detection in mpfr_exp_3\n");
+          mpfr_dump (x);
+          exit (1);
+        }
+      /* Check underflow. Corner case of mpfr_exp_3 */
+      mpfr_set_str (x,
+      "-0.1011000101110010000101111111011111010001110011110111100110101100E30",
+                    2, GMP_RNDN);
+      mpfr_clear_underflow ();
+      mpfr_exp (x, x, GMP_RNDN);
+      if (!mpfr_underflow_p ())
+        {
+          printf ("Wrong underflow detection in mpfr_exp_3\n");
+          mpfr_dump (x);
+          exit (1);
+        }
+      mpfr_set_prec (x, 53);
+    }
+
+  /* check overflow */
+  set_emax (10);
+  mpfr_set_ui (x, 7, GMP_RNDN);
+  test_exp (y, x, GMP_RNDN);
+  if (!mpfr_inf_p (y) || mpfr_sgn (y) < 0)
+    {
+      printf ("Error for exp(7) for emax=10\n");
+      exit (1);
+    }
+  set_emax (emax);
+
+  /* check underflow */
+  set_emin (-10);
+  mpfr_set_si (x, -9, GMP_RNDN);
+  test_exp (y, x, GMP_RNDN);
+  if (mpfr_cmp_ui (y, 0) || mpfr_sgn (y) < 0)
+    {
+      printf ("Error for exp(-9) for emin=-10\n");
+      printf ("Expected +0\n");
+      printf ("Got      "); mpfr_print_binary (y); puts ("");
+      exit (1);
+    }
+  set_emin (emin);
+
+  /* check case EXP(x) < -precy */
+  mpfr_set_prec (y, 2);
+  mpfr_set_str_binary (x, "-0.1E-3");
+  test_exp (y, x, GMP_RNDD);
+  if (mpfr_cmp_ui_2exp (y, 3, -2))
+    {
+      printf ("Error for exp(-1/16), prec=2, RNDD\n");
+      printf ("expected 0.11, got ");
+      mpfr_dump (y);
+      exit (1);
+    }
+  test_exp (y, x, GMP_RNDZ);
+  if (mpfr_cmp_ui_2exp (y, 3, -2))
+    {
+      printf ("Error for exp(-1/16), prec=2, RNDZ\n");
+      printf ("expected 0.11, got ");
+      mpfr_dump (y);
+      exit (1);
+    }
+  mpfr_set_str_binary (x, "0.1E-3");
+  test_exp (y, x, GMP_RNDN);
+  if (mpfr_cmp_ui (y, 1))
+    {
+      printf ("Error for exp(1/16), prec=2, RNDN\n");
+      exit (1);
+    }
+  test_exp (y, x, GMP_RNDU);
+  if (mpfr_cmp_ui_2exp (y, 3, -1))
+    {
+      printf ("Error for exp(1/16), prec=2, RNDU\n");
+      exit (1);
+    }
+
+  /* bug reported by Franky Backeljauw, 28 Mar 2003 */
+  mpfr_set_prec (x, 53);
+  mpfr_set_prec (y, 53);
+  mpfr_set_str_binary (x, "1.1101011000111101011110000111010010101001101001110111e28");
+  test_exp (y, x, GMP_RNDN);
+
+  mpfr_set_prec (x, 153);
+  mpfr_set_prec (z, 153);
+  mpfr_set_str_binary (x, "1.1101011000111101011110000111010010101001101001110111e28");
+  test_exp (z, x, GMP_RNDN);
+  mpfr_prec_round (z, 53, GMP_RNDN);
+
+  if (mpfr_cmp (y, z))
+    {
+      printf ("Error in mpfr_exp for large argument\n");
+      exit (1);
+    }
+
+  /* corner cases in mpfr_exp_3 */
+  mpfr_set_prec (x, 2);
+  mpfr_set_ui (x, 1, GMP_RNDN);
+  mpfr_set_prec (y, 2);
+  mpfr_exp_3 (y, x, GMP_RNDN);
+
+  /* Check some little things about overflow detection */
+  set_emin (-125);
+  set_emax (128);
+  mpfr_set_prec (x, 107);
+  mpfr_set_prec (y, 107);
+  mpfr_set_str_binary (x, "0.11110000000000000000000000000000000000000000000"
+                       "0000000000000000000000000000000000000000000000000000"
+                       "00000000E4");
+  test_exp (y, x, GMP_RNDN);
+  if (mpfr_cmp_str (y, "0.11000111100001100110010101111101011010010101010000"
+                    "1101110111100010111001011111111000110111001011001101010"
+                    "01E22", 2, GMP_RNDN))
+    {
+      printf ("Special overflow error (1)\n");
+      mpfr_dump (y);
+      exit (1);
+    }
+
+  set_emin (emin);
+  set_emax (emax);
+
+  /* Check for overflow producing a segfault with HUGE exponent */
+  mpfr_set_ui  (x, 3, GMP_RNDN);
+  mpfr_mul_2ui (x, x, 32, GMP_RNDN);
+  test_exp (y, x, GMP_RNDN); /* Can't test return value: May overflow or not*/
+
+  /* Bug due to wrong approximation of (x)/log2 */
+  mpfr_set_prec (x, 163);
+
+  mpfr_set_str (x, "-4.28ac8fceeadcda06bb56359017b1c81b85b392e7", 16,
+                GMP_RNDN);
+  mpfr_exp (x, x, GMP_RNDN);
+  if (mpfr_cmp_str (x, "3.fffffffffffffffffffffffffffffffffffffffe8@-2",
+                    16, GMP_RNDN))
+    {
+      printf ("Error for x= -4.28ac8fceeadcda06bb56359017b1c81b85b392e7");
+      printf ("expected  3.fffffffffffffffffffffffffffffffffffffffe8@-2");
+      printf ("Got       ");
+      mpfr_out_str (stdout, 16, 0, x, GMP_RNDN); putchar ('\n');
+    }
+
+  /* bug found by Guillaume Melquiond, 13 Sep 2005 */
+  mpfr_set_prec (x, 53);
+  mpfr_set_str_binary (x, "-1E-400");
+  mpfr_exp (x, x, GMP_RNDZ);
+  if (mpfr_cmp_ui (x, 1) == 0)
+    {
+      printf ("Error for exp(-2^(-400))\n");
+      exit (1);
+    }
+
+  mpfr_clear (x);
+  mpfr_clear (y);
+  mpfr_clear (z);
+}
+
+/* check sign of inexact flag */
+static void
+check_inexact (void)
+{
+  mpfr_t x, y;
+  int inexact;
+
+  mpfr_init2 (x, 53);
+  mpfr_init2 (y, 53);
+
+  mpfr_set_str_binary (x,
+        "1.0000000000001001000110100100101000001101101011100101e2");
+  inexact = test_exp (y, x, GMP_RNDN);
+  if (inexact <= 0)
+    {
+      printf ("Wrong inexact flag (Got %d instead of 1)\n", inexact);
+      exit (1);
+    }
+
+  mpfr_clear (x);
+  mpfr_clear (y);
+}
+
+static void
+check_exp10(void)
+{
+  mpfr_t x;
+  int inexact;
+
+  mpfr_init2 (x, 200);
+  mpfr_set_ui(x, 4, GMP_RNDN);
+
+  inexact = mpfr_exp10 (x, x, GMP_RNDN);
+  if (mpfr_cmp_ui(x, 10*10*10*10))
+    {
+      printf ("exp10: Wrong returned value\n");
+      exit (1);
+    }
+  if (inexact != 0)
+    {
+      printf ("exp10: Wrong inexact flag\n");
+      exit (1);
+    }
+
+  mpfr_clear (x);
+}
+
+static void
+overflowed_exp0 (void)
+{
+  mpfr_t x, y;
+  int emax, i, inex, rnd, err = 0;
+  mp_exp_t old_emax;
+
+  old_emax = mpfr_get_emax ();
+
+  mpfr_init2 (x, 8);
+  mpfr_init2 (y, 8);
+
+  for (emax = -1; emax <= 0; emax++)
+    {
+      mpfr_set_ui_2exp (y, 1, emax, GMP_RNDN);
+      mpfr_nextbelow (y);
+      set_emax (emax);  /* 1 is not representable. */
+      /* and if emax < 0, 1 - eps is not representable either. */
+      for (i = -1; i <= 1; i++)
+        RND_LOOP (rnd)
+        {
+          mpfr_set_si_2exp (x, i, -512 * ABS (i), GMP_RNDN);
+          mpfr_clear_flags ();
+          inex = mpfr_exp (x, x, (mp_rnd_t) rnd);
+          if ((i >= 0 || emax < 0 || rnd == GMP_RNDN || rnd == GMP_RNDU) &&
+              ! mpfr_overflow_p ())
+            {
+              printf ("Error in overflowed_exp0 (i = %d, rnd = %s):\n"
+                      "  The overflow flag is not set.\n",
+                      i, mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+              err = 1;
+            }
+          if (rnd == GMP_RNDZ || rnd == GMP_RNDD)
+            {
+              if (inex >= 0)
+                {
+                  printf ("Error in overflowed_exp0 (i = %d, rnd = %s):\n"
+                          "  The inexact value must be negative.\n",
+                          i, mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                  err = 1;
+                }
+              if (! mpfr_equal_p (x, y))
+                {
+                  printf ("Error in overflowed_exp0 (i = %d, rnd = %s):\n"
+                          "  Got ", i, mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                  mpfr_print_binary (x);
+                  printf (" instead of 0.11111111E%d.\n", emax);
+                  err = 1;
+                }
+            }
+          else
+            {
+              if (inex <= 0)
+                {
+                  printf ("Error in overflowed_exp0 (i = %d, rnd = %s):\n"
+                          "  The inexact value must be positive.\n",
+                          i, mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                  err = 1;
+                }
+              if (! (mpfr_inf_p (x) && MPFR_SIGN (x) > 0))
+                {
+                  printf ("Error in overflowed_exp0 (i = %d, rnd = %s):\n"
+                          "  Got ", i, mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                  mpfr_print_binary (x);
+                  printf (" instead of +Inf.\n");
+                  err = 1;
+                }
+            }
+        }
+      set_emax (old_emax);
+    }
+
+  if (err)
+    exit (1);
+  mpfr_clear (x);
+  mpfr_clear (y);
+}
+
+/* This bug occurs in mpfr_exp_2 on a Linux-64 machine, r5475. */
+static void
+bug20080731 (void)
+{
+  mp_exp_t emin;
+  mpfr_t x, y1, y2;
+  mp_prec_t prec = 64;
+
+  emin = mpfr_get_emin ();
+  set_emin (MPFR_EMIN_MIN);
+
+  mpfr_init2 (x, 200);
+  mpfr_set_str (x, "-2.c5c85fdf473de6af278ece700fcbdabd03cd0cb9ca62d8b62c@7",
+                16, GMP_RNDN);
+
+  mpfr_init2 (y1, prec);
+  mpfr_exp (y1, x, GMP_RNDU);
+
+  /* Compute the result with a higher internal precision. */
+  mpfr_init2 (y2, 300);
+  mpfr_exp (y2, x, GMP_RNDU);
+  mpfr_prec_round (y2, prec, GMP_RNDU);
+
+  if (mpfr_cmp0 (y1, y2) != 0)
+    {
+      printf ("Error in bug20080731\nExpected ");
+      mpfr_out_str (stdout, 16, 0, y2, GMP_RNDN);
+      printf ("\nGot      ");
+      mpfr_out_str (stdout, 16, 0, y1, GMP_RNDN);
+      printf ("\n");
+      exit (1);
+    }
+
+  mpfr_clears (x, y1, y2, (mpfr_ptr) 0);
+  set_emin (emin);
+}
+
+/* Emulate mpfr_exp with mpfr_exp_3 in the general case. */
+static int
+exp_3 (mpfr_ptr y, mpfr_srcptr x, mp_rnd_t rnd_mode)
+{
+  int inexact;
+
+  inexact = mpfr_exp_3 (y, x, rnd_mode);
+  return mpfr_check_range (y, inexact, rnd_mode);
+}
+
+static void
+underflow_up (int extended_emin)
+{
+  mpfr_t minpos, x, y, t, t2;
+  int precx, precy;
+  int inex;
+  int rnd;
+  int e3;
+  int i, j;
+
+  mpfr_init2 (minpos, 2);
+  mpfr_set_ui (minpos, 0, GMP_RNDN);
+  mpfr_nextabove (minpos);
+
+  /* Let's test values near the underflow boundary.
+   *
+   * Minimum representable positive number: minpos = 2^(emin - 1).
+   * Let's choose an MPFR number x = log(minpos) + eps, with |eps| small
+   * (note: eps cannot be 0, and cannot be a rational number either).
+   * Then exp(x) = minpos * exp(eps) ~= minpos * (1 + eps + eps^2).
+   * We will compute y = rnd(exp(x)) in some rounding mode, precision p.
+   *   1. If eps > 0, then in any rounding mode:
+   *        rnd(exp(x)) >= minpos and no underflow.
+   *      So, let's take x1 = rndu(log(minpos)) in some precision.
+   *   2. If eps < 0, then exp(x) < minpos and the result will be either 0
+   *      or minpos. An underflow always occurs in GMP_RNDZ and GMP_RNDD,
+   *      but not necessarily in GMP_RNDN and GMP_RNDU (this is underflow
+   *      after rounding in an unbounded exponent range). If -a < eps < -b,
+   *        minpos * (1 - a) < exp(x) < minpos * (1 - b + b^2).
+   *      - If eps > -2^(-p), no underflow in GMP_RNDU.
+   *      - If eps > -2^(-p-1), no underflow in GMP_RNDN.
+   *      - If eps < - (2^(-p-1) + 2^(-2p-1)), underflow in GMP_RNDN.
+   *      - If eps < - (2^(-p) + 2^(-2p+1)), underflow in GMP_RNDU.
+   *      - In GMP_RNDN, result is minpos iff exp(eps) > 1/2, i.e.
+   *        - log(2) < eps < ...
+   *
+   * Moreover, since precy < MPFR_EXP_THRESHOLD (to avoid tests that take
+   * too much time), mpfr_exp() always selects mpfr_exp_2(); so, we need
+   * to test mpfr_exp_3() too. This will be done via the e3 variable:
+   *   e3 = 0: mpfr_exp(), thus mpfr_exp_2().
+   *   e3 = 1: mpfr_exp_3(), via the exp_3() wrapper.
+   * i.e.: inex = e3 ? exp_3 (y, x, rnd) : mpfr_exp (y, x, rnd);
+   */
+
+  /* Case eps > 0. In revision 5461 (trunk) on a 64-bit Linux machine:
+   *   Incorrect flags in underflow_up, eps > 0, GMP_RNDN and extended emin
+   *   for precx = 96, precy = 16, mpfr_exp_3
+   *   Got 9 instead of 8.
+   * Note: testing this case in several precisions for x and y introduces
+   * some useful random. Indeed, the bug is not always triggered.
+   * Fixed in r5469.
+   */
+  for (precx = 16; precx <= 128; precx += 16)
+    {
+      mpfr_init2 (x, precx);
+      mpfr_log (x, minpos, GMP_RNDU);
+      for (precy = 16; precy <= 128; precy += 16)
+        {
+          mpfr_init2 (y, precy);
+
+          for (e3 = 0; e3 <= 1; e3++)
+            {
+              RND_LOOP (rnd)
+                {
+                  int err = 0;
+
+                  mpfr_clear_flags ();
+                  inex = e3 ? exp_3 (y, x, (mpfr_rnd_t) rnd)
+                    : mpfr_exp (y, x, (mpfr_rnd_t) rnd);
+                  if (__gmpfr_flags != MPFR_FLAGS_INEXACT)
+                    {
+                      printf ("Incorrect flags in underflow_up, eps > 0, %s",
+                              mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                      if (extended_emin)
+                        printf (" and extended emin");
+                      printf ("\nfor precx = %d, precy = %d, %s\n",
+                              precx, precy, e3 ? "mpfr_exp_3" : "mpfr_exp");
+                      printf ("Got %u instead of %u.\n", __gmpfr_flags,
+                              (unsigned int) MPFR_FLAGS_INEXACT);
+                      err = 1;
+                    }
+                  if (mpfr_cmp0 (y, minpos) < 0)
+                    {
+                      printf ("Incorrect result in underflow_up, eps > 0, %s",
+                              mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                      if (extended_emin)
+                        printf (" and extended emin");
+                      printf ("\nfor precx = %d, precy = %d, %s\n",
+                              precx, precy, e3 ? "mpfr_exp_3" : "mpfr_exp");
+                      mpfr_dump (y);
+                      err = 1;
+                    }
+                  MPFR_ASSERTN (inex != 0);
+                  if (rnd == GMP_RNDD || rnd == GMP_RNDZ)
+                    MPFR_ASSERTN (inex < 0);
+                  if (rnd == GMP_RNDU)
+                    MPFR_ASSERTN (inex > 0);
+                  if (err)
+                    exit (1);
+                }
+            }
+
+          mpfr_clear (y);
+        }
+      mpfr_clear (x);
+    }
+
+  /* Case - log(2) < eps < 0 in GMP_RNDN, starting with small-precision x;
+   * only check the result and the ternary value.
+   * Previous to r5453 (trunk), on 32-bit and 64-bit machines, this fails
+   * for precx = 65 and precy = 16, e.g.:
+   *   exp_2.c:264:  assertion failed: ...
+   * because mpfr_sub (r, x, r, GMP_RNDU); yields a null value. This is
+   * fixed in r5453 by going to next Ziv's iteration.
+   */
+  for (precx = sizeof(mp_exp_t) * CHAR_BIT + 1; precx <= 81; precx += 8)
+    {
+      mpfr_init2 (x, precx);
+      mpfr_log (x, minpos, GMP_RNDD);  /* |ulp| <= 1/2 */
+      for (precy = 16; precy <= 128; precy += 16)
+        {
+          mpfr_init2 (y, precy);
+          inex = mpfr_exp (y, x, GMP_RNDN);
+          if (inex <= 0 || mpfr_cmp0 (y, minpos) != 0)
+            {
+              printf ("Error in underflow_up, - log(2) < eps < 0");
+              if (extended_emin)
+                printf (" and extended emin");
+              printf (" for prec = %d\nExpected ", precy);
+              mpfr_out_str (stdout, 16, 0, minpos, GMP_RNDN);
+              printf (" (minimum positive MPFR number) and inex > 0\nGot ");
+              mpfr_out_str (stdout, 16, 0, y, GMP_RNDN);
+              printf ("\nwith inex = %d\n", inex);
+              exit (1);
+            }
+          mpfr_clear (y);
+        }
+      mpfr_clear (x);
+    }
+
+  /* Cases eps ~ -2^(-p) and eps ~ -2^(-p-1). More precisely,
+   *   _ for j = 0, eps > -2^(-(p+i)),
+   *   _ for j = 1, eps < - (2^(-(p+i)) + 2^(1-2(p+i))),
+   * where i = 0 or 1.
+   */
+  mpfr_inits2 (2, t, t2, (mpfr_ptr) 0);
+  for (precy = 16; precy <= 128; precy += 16)
+    {
+      mpfr_set_ui_2exp (t, 1, - precy, GMP_RNDN);         /* 2^(-p) */
+      mpfr_set_ui_2exp (t2, 1, 1 - 2 * precy, GMP_RNDN);  /* 2^(-2p+1) */
+      precx = sizeof(mp_exp_t) * CHAR_BIT + 2 * precy + 8;
+      mpfr_init2 (x, precx);
+      mpfr_init2 (y, precy);
+      for (i = 0; i <= 1; i++)
+        {
+          for (j = 0; j <= 1; j++)
+            {
+              if (j == 0)
+                {
+                  /* Case eps > -2^(-(p+i)). */
+                  mpfr_log (x, minpos, GMP_RNDU);
+                }
+              else  /* j == 1 */
+                {
+                  /* Case eps < - (2^(-(p+i)) + 2^(1-2(p+i))). */
+                  mpfr_log (x, minpos, GMP_RNDD);
+                  inex = mpfr_sub (x, x, t2, GMP_RNDN);
+                  MPFR_ASSERTN (inex == 0);
+                }
+              inex = mpfr_sub (x, x, t, GMP_RNDN);
+              MPFR_ASSERTN (inex == 0);
+
+              RND_LOOP (rnd)
+                for (e3 = 0; e3 <= 1; e3++)
+                  {
+                    int err = 0;
+                    unsigned int flags;
+
+                    flags = MPFR_FLAGS_INEXACT |
+                      ((rnd == GMP_RNDU && (i == 1 || j == 0)) ||
+                       (rnd == GMP_RNDN && (i == 1 && j == 0)) ?
+                       0 : MPFR_FLAGS_UNDERFLOW);
+                    mpfr_clear_flags ();
+                    inex = e3 ? exp_3 (y, x, (mpfr_rnd_t) rnd)
+                      : mpfr_exp (y, x, (mpfr_rnd_t) rnd);
+                    if (__gmpfr_flags != flags)
+                      {
+                        printf ("Incorrect flags in underflow_up, %s",
+                                mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                        if (extended_emin)
+                          printf (" and extended emin");
+                        printf ("\nfor precx = %d, precy = %d, ",
+                                precx, precy);
+                        if (j == 0)
+                          printf ("eps >~ -2^(-%d)", precy + i);
+                        else
+                          printf ("eps <~ - (2^(-%d) + 2^(%d))",
+                                  precy + i, 1 - 2 * (precy + i));
+                        printf (", %s\n", e3 ? "mpfr_exp_3" : "mpfr_exp");
+                        printf ("Got %u instead of %u.\n",
+                                __gmpfr_flags, flags);
+                        err = 1;
+                      }
+                    if (rnd == GMP_RNDU || rnd == GMP_RNDN ?
+                        mpfr_cmp0 (y, minpos) != 0 : MPFR_NOTZERO (y))
+                      {
+                        printf ("Incorrect result in underflow_up, %s",
+                                mpfr_print_rnd_mode ((mp_rnd_t) rnd));
+                        if (extended_emin)
+                          printf (" and extended emin");
+                        printf ("\nfor precx = %d, precy = %d, ",
+                                precx, precy);
+                        if (j == 0)
+                          printf ("eps >~ -2^(-%d)", precy + i);
+                        else
+                          printf ("eps <~ - (2^(-%d) + 2^(%d))",
+                                  precy + i, 1 - 2 * (precy + i));
+                        printf (", %s\n", e3 ? "mpfr_exp_3" : "mpfr_exp");
+                        mpfr_dump (y);
+                        err = 1;
+                      }
+                    if (err)
+                      exit (1);
+                  }  /* for (e3 ...) */
+            }  /* for (j ...) */
+          mpfr_div_2si (t, t, 1, GMP_RNDN);
+          mpfr_div_2si (t2, t2, 2, GMP_RNDN);
+        }  /* for (i ...) */
+      mpfr_clears (x, y, (mpfr_ptr) 0);
+    }  /* for (precy ...) */
+  mpfr_clears (t, t2, (mpfr_ptr) 0);
+
+  /* Case exp(eps) ~= 1/2, i.e. eps ~= - log(2).
+   * We choose x0 and x1 with high enough precision such that:
+   *   x0 = rndd(rndd(log(minpos)) - rndu(log(2)))
+   *   x1 = rndu(rndu(log(minpos)) - rndd(log(2)))
+   * In revision 5507 (trunk) on a 64-bit Linux machine, this fails:
+   *   Error in underflow_up, eps >~ - log(2) and extended emin
+   *   for precy = 16, mpfr_exp
+   *   Expected 1.0@-1152921504606846976 (minimum positive MPFR number),
+   *   inex > 0 and flags = 9
+   *   Got 0
+   *   with inex = -1 and flags = 9
+   * due to a double-rounding problem in mpfr_mul_2si when rescaling
+   * the result.
+   */
+  mpfr_inits2 (sizeof(mp_exp_t) * CHAR_BIT + 64, x, t, (mpfr_ptr) 0);
+  for (i = 0; i <= 1; i++)
+    {
+      mpfr_log (x, minpos, i ? GMP_RNDU : GMP_RNDD);
+      mpfr_const_log2 (t, i ? GMP_RNDD : GMP_RNDU);
+      mpfr_sub (x, x, t, i ? GMP_RNDU : GMP_RNDD);
+      for (precy = 16; precy <= 128; precy += 16)
+        {
+          mpfr_init2 (y, precy);
+          for (e3 = 0; e3 <= 1; e3++)
+            {
+              unsigned int flags, uflags =
+                MPFR_FLAGS_INEXACT | MPFR_FLAGS_UNDERFLOW;
+
+              mpfr_clear_flags ();
+              inex = e3 ? exp_3 (y, x, GMP_RNDN) : mpfr_exp (y, x, GMP_RNDN);
+              flags = __gmpfr_flags;
+              if (flags != uflags ||
+                  (i ? (inex <= 0 || mpfr_cmp0 (y, minpos) != 0)
+                     : (inex >= 0 || MPFR_NOTZERO (y))))
+                {
+                  printf ("Error in underflow_up, eps %c~ - log(2)",
+                          i ? '>' : '<');
+                  if (extended_emin)
+                    printf (" and extended emin");
+                  printf ("\nfor precy = %d, %s\nExpected ", precy,
+                          e3 ? "mpfr_exp_3" : "mpfr_exp");
+                  if (i)
+                    {
+                      mpfr_out_str (stdout, 16, 0, minpos, GMP_RNDN);
+                      printf (" (minimum positive MPFR number),\ninex >");
+                    }
+                  else
+                    {
+                      printf ("+0, inex <");
+                    }
+                  printf (" 0 and flags = %u\nGot ", uflags);
+                  mpfr_out_str (stdout, 16, 0, y, GMP_RNDN);
+                  printf ("\nwith inex = %d and flags = %u\n", inex, flags);
+                  exit (1);
+                }
+            }
+          mpfr_clear (y);
+        }
+    }
+  mpfr_clears (x, t, (mpfr_ptr) 0);
+
+  mpfr_clear (minpos);
+}
+
+static void
+underflow (void)
+{
+  mp_exp_t emin;
+
+  underflow_up (0);
+
+  emin = mpfr_get_emin ();
+  set_emin (MPFR_EMIN_MIN);
+  if (mpfr_get_emin () != emin)
+    {
+      underflow_up (1);
+      set_emin (emin);
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  tests_start_mpfr ();
+
+  if (argc > 1)
+    check_large ();
+
+  check_inexact ();
+  check_special ();
+
+  test_generic (2, 100, 100);
+
+  compare_exp2_exp3 (20, 1000);
+  check_worst_cases();
+  check3("0.0", GMP_RNDU, "1.0");
+  check3("-1e-170", GMP_RNDU, "1.0");
+  check3("-1e-170", GMP_RNDN, "1.0");
+  check3("-8.88024741073346941839e-17", GMP_RNDU, "1.0");
+  check3("8.70772839244701057915e-01", GMP_RNDN, "2.38875626491680437269");
+  check3("1.0", GMP_RNDN, "2.71828182845904509080");
+  check3("-3.42135637628104173534e-07", GMP_RNDZ, "0.999999657864420798958");
+  /* worst case for argument reduction, very near from 5*log(2),
+     thanks to Jean-Michel Muller  */
+  check3("3.4657359027997265421", GMP_RNDN, "32.0");
+  check3("3.4657359027997265421", GMP_RNDU, "32.0");
+  check3("3.4657359027997265421", GMP_RNDD, "31.999999999999996447");
+  check3("2.26523754332090625496e+01", GMP_RNDD, "6.8833785261699581146e9");
+  check3("1.31478962104089092122e+01", GMP_RNDZ, "5.12930793917860137299e+05");
+  check3("4.25637507920002378103e-01", GMP_RNDU, "1.53056585656161181497e+00");
+  check3("6.26551618962329307459e-16", GMP_RNDU, "1.00000000000000066613e+00");
+  check3("-3.35589513871216568383e-03",GMP_RNDD, "9.96649729583626853291e-01");
+  check3("1.95151388850007272424e+01", GMP_RNDU, "2.98756340674767792225e+08");
+  check3("2.45045953503350730784e+01", GMP_RNDN, "4.38743344916128387451e+10");
+  check3("2.58165606081678085104e+01", GMP_RNDD, "1.62925781879432281494e+11");
+  check3("-2.36539020084338638128e+01",GMP_RNDZ, "5.33630792749924762447e-11");
+  check3("2.39211946135858077866e+01", GMP_RNDU, "2.44817704330214385986e+10");
+  check3("-2.78190533055889162029e+01",GMP_RNDZ, "8.2858803483596879512e-13");
+  check3("2.64028186174889789584e+01", GMP_RNDD, "2.9281844652878973388e11");
+  check3("2.92086338843268329413e+01", GMP_RNDZ, "4.8433797301907177734e12");
+  check3("-2.46355324071459982349e+01",GMP_RNDZ, "1.9995129297760994791e-11");
+  check3("-2.23509444608605427618e+01",GMP_RNDZ, "1.9638492867489702307e-10");
+  check3("-2.41175390197331687148e+01",GMP_RNDD, "3.3564940885530624592e-11");
+  check3("2.46363885231578088053e+01", GMP_RNDU, "5.0055014282693267822e10");
+  check3("111.1263531080090984914932050742208957672119140625", GMP_RNDN, "1.8262572323517295459e48");
+  check3("-3.56196340354684821250e+02",GMP_RNDN, "2.0225297096141478156e-155");
+  check3("6.59678273772710895173e+02", GMP_RNDU, "3.1234469273830195529e286");
+  check3("5.13772529701934331570e+02", GMP_RNDD, "1.3445427121297197752e223");
+  check3("3.57430211008718345056e+02", GMP_RNDD, "1.6981197246857298443e155");
+  check3("3.82001814471465536371e+02", GMP_RNDU, "7.9667300591087367805e165");
+  check3("5.92396038219384422518e+02", GMP_RNDD, "1.880747529554661989e257");
+  check3("-5.02678550462488090034e+02",GMP_RNDU, "4.8919201895446217839e-219");
+  check3("5.30015757134837031117e+02", GMP_RNDD, "1.5237672861171573939e230");
+  check3("5.16239362447650933063e+02", GMP_RNDZ, "1.5845518406744492105e224");
+  check3("6.00812634798592370977e-01", GMP_RNDN, "1.823600119339019443");
+  check_exp10 ();
+
+  bug20080731 ();
+
+  overflowed_exp0 ();
+  underflow ();
+
+  data_check ("data/exp", mpfr_exp, "mpfr_exp");
+  bad_cases (mpfr_exp, mpfr_log, "mpfr_exp", 0, -256, 255, 4, 128, 800, 50);
+
+  tests_end_mpfr ();
+  return 0;
+}