OSDN Git Service

* arith.c (gfc_arith_init_1): Fix off by one problem;
[pf3gnuchains/gcc-fork.git] / gcc / fortran / arith.c
index b4041a6..88b6c36 100644 (file)
@@ -1,5 +1,5 @@
 /* Compiler arithmetic
-   Copyright (C) 2000, 2001, 2002, 2003, 2004 Free Software Foundation,
+   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation,
    Inc.
    Contributed by Andy Vaught
 
@@ -26,496 +26,55 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
    and this file provides the interface.  */
 
 #include "config.h"
-
-#include <string.h>
-
+#include "system.h"
+#include "flags.h"
 #include "gfortran.h"
 #include "arith.h"
 
-mpf_t pi, half_pi, two_pi, e;
-
-/* The gfc_(integer|real)_kinds[] structures have everything the front
-   end needs to know about integers and real numbers on the target.
-   Other entries of the structure are calculated from these values.
-   The first entry is the default kind, the second entry of the real
-   structure is the default double kind.  */
-
-#define MPZ_NULL {{0,0,0}}
-#define MPF_NULL {{0,0,0,0}}
-
-#define DEF_GFC_INTEGER_KIND(KIND,RADIX,DIGITS,BIT_SIZE)               \
-       {KIND, RADIX, DIGITS, BIT_SIZE, 0, MPZ_NULL, MPZ_NULL, MPZ_NULL}
-
-#define DEF_GFC_LOGICAL_KIND(KIND,BIT_SIZE)                            \
-       {KIND, BIT_SIZE}
-
-#define DEF_GFC_REAL_KIND(KIND,RADIX,DIGITS,MIN_EXP, MAX_EXP)          \
-       {KIND, RADIX, DIGITS, MIN_EXP, MAX_EXP,                         \
-        0, 0, MPF_NULL, MPF_NULL, MPF_NULL}
-
-gfc_integer_info gfc_integer_kinds[] = {
-  DEF_GFC_INTEGER_KIND (4, 2, 31, 32),
-  DEF_GFC_INTEGER_KIND (8, 2, 63, 64),
-  DEF_GFC_INTEGER_KIND (2, 2, 15, 16),
-  DEF_GFC_INTEGER_KIND (1, 2,  7,  8),
-  DEF_GFC_INTEGER_KIND (0, 0,  0,  0)
-};
-
-gfc_logical_info gfc_logical_kinds[] = {
-  DEF_GFC_LOGICAL_KIND (4, 32),
-  DEF_GFC_LOGICAL_KIND (8, 64),
-  DEF_GFC_LOGICAL_KIND (2, 16),
-  DEF_GFC_LOGICAL_KIND (1,  8),
-  DEF_GFC_LOGICAL_KIND (0,  0)
-};
-
-gfc_real_info gfc_real_kinds[] = {
-  DEF_GFC_REAL_KIND (4, 2, 24,  -125,  128),
-  DEF_GFC_REAL_KIND (8, 2, 53, -1021, 1024),
-  DEF_GFC_REAL_KIND (0, 0,  0,     0,    0)
-};
-
-
-/* The integer kind to use for array indices.  This will be set to the
-   proper value based on target information from the backend.  */
-
-int gfc_index_integer_kind;
-
-
-/* Compute the natural log of arg.
-
-   We first get the argument into the range 0.5 to 1.5 by successive
-   multiplications or divisions by e.  Then we use the series:
-
-     ln(x) = (x-1) - (x-1)^/2 + (x-1)^3/3 - (x-1)^4/4 + ...
-
-   Because we are expanding in powers of (x-1), and 0.5 < x < 1.5, we
-   have -0.5 < (x-1) < 0.5.  Ignoring the harmonic term, this means
-   that each term is at most 1/(2^i), meaning one bit is gained per
-   iteration.
-
-   Not very efficient, but it doesn't have to be.  */
+/* MPFR does not have a direct replacement for mpz_set_f() from GMP.
+   It's easily implemented with a few calls though.  */
 
 void
-natural_logarithm (mpf_t * arg, mpf_t * result)
+gfc_mpfr_to_mpz (mpz_t z, mpfr_t x)
 {
-  mpf_t x, xp, t, log;
-  int i, p;
-
-  mpf_init_set (x, *arg);
-  mpf_init (t);
-
-  p = 0;
-
-  mpf_set_str (t, "0.5", 10);
-  while (mpf_cmp (x, t) < 0)
-    {
-      mpf_mul (x, x, e);
-      p--;
-    }
-
-  mpf_set_str (t, "1.5", 10);
-  while (mpf_cmp (x, t) > 0)
-    {
-      mpf_div (x, x, e);
-      p++;
-    }
+  mp_exp_t e;
 
-  mpf_sub_ui (x, x, 1);
-  mpf_init_set_ui (log, 0);
-  mpf_init_set_ui (xp, 1);
+  e = mpfr_get_z_exp (z, x);
+  /* MPFR 2.0.1 (included with GMP 4.1) has a bug whereby mpfr_get_z_exp
+     may set the sign of z incorrectly.  Work around that here.  */
+  if (mpfr_sgn (x) != mpz_sgn (z))
+    mpz_neg (z, z);
 
-  for (i = 1; i < GFC_REAL_BITS; i++)
-    {
-      mpf_mul (xp, xp, x);
-      mpf_div_ui (t, xp, i);
-
-      if (i % 2 == 0)
-       mpf_sub (log, log, t);
-      else
-       mpf_add (log, log, t);
-    }
-
-  /* Add in the log (e^p) = p */
-
-  if (p < 0)
-    mpf_sub_ui (log, log, -p);
+  if (e > 0)
+    mpz_mul_2exp (z, z, e);
   else
-    mpf_add_ui (log, log, p);
-
-  mpf_clear (x);
-  mpf_clear (xp);
-  mpf_clear (t);
-
-  mpf_set (*result, log);
-  mpf_clear (log);
+    mpz_tdiv_q_2exp (z, z, -e);
 }
 
 
-/* Calculate the common logarithm of arg.  We use the natural
-   logaritm of arg and of 10:
-
-   log10(arg) = log(arg)/log(10)  */
+/* Set the model number precision by the requested KIND.  */
 
 void
-common_logarithm (mpf_t * arg, mpf_t * result)
+gfc_set_model_kind (int kind)
 {
-  mpf_t i10, log10;
-
-  natural_logarithm (arg, result);
-
-  mpf_init_set_ui (i10, 10);
-  mpf_init (log10);
-  natural_logarithm (&i10, &log10);
+  int index = gfc_validate_kind (BT_REAL, kind, false);
+  int base2prec;
 
-  mpf_div (*result, *result, log10);
-  mpf_clear (i10);
-  mpf_clear (log10);
+  base2prec = gfc_real_kinds[index].digits;
+  if (gfc_real_kinds[index].radix != 2)
+    base2prec *= gfc_real_kinds[index].radix / 2;
+  mpfr_set_default_prec (base2prec);
 }
 
-/* Calculate exp(arg).
-
-   We use a reduction of the form
-
-     x = Nln2 + r
-
-   Then we obtain exp(r) from the McLaurin series.
-   exp(x) is then recovered from the identity
 
-     exp(x) = 2^N*exp(r).  */
+/* Set the model number precision from mpfr_t x.  */
 
 void
-exponential (mpf_t * arg, mpf_t * result)
+gfc_set_model (mpfr_t x)
 {
-  mpf_t two, ln2, power, q, r, num, denom, term, x, xp;
-  int i;
-  long n;
-  unsigned long p, mp;
-
-
-  mpf_init_set (x, *arg);
-
-  if (mpf_cmp_ui (x, 0) == 0)
-    {
-      mpf_set_ui (*result, 1);
-    }
-  else if (mpf_cmp_ui (x, 1) == 0)
-    {
-      mpf_set (*result, e);
-    }
-  else
-    {
-      mpf_init_set_ui (two, 2);
-      mpf_init (ln2);
-      mpf_init (q);
-      mpf_init (r);
-      mpf_init (power);
-      mpf_init (term);
-
-      natural_logarithm (&two, &ln2);
-
-      mpf_div (q, x, ln2);
-      mpf_floor (power, q);
-      mpf_mul (q, power, ln2);
-      mpf_sub (r, x, q);
-
-      mpf_init_set_ui (xp, 1);
-      mpf_init_set_ui (num, 1);
-      mpf_init_set_ui (denom, 1);
-
-      for (i = 1; i <= GFC_REAL_BITS + 10; i++)
-       {
-         mpf_mul (num, num, r);
-         mpf_mul_ui (denom, denom, i);
-         mpf_div (term, num, denom);
-         mpf_add (xp, xp, term);
-       }
-
-      /* Reconstruction step */
-      n = (long) mpf_get_d (power);
-
-      if (n > 0)
-       {
-         p = (unsigned int) n;
-         mpf_mul_2exp (*result, xp, p);
-       }
-      else
-       {
-         mp = (unsigned int) (-n);
-         mpf_div_2exp (*result, xp, mp);
-       }
-
-      mpf_clear (two);
-      mpf_clear (ln2);
-      mpf_clear (q);
-      mpf_clear (r);
-      mpf_clear (power);
-      mpf_clear (num);
-      mpf_clear (denom);
-      mpf_clear (term);
-      mpf_clear (xp);
-    }
-
-  mpf_clear (x);
+  mpfr_set_default_prec (mpfr_get_prec (x));
 }
 
-
-/* Calculate sin(arg).
-
-   We use a reduction of the form
-
-     x= N*2pi + r
-
-   Then we obtain sin(r) from the McLaurin series.  */
-
-void
-sine (mpf_t * arg, mpf_t * result)
-{
-  mpf_t factor, q, r, num, denom, term, x, xp;
-  int i, sign;
-
-  mpf_init_set (x, *arg);
-
-  /* Special case (we do not treat multiples of pi due to roundoff issues) */
-  if (mpf_cmp_ui (x, 0) == 0)
-    {
-      mpf_set_ui (*result, 0);
-    }
-  else
-    {
-      mpf_init (q);
-      mpf_init (r);
-      mpf_init (factor);
-      mpf_init (term);
-
-      mpf_div (q, x, two_pi);
-      mpf_floor (factor, q);
-      mpf_mul (q, factor, two_pi);
-      mpf_sub (r, x, q);
-
-      mpf_init_set_ui (xp, 0);
-      mpf_init_set_ui (num, 1);
-      mpf_init_set_ui (denom, 1);
-
-      sign = -1;
-      for (i = 1; i < GFC_REAL_BITS + 10; i++)
-       {
-         mpf_mul (num, num, r);
-         mpf_mul_ui (denom, denom, i);
-         if (i % 2 == 0)
-           continue;
-
-         sign = -sign;
-         mpf_div (term, num, denom);
-         if (sign > 0)
-           mpf_add (xp, xp, term);
-         else
-           mpf_sub (xp, xp, term);
-       }
-
-      mpf_set (*result, xp);
-
-      mpf_clear (q);
-      mpf_clear (r);
-      mpf_clear (factor);
-      mpf_clear (num);
-      mpf_clear (denom);
-      mpf_clear (term);
-      mpf_clear (xp);
-    }
-
-  mpf_clear (x);
-}
-
-
-/* Calculate cos(arg).
-
-   Similar to sine.  */
-
-void
-cosine (mpf_t * arg, mpf_t * result)
-{
-  mpf_t factor, q, r, num, denom, term, x, xp;
-  int i, sign;
-
-  mpf_init_set (x, *arg);
-
-  /* Special case (we do not treat multiples of pi due to roundoff issues) */
-  if (mpf_cmp_ui (x, 0) == 0)
-    {
-      mpf_set_ui (*result, 1);
-    }
-  else
-    {
-      mpf_init (q);
-      mpf_init (r);
-      mpf_init (factor);
-      mpf_init (term);
-
-      mpf_div (q, x, two_pi);
-      mpf_floor (factor, q);
-      mpf_mul (q, factor, two_pi);
-      mpf_sub (r, x, q);
-
-      mpf_init_set_ui (xp, 1);
-      mpf_init_set_ui (num, 1);
-      mpf_init_set_ui (denom, 1);
-
-      sign = 1;
-      for (i = 1; i < GFC_REAL_BITS + 10; i++)
-       {
-         mpf_mul (num, num, r);
-         mpf_mul_ui (denom, denom, i);
-         if (i % 2 != 0)
-           continue;
-
-         sign = -sign;
-         mpf_div (term, num, denom);
-         if (sign > 0)
-           mpf_add (xp, xp, term);
-         else
-           mpf_sub (xp, xp, term);
-       }
-      mpf_set (*result, xp);
-
-      mpf_clear (q);
-      mpf_clear (r);
-      mpf_clear (factor);
-      mpf_clear (num);
-      mpf_clear (denom);
-      mpf_clear (term);
-      mpf_clear (xp);
-    }
-
-  mpf_clear (x);
-}
-
-
-/* Calculate atan(arg).
-
-   Similar to sine but requires special handling for x near 1.  */
-
-void
-arctangent (mpf_t * arg, mpf_t * result)
-{
-  mpf_t absval, convgu, convgl, num, term, x, xp;
-  int i, sign;
-
-  mpf_init_set (x, *arg);
-
-  /* Special cases */
-  if (mpf_cmp_ui (x, 0) == 0)
-    {
-      mpf_set_ui (*result, 0);
-    }
-  else if (mpf_cmp_ui (x, 1) == 0)
-    {
-      mpf_init (num);
-      mpf_div_ui (num, half_pi, 2);
-      mpf_set (*result, num);
-      mpf_clear (num);
-    }
-  else if (mpf_cmp_si (x, -1) == 0)
-    {
-      mpf_init (num);
-      mpf_div_ui (num, half_pi, 2);
-      mpf_neg (*result, num);
-      mpf_clear (num);
-    }
-  else
-    {                          /* General cases */
-
-      mpf_init (absval);
-      mpf_abs (absval, x);
-
-      mpf_init_set_d (convgu, 1.5);
-      mpf_init_set_d (convgl, 0.5);
-      mpf_init_set_ui (num, 1);
-      mpf_init (term);
-
-      if (mpf_cmp (absval, convgl) < 0)
-       {
-         mpf_init_set_ui (xp, 0);
-         sign = -1;
-         for (i = 1; i < GFC_REAL_BITS + 10; i++)
-           {
-             mpf_mul (num, num, absval);
-             if (i % 2 == 0)
-               continue;
-
-             sign = -sign;
-             mpf_div_ui (term, num, i);
-             if (sign > 0)
-               mpf_add (xp, xp, term);
-             else
-               mpf_sub (xp, xp, term);
-           }
-       }
-      else if (mpf_cmp (absval, convgu) >= 0)
-       {
-         mpf_init_set (xp, half_pi);
-         sign = 1;
-         for (i = 1; i < GFC_REAL_BITS + 10; i++)
-           {
-             mpf_div (num, num, absval);
-             if (i % 2 == 0)
-               continue;
-
-             sign = -sign;
-             mpf_div_ui (term, num, i);
-             if (sign > 0)
-               mpf_add (xp, xp, term);
-             else
-               mpf_sub (xp, xp, term);
-           }
-       }
-      else
-       {
-         mpf_init_set_ui (xp, 0);
-
-         mpf_sub_ui (num, absval, 1);
-         mpf_add_ui (term, absval, 1);
-         mpf_div (absval, num, term);
-
-         mpf_set_ui (num, 1);
-
-         sign = -1;
-         for (i = 1; i < GFC_REAL_BITS + 10; i++)
-           {
-             mpf_mul (num, num, absval);
-             if (i % 2 == 0)
-               continue;
-             sign = -sign;
-             mpf_div_ui (term, num, i);
-             if (sign > 0)
-               mpf_add (xp, xp, term);
-             else
-               mpf_sub (xp, xp, term);
-           }
-
-         mpf_div_ui (term, half_pi, 2);
-         mpf_add (xp, term, xp);
-       }
-
-      /* This makes sure to preserve the identity arctan(-x) = -arctan(x)
-         and improves accuracy to boot.  */
-
-      if (mpf_cmp_ui (x, 0) > 0)
-       mpf_set (*result, xp);
-      else
-       mpf_neg (*result, xp);
-
-      mpf_clear (absval);
-      mpf_clear (convgl);
-      mpf_clear (convgu);
-      mpf_clear (num);
-      mpf_clear (term);
-      mpf_clear (xp);
-    }
-  mpf_clear (x);
-}
-
-
 /* Calculate atan2 (y, x)
 
 atan2(y, x) = atan(y/x)                                if x > 0,
@@ -525,97 +84,46 @@ atan2(y, x) = atan(y/x)                            if x > 0,
 */
 
 void
-arctangent2 (mpf_t * y, mpf_t * x, mpf_t * result)
+arctangent2 (mpfr_t y, mpfr_t x, mpfr_t result)
 {
-  mpf_t t;
+  int i;
+  mpfr_t t;
 
-  mpf_init (t);
+  gfc_set_model (y);
+  mpfr_init (t);
 
-  switch (mpf_sgn (*x))
+  i = mpfr_sgn (x);
+
+  if (i > 0)
     {
-    case 1:
-      mpf_div (t, *y, *x);
-      arctangent (&t, result);
-      break;
-    case -1:
-      mpf_div (t, *y, *x);
-      mpf_abs (t, t);
-      arctangent (&t, &t);
-      mpf_sub (*result, pi, t);
-      if (mpf_sgn (*y) == -1)
-       mpf_neg (*result, *result);
-      break;
-    case 0:
-      if (mpf_sgn (*y) == 0)
-       mpf_set_ui (*result, 0);
+      mpfr_div (t, y, x, GFC_RND_MODE);
+      mpfr_atan (result, t, GFC_RND_MODE);
+    }
+  else if (i < 0)
+    {
+      mpfr_const_pi (result, GFC_RND_MODE);
+      mpfr_div (t, y, x, GFC_RND_MODE);
+      mpfr_abs (t, t, GFC_RND_MODE);
+      mpfr_atan (t, t, GFC_RND_MODE);
+      mpfr_sub (result, result, t, GFC_RND_MODE);
+      if (mpfr_sgn (y) < 0)
+       mpfr_neg (result, result, GFC_RND_MODE);
+    }
+  else
+    {
+      if (mpfr_sgn (y) == 0)
+       mpfr_set_ui (result, 0, GFC_RND_MODE);
       else
        {
-         mpf_set (*result, half_pi);
-         if (mpf_sgn (*y) == -1)
-           mpf_neg (*result, *result);
+          mpfr_const_pi (result, GFC_RND_MODE);
+          mpfr_div_ui (result, result, 2, GFC_RND_MODE);
+         if (mpfr_sgn (y) < 0)
+           mpfr_neg (result, result, GFC_RND_MODE);
        }
-       break;
     }
-  mpf_clear (t);
-}
-
-/* Calculate cosh(arg). */
-
-void
-hypercos (mpf_t * arg, mpf_t * result)
-{
-  mpf_t neg, term1, term2, x, xp;
 
-  mpf_init_set (x, *arg);
+  mpfr_clear (t);
 
-  mpf_init (neg);
-  mpf_init (term1);
-  mpf_init (term2);
-  mpf_init (xp);
-
-  mpf_neg (neg, x);
-
-  exponential (&x, &term1);
-  exponential (&neg, &term2);
-
-  mpf_add (xp, term1, term2);
-  mpf_div_ui (*result, xp, 2);
-
-  mpf_clear (neg);
-  mpf_clear (term1);
-  mpf_clear (term2);
-  mpf_clear (x);
-  mpf_clear (xp);
-}
-
-
-/* Calculate sinh(arg). */
-
-void
-hypersine (mpf_t * arg, mpf_t * result)
-{
-  mpf_t neg, term1, term2, x, xp;
-
-  mpf_init_set (x, *arg);
-
-  mpf_init (neg);
-  mpf_init (term1);
-  mpf_init (term2);
-  mpf_init (xp);
-
-  mpf_neg (neg, x);
-
-  exponential (&x, &term1);
-  exponential (&neg, &term2);
-
-  mpf_sub (xp, term1, term2);
-  mpf_div_ui (*result, xp, 2);
-
-  mpf_clear (neg);
-  mpf_clear (term1);
-  mpf_clear (term2);
-  mpf_clear (x);
-  mpf_clear (xp);
 }
 
 
@@ -638,15 +146,18 @@ gfc_arith_error (arith code)
     case ARITH_UNDERFLOW:
       p = "Arithmetic underflow";
       break;
+    case ARITH_NAN:
+      p = "Arithmetic NaN";
+      break;
     case ARITH_DIV0:
       p = "Division by zero";
       break;
-    case ARITH_0TO0:
-      p = "Indeterminate form 0 ** 0";
-      break;
     case ARITH_INCOMMENSURATE:
       p = "Array operands are incommensurate";
       break;
+    case ARITH_ASYMMETRIC:
+      p = "Integer outside symmetric range implied by Standard Fortran";
+      break;
     default:
       gfc_internal_error ("gfc_arith_error(): Bad error code");
     }
@@ -662,72 +173,16 @@ gfc_arith_init_1 (void)
 {
   gfc_integer_info *int_info;
   gfc_real_info *real_info;
-  mpf_t a, b;
+  mpfr_t a, b, c;
   mpz_t r;
-  int i, n, limit;
-
-  /* Set the default precision for GMP computations.  */
-  mpf_set_default_prec (GFC_REAL_BITS + 30);
-
-  /* Calculate e, needed by the natural_logarithm() subroutine.  */
-  mpf_init (b);
-  mpf_init_set_ui (e, 0);
-  mpf_init_set_ui (a, 1);
-
-  for (i = 1; i < 100; i++)
-    {
-      mpf_add (e, e, a);
-      mpf_div_ui (a, a, i);    /* 1/(i!) */
-    }
-
-  /* Calculate pi, 2pi, pi/2, and -pi/2, needed for trigonometric
-     functions.
-
-     We use the Bailey, Borwein and Plouffe formula:
-
-       pi = \sum{n=0}^\infty (1/16)^n [4/(8n+1) - 2/(8n+4) - 1/(8n+5) - 1/(8n+6)]
-
-     which gives about four bits per iteration.  */
-
-  mpf_init_set_ui (pi, 0);
-
-  mpf_init (two_pi);
-  mpf_init (half_pi);
-
-  limit = (GFC_REAL_BITS / 4) + 10;    /* (1/16)^n gives 4 bits per iteration */
-
-  for (n = 0; n < limit; n++)
-    {
-      mpf_set_ui (b, 4);
-      mpf_div_ui (b, b, 8 * n + 1);    /* 4/(8n+1) */
-
-      mpf_set_ui (a, 2);
-      mpf_div_ui (a, a, 8 * n + 4);    /* 2/(8n+4) */
-      mpf_sub (b, b, a);
-
-      mpf_set_ui (a, 1);
-      mpf_div_ui (a, a, 8 * n + 5);    /* 1/(8n+5) */
-      mpf_sub (b, b, a);
-
-      mpf_set_ui (a, 1);
-      mpf_div_ui (a, a, 8 * n + 6);    /* 1/(8n+6) */
-      mpf_sub (b, b, a);
-
-      mpf_set_ui (a, 16);
-      mpf_pow_ui (a, a, n);    /* 16^n */
-
-      mpf_div (b, b, a);
-
-      mpf_add (pi, pi, b);
-    }
+  int i;
 
-  mpf_mul_ui (two_pi, pi, 2);
-  mpf_div_ui (half_pi, pi, 2);
+  mpfr_set_default_prec (128);
+  mpfr_init (a);
+  mpz_init (r);
 
   /* Convert the minimum/maximum values for each kind into their
      GNU MP representation.  */
-  mpz_init (r);
-
   for (int_info = gfc_integer_kinds; int_info->kind != 0; int_info++)
     {
       /* Huge */
@@ -740,70 +195,104 @@ gfc_arith_init_1 (void)
       /* These are the numbers that are actually representable by the
          target.  For bases other than two, this needs to be changed.  */
       if (int_info->radix != 2)
-       gfc_internal_error ("Fix min_int, max_int calculation");
+        gfc_internal_error ("Fix min_int, max_int calculation");
+
+      /* See PRs 13490 and 17912, related to integer ranges.
+         The pedantic_min_int exists for range checking when a program
+         is compiled with -pedantic, and reflects the belief that
+         Standard Fortran requires integers to be symmetrical, i.e.
+         every negative integer must have a representable positive
+         absolute value, and vice versa.  */
+
+      mpz_init (int_info->pedantic_min_int);
+      mpz_neg (int_info->pedantic_min_int, int_info->huge);
 
       mpz_init (int_info->min_int);
-      mpz_neg (int_info->min_int, int_info->huge);
-      /* No -1 here, because the representation is symmetric.  */
+      mpz_sub_ui (int_info->min_int, int_info->pedantic_min_int, 1);
 
       mpz_init (int_info->max_int);
       mpz_add (int_info->max_int, int_info->huge, int_info->huge);
       mpz_add_ui (int_info->max_int, int_info->max_int, 1);
 
       /* Range */
-      mpf_set_z (a, int_info->huge);
-      common_logarithm (&a, &a);
-      mpf_trunc (a, a);
-      mpz_set_f (r, a);
+      mpfr_set_z (a, int_info->huge, GFC_RND_MODE);
+      mpfr_log10 (a, a, GFC_RND_MODE);
+      mpfr_trunc (a, a);
+      gfc_mpfr_to_mpz (r, a);
       int_info->range = mpz_get_si (r);
     }
 
-  /*  mpf_set_default_prec(GFC_REAL_BITS); */
+  mpfr_clear (a);
+
   for (real_info = gfc_real_kinds; real_info->kind != 0; real_info++)
     {
-      /* Huge */
-      mpf_set_ui (a, real_info->radix);
-      mpf_set_ui (b, real_info->radix);
+      gfc_set_model_kind (real_info->kind);
 
-      mpf_pow_ui (a, a, real_info->max_exponent);
-      mpf_pow_ui (b, b, real_info->max_exponent - real_info->digits);
+      mpfr_init (a);
+      mpfr_init (b);
+      mpfr_init (c);
 
-      mpf_init (real_info->huge);
-      mpf_sub (real_info->huge, a, b);
+      /* huge(x) = (1 - b**(-p)) * b**(emax-1) * b  */
+      /* a = 1 - b**(-p) */
+      mpfr_set_ui (a, 1, GFC_RND_MODE);
+      mpfr_set_ui (b, real_info->radix, GFC_RND_MODE);
+      mpfr_pow_si (b, b, -real_info->digits, GFC_RND_MODE);
+      mpfr_sub (a, a, b, GFC_RND_MODE);
 
-      /* Tiny */
-      mpf_set_ui (b, real_info->radix);
-      mpf_pow_ui (b, b, 1 - real_info->min_exponent);
+      /* c = b**(emax-1) */
+      mpfr_set_ui (b, real_info->radix, GFC_RND_MODE);
+      mpfr_pow_ui (c, b, real_info->max_exponent - 1, GFC_RND_MODE);
 
-      mpf_init (real_info->tiny);
-      mpf_ui_div (real_info->tiny, 1, b);
+      /* a = a * c = (1 - b**(-p)) * b**(emax-1) */
+      mpfr_mul (a, a, c, GFC_RND_MODE);
 
-      /* Epsilon */
-      mpf_set_ui (b, real_info->radix);
-      mpf_pow_ui (b, b, real_info->digits - 1);
+      /* a = (1 - b**(-p)) * b**(emax-1) * b */
+      mpfr_mul_ui (a, a, real_info->radix, GFC_RND_MODE);
 
-      mpf_init (real_info->epsilon);
-      mpf_ui_div (real_info->epsilon, 1, b);
+      mpfr_init (real_info->huge);
+      mpfr_set (real_info->huge, a, GFC_RND_MODE);
 
-      /* Range */
-      common_logarithm (&real_info->huge, &a);
-      common_logarithm (&real_info->tiny, &b);
-      mpf_neg (b, b);
+      /* tiny(x) = b**(emin-1) */
+      mpfr_set_ui (b, real_info->radix, GFC_RND_MODE);
+      mpfr_pow_si (b, b, real_info->min_exponent - 1, GFC_RND_MODE);
+
+      mpfr_init (real_info->tiny);
+      mpfr_set (real_info->tiny, b, GFC_RND_MODE);
+
+      /* subnormal (x) = b**(emin - digit) */
+      mpfr_set_ui (b, real_info->radix, GFC_RND_MODE);
+      mpfr_pow_si (b, b, real_info->min_exponent - real_info->digits,
+                  GFC_RND_MODE);
+
+      mpfr_init (real_info->subnormal);
+      mpfr_set (real_info->subnormal, b, GFC_RND_MODE);
+
+      /* epsilon(x) = b**(1-p) */
+      mpfr_set_ui (b, real_info->radix, GFC_RND_MODE);
+      mpfr_pow_si (b, b, 1 - real_info->digits, GFC_RND_MODE);
 
-      if (mpf_cmp (a, b) > 0)
-       mpf_set (a, b);         /* a = min(a, b) */
+      mpfr_init (real_info->epsilon);
+      mpfr_set (real_info->epsilon, b, GFC_RND_MODE);
 
-      mpf_trunc (a, a);
-      mpz_set_f (r, a);
+      /* range(x) = int(min(log10(huge(x)), -log10(tiny)) */
+      mpfr_log10 (a, real_info->huge, GFC_RND_MODE);
+      mpfr_log10 (b, real_info->tiny, GFC_RND_MODE);
+      mpfr_neg (b, b, GFC_RND_MODE);
+
+      if (mpfr_cmp (a, b) > 0)
+       mpfr_set (a, b, GFC_RND_MODE);          /* a = min(a, b) */
+
+      mpfr_trunc (a, a);
+      gfc_mpfr_to_mpz (r, a);
       real_info->range = mpz_get_si (r);
 
-      /* Precision */
-      mpf_set_ui (a, real_info->radix);
-      common_logarithm (&a, &a);
+      /* precision(x) = int((p - 1) * log10(b)) + k */
+      mpfr_set_ui (a, real_info->radix, GFC_RND_MODE);
+      mpfr_log10 (a, a, GFC_RND_MODE);
 
-      mpf_mul_ui (a, a, real_info->digits - 1);
-      mpf_trunc (a, a);
-      mpz_set_f (r, a);
+      mpfr_mul_ui (a, a, real_info->digits - 1, GFC_RND_MODE);
+      mpfr_trunc (a, a);
+      gfc_mpfr_to_mpz (r, a);
       real_info->precision = mpz_get_si (r);
 
       /* If the radix is an integral power of 10, add one to the
@@ -811,11 +300,13 @@ gfc_arith_init_1 (void)
       for (i = 10; i <= real_info->radix; i *= 10)
        if (i == real_info->radix)
          real_info->precision++;
+
+      mpfr_clear (a);
+      mpfr_clear (b);
+      mpfr_clear (c);
     }
 
   mpz_clear (r);
-  mpf_clear (a);
-  mpf_clear (b);
 }
 
 
@@ -827,12 +318,6 @@ gfc_arith_done_1 (void)
   gfc_integer_info *ip;
   gfc_real_info *rp;
 
-  mpf_clear (e);
-
-  mpf_clear (pi);
-  mpf_clear (half_pi);
-  mpf_clear (two_pi);
-
   for (ip = gfc_integer_kinds; ip->kind; ip++)
     {
       mpz_clear (ip->min_int);
@@ -842,160 +327,16 @@ gfc_arith_done_1 (void)
 
   for (rp = gfc_real_kinds; rp->kind; rp++)
     {
-      mpf_clear (rp->epsilon);
-      mpf_clear (rp->huge);
-      mpf_clear (rp->tiny);
-    }
-}
-
-
-/* Return default kinds.  */
-
-int
-gfc_default_integer_kind (void)
-{
-  return gfc_integer_kinds[gfc_option.i8 ? 1 : 0].kind;
-}
-
-int
-gfc_default_real_kind (void)
-{
-  return gfc_real_kinds[gfc_option.r8 ? 1 : 0].kind;
-}
-
-int
-gfc_default_double_kind (void)
-{
-  return gfc_real_kinds[1].kind;
-}
-
-int
-gfc_default_character_kind (void)
-{
-  return 1;
-}
-
-int
-gfc_default_logical_kind (void)
-{
-  return gfc_logical_kinds[gfc_option.i8 ? 1 : 0].kind;
-}
-
-int
-gfc_default_complex_kind (void)
-{
-  return gfc_default_real_kind ();
-}
-
-
-/* Make sure that a valid kind is present.  Returns an index into the
-   gfc_integer_kinds array, -1 if the kind is not present.  */
-
-static int
-validate_integer (int kind)
-{
-  int i;
-
-  for (i = 0;; i++)
-    {
-      if (gfc_integer_kinds[i].kind == 0)
-       {
-         i = -1;
-         break;
-       }
-      if (gfc_integer_kinds[i].kind == kind)
-       break;
-    }
-
-  return i;
-}
-
-
-static int
-validate_real (int kind)
-{
-  int i;
-
-  for (i = 0;; i++)
-    {
-      if (gfc_real_kinds[i].kind == 0)
-       {
-         i = -1;
-         break;
-       }
-      if (gfc_real_kinds[i].kind == kind)
-       break;
+      mpfr_clear (rp->epsilon);
+      mpfr_clear (rp->huge);
+      mpfr_clear (rp->tiny);
     }
-
-  return i;
-}
-
-
-static int
-validate_logical (int kind)
-{
-  int i;
-
-  for (i = 0;; i++)
-    {
-      if (gfc_logical_kinds[i].kind == 0)
-       {
-         i = -1;
-         break;
-       }
-      if (gfc_logical_kinds[i].kind == kind)
-       break;
-    }
-
-  return i;
-}
-
-
-static int
-validate_character (int kind)
-{
-
-  if (kind == gfc_default_character_kind ())
-    return 0;
-  return -1;
-}
-
-
-/* Validate a kind given a basic type.  The return value is the same
-   for the child functions, with -1 indicating nonexistence of the
-   type.  */
-
-int
-gfc_validate_kind (bt type, int kind)
-{
-  int rc;
-
-  switch (type)
-    {
-    case BT_REAL:              /* Fall through */
-    case BT_COMPLEX:
-      rc = validate_real (kind);
-      break;
-    case BT_INTEGER:
-      rc = validate_integer (kind);
-      break;
-    case BT_LOGICAL:
-      rc = validate_logical (kind);
-      break;
-    case BT_CHARACTER:
-      rc = validate_character (kind);
-      break;
-
-    default:
-      gfc_internal_error ("gfc_validate_kind(): Got bad type");
-    }
-
-  return rc;
 }
 
 
 /* Given an integer and a kind, make sure that the integer lies within
-   the range of the kind.  Returns ARITH_OK or ARITH_OVERFLOW.  */
+   the range of the kind.  Returns ARITH_OK, ARITH_ASYMMETRIC or
+   ARITH_OVERFLOW.  */
 
 static arith
 gfc_check_integer_range (mpz_t p, int kind)
@@ -1003,12 +344,15 @@ gfc_check_integer_range (mpz_t p, int kind)
   arith result;
   int i;
 
-  i = validate_integer (kind);
-  if (i == -1)
-    gfc_internal_error ("gfc_check_integer_range(): Bad kind");
-
+  i = gfc_validate_kind (BT_INTEGER, kind, false);
   result = ARITH_OK;
 
+  if (pedantic)
+    {
+      if (mpz_cmp (p, gfc_integer_kinds[i].pedantic_min_int) < 0)
+        result = ARITH_ASYMMETRIC;
+    }
+
   if (mpz_cmp (p, gfc_integer_kinds[i].min_int) < 0
       || mpz_cmp (p, gfc_integer_kinds[i].max_int) > 0)
     result = ARITH_OVERFLOW;
@@ -1022,34 +366,61 @@ gfc_check_integer_range (mpz_t p, int kind)
    ARITH_UNDERFLOW.  */
 
 static arith
-gfc_check_real_range (mpf_t p, int kind)
+gfc_check_real_range (mpfr_t p, int kind)
 {
   arith retval;
-  mpf_t q;
+  mpfr_t q;
   int i;
 
-  mpf_init (q);
-  mpf_abs (q, p);
+  i = gfc_validate_kind (BT_REAL, kind, false);
 
-  i = validate_real (kind);
-  if (i == -1)
-    gfc_internal_error ("gfc_check_real_range(): Bad kind");
+  gfc_set_model (p);
+  mpfr_init (q);
+  mpfr_abs (q, p, GFC_RND_MODE);
 
-  retval = ARITH_OK;
-  if (mpf_sgn (q) == 0)
-    goto done;
-
-  if (mpf_cmp (q, gfc_real_kinds[i].huge) == 1)
+  if (mpfr_sgn (q) == 0)
+    retval = ARITH_OK;
+  else if (mpfr_cmp (q, gfc_real_kinds[i].huge) > 0)
+    retval = ARITH_OVERFLOW;
+  else if (mpfr_cmp (q, gfc_real_kinds[i].subnormal) < 0)
+    retval = ARITH_UNDERFLOW;
+  else if (mpfr_cmp (q, gfc_real_kinds[i].tiny) < 0)
     {
-      retval = ARITH_OVERFLOW;
-      goto done;
-    }
+      /* MPFR operates on a numbers with a given precision and enormous
+       exponential range.  To represent subnormal numbers the exponent is
+       allowed to become smaller than emin, but always retains the full
+       precision.  This function resets unused bits to 0 to alleviate
+       rounding problems.  Note, a future version of MPFR will have a
+       mpfr_subnormalize() function, which handles this truncation in a
+       more efficient and robust way.  */
+
+      int j, k;
+      char *bin, *s;
+      mp_exp_t e;
+
+      bin = mpfr_get_str (NULL, &e, gfc_real_kinds[i].radix, 0, q, GMP_RNDN);
+      k = gfc_real_kinds[i].digits - (gfc_real_kinds[i].min_exponent - e);
+      for (j = k; j < gfc_real_kinds[i].digits; j++)
+       bin[j] = '0';
+      /* Need space for '0.', bin, 'E', and e */
+      s = (char *) gfc_getmem (strlen(bin)+10);
+      sprintf (s, "0.%sE%d", bin, (int) e);
+      mpfr_set_str (q, s, gfc_real_kinds[i].radix, GMP_RNDN);
+
+      if (mpfr_sgn (p) < 0)
+       mpfr_neg (p, q, GMP_RNDN);
+      else
+       mpfr_set (p, q, GMP_RNDN);
 
-  if (mpf_cmp (q, gfc_real_kinds[i].tiny) == -1)
-    retval = ARITH_UNDERFLOW;
+      gfc_free (s);
+      gfc_free (bin);
 
-done:
-  mpf_clear (q);
+      retval = ARITH_OK;
+    }
+  else
+    retval = ARITH_OK;
+
+  mpfr_clear (q);
 
   return retval;
 }
@@ -1081,12 +452,14 @@ gfc_constant_result (bt type, int kind, locus * where)
       break;
 
     case BT_REAL:
-      mpf_init (result->value.real);
+      gfc_set_model_kind (kind);
+      mpfr_init (result->value.real);
       break;
 
     case BT_COMPLEX:
-      mpf_init (result->value.complex.r);
-      mpf_init (result->value.complex.i);
+      gfc_set_model_kind (kind);
+      mpfr_init (result->value.complex.r);
+      mpfr_init (result->value.complex.i);
       break;
 
     default:
@@ -1173,7 +546,7 @@ gfc_arith_neqv (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 
 
 /* Make sure a constant numeric expression is within the range for
-   it's type and kind.  Note that there's also a gfc_check_range(),
+   its type and kind.  Note that there's also a gfc_check_range(),
    but that one deals with the intrinsic RANGE function.  */
 
 arith
@@ -1189,12 +562,20 @@ gfc_range_check (gfc_expr * e)
 
     case BT_REAL:
       rc = gfc_check_real_range (e->value.real, e->ts.kind);
+      if (rc == ARITH_UNDERFLOW)
+        mpfr_set_ui (e->value.real, 0, GFC_RND_MODE);
       break;
 
     case BT_COMPLEX:
       rc = gfc_check_real_range (e->value.complex.r, e->ts.kind);
-      if (rc != ARITH_OK)
-       rc = gfc_check_real_range (e->value.complex.i, e->ts.kind);
+      if (rc == ARITH_UNDERFLOW)
+        mpfr_set_ui (e->value.complex.r, 0, GFC_RND_MODE);
+      if (rc == ARITH_OK || rc == ARITH_UNDERFLOW)
+        {
+          rc = gfc_check_real_range (e->value.complex.i, e->ts.kind);
+          if (rc == ARITH_UNDERFLOW)
+            mpfr_set_ui (e->value.complex.i, 0, GFC_RND_MODE);
+        }
 
       break;
 
@@ -1206,6 +587,36 @@ gfc_range_check (gfc_expr * e)
 }
 
 
+/* Several of the following routines use the same set of statements to
+   check the validity of the result.  Encapsulate the checking here.  */
+
+static arith
+check_result (arith rc, gfc_expr * x, gfc_expr * r, gfc_expr ** rp)
+{
+  arith val = rc;
+
+  if (val == ARITH_UNDERFLOW)
+    {
+      if (gfc_option.warn_underflow)
+       gfc_warning ("%s at %L", gfc_arith_error (val), &x->where);
+      val = ARITH_OK;
+    }
+
+  if (val == ARITH_ASYMMETRIC)
+    {
+      gfc_warning ("%s at %L", gfc_arith_error (val), &x->where);
+      val = ARITH_OK;
+    }
+
+  if (val != ARITH_OK)
+    gfc_free_expr (r);
+  else
+    *rp = r;
+
+  return val;
+}
+
+
 /* It may seem silly to have a subroutine that actually computes the
    unary plus of a constant, but it prevents us from making exceptions
    in the code elsewhere.  */
@@ -1213,7 +624,6 @@ gfc_range_check (gfc_expr * e)
 static arith
 gfc_arith_uplus (gfc_expr * op1, gfc_expr ** resultp)
 {
-
   *resultp = gfc_copy_expr (op1);
   return ARITH_OK;
 }
@@ -1234,12 +644,12 @@ gfc_arith_uminus (gfc_expr * op1, gfc_expr ** resultp)
       break;
 
     case BT_REAL:
-      mpf_neg (result->value.real, op1->value.real);
+      mpfr_neg (result->value.real, op1->value.real, GFC_RND_MODE);
       break;
 
     case BT_COMPLEX:
-      mpf_neg (result->value.complex.r, op1->value.complex.r);
-      mpf_neg (result->value.complex.i, op1->value.complex.i);
+      mpfr_neg (result->value.complex.r, op1->value.complex.r, GFC_RND_MODE);
+      mpfr_neg (result->value.complex.i, op1->value.complex.i, GFC_RND_MODE);
       break;
 
     default:
@@ -1248,12 +658,7 @@ gfc_arith_uminus (gfc_expr * op1, gfc_expr ** resultp)
 
   rc = gfc_range_check (result);
 
-  if (rc != ARITH_OK)
-    gfc_free_expr (result);
-  else
-    *resultp = result;
-
-  return rc;
+  return check_result (rc, op1, result, resultp);
 }
 
 
@@ -1272,15 +677,16 @@ gfc_arith_plus (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
       break;
 
     case BT_REAL:
-      mpf_add (result->value.real, op1->value.real, op2->value.real);
+      mpfr_add (result->value.real, op1->value.real, op2->value.real,
+               GFC_RND_MODE);
       break;
 
     case BT_COMPLEX:
-      mpf_add (result->value.complex.r, op1->value.complex.r,
-              op2->value.complex.r);
+      mpfr_add (result->value.complex.r, op1->value.complex.r,
+              op2->value.complex.r, GFC_RND_MODE);
 
-      mpf_add (result->value.complex.i, op1->value.complex.i,
-              op2->value.complex.i);
+      mpfr_add (result->value.complex.i, op1->value.complex.i,
+              op2->value.complex.i, GFC_RND_MODE);
       break;
 
     default:
@@ -1289,12 +695,7 @@ gfc_arith_plus (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 
   rc = gfc_range_check (result);
 
-  if (rc != ARITH_OK)
-    gfc_free_expr (result);
-  else
-    *resultp = result;
-
-  return rc;
+  return check_result (rc, op1, result, resultp);
 }
 
 
@@ -1313,16 +714,16 @@ gfc_arith_minus (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
       break;
 
     case BT_REAL:
-      mpf_sub (result->value.real, op1->value.real, op2->value.real);
+      mpfr_sub (result->value.real, op1->value.real, op2->value.real,
+                GFC_RND_MODE);
       break;
 
     case BT_COMPLEX:
-      mpf_sub (result->value.complex.r, op1->value.complex.r,
-              op2->value.complex.r);
-
-      mpf_sub (result->value.complex.i, op1->value.complex.i,
-              op2->value.complex.i);
+      mpfr_sub (result->value.complex.r, op1->value.complex.r,
+              op2->value.complex.r, GFC_RND_MODE);
 
+      mpfr_sub (result->value.complex.i, op1->value.complex.i,
+              op2->value.complex.i, GFC_RND_MODE);
       break;
 
     default:
@@ -1331,12 +732,7 @@ gfc_arith_minus (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 
   rc = gfc_range_check (result);
 
-  if (rc != ARITH_OK)
-    gfc_free_expr (result);
-  else
-    *resultp = result;
-
-  return rc;
+  return check_result (rc, op1, result, resultp);
 }
 
 
@@ -1344,7 +740,7 @@ static arith
 gfc_arith_times (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 {
   gfc_expr *result;
-  mpf_t x, y;
+  mpfr_t x, y;
   arith rc;
 
   result = gfc_constant_result (op1->ts.type, op1->ts.kind, &op1->where);
@@ -1356,23 +752,28 @@ gfc_arith_times (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
       break;
 
     case BT_REAL:
-      mpf_mul (result->value.real, op1->value.real, op2->value.real);
+      mpfr_mul (result->value.real, op1->value.real, op2->value.real,
+               GFC_RND_MODE);
       break;
 
     case BT_COMPLEX:
-      mpf_init (x);
-      mpf_init (y);
 
-      mpf_mul (x, op1->value.complex.r, op2->value.complex.r);
-      mpf_mul (y, op1->value.complex.i, op2->value.complex.i);
-      mpf_sub (result->value.complex.r, x, y);
+      /* FIXME:  possible numericals problem.  */
 
-      mpf_mul (x, op1->value.complex.r, op2->value.complex.i);
-      mpf_mul (y, op1->value.complex.i, op2->value.complex.r);
-      mpf_add (result->value.complex.i, x, y);
+      gfc_set_model (op1->value.complex.r);
+      mpfr_init (x);
+      mpfr_init (y);
 
-      mpf_clear (x);
-      mpf_clear (y);
+      mpfr_mul (x, op1->value.complex.r, op2->value.complex.r, GFC_RND_MODE);
+      mpfr_mul (y, op1->value.complex.i, op2->value.complex.i, GFC_RND_MODE);
+      mpfr_sub (result->value.complex.r, x, y, GFC_RND_MODE);
+
+      mpfr_mul (x, op1->value.complex.r, op2->value.complex.i, GFC_RND_MODE);
+      mpfr_mul (y, op1->value.complex.i, op2->value.complex.r, GFC_RND_MODE);
+      mpfr_add (result->value.complex.i, x, y, GFC_RND_MODE);
+
+      mpfr_clear (x);
+      mpfr_clear (y);
 
       break;
 
@@ -1382,12 +783,7 @@ gfc_arith_times (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 
   rc = gfc_range_check (result);
 
-  if (rc != ARITH_OK)
-    gfc_free_expr (result);
-  else
-    *resultp = result;
-
-  return rc;
+  return check_result (rc, op1, result, resultp);
 }
 
 
@@ -1395,7 +791,7 @@ static arith
 gfc_arith_divide (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 {
   gfc_expr *result;
-  mpf_t x, y, div;
+  mpfr_t x, y, div;
   arith rc;
 
   rc = ARITH_OK;
@@ -1416,44 +812,51 @@ gfc_arith_divide (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
       break;
 
     case BT_REAL:
-      if (mpf_sgn (op2->value.real) == 0)
+      /* FIXME: MPFR correctly generates NaN.  This may not be needed.  */
+      if (mpfr_sgn (op2->value.real) == 0)
        {
          rc = ARITH_DIV0;
          break;
        }
 
-      mpf_div (result->value.real, op1->value.real, op2->value.real);
+      mpfr_div (result->value.real, op1->value.real, op2->value.real,
+               GFC_RND_MODE);
       break;
 
     case BT_COMPLEX:
-      if (mpf_sgn (op2->value.complex.r) == 0
-         && mpf_sgn (op2->value.complex.i) == 0)
+      /* FIXME: MPFR correctly generates NaN.  This may not be needed.  */
+      if (mpfr_sgn (op2->value.complex.r) == 0
+         && mpfr_sgn (op2->value.complex.i) == 0)
        {
          rc = ARITH_DIV0;
          break;
        }
 
-      mpf_init (x);
-      mpf_init (y);
-      mpf_init (div);
+      gfc_set_model (op1->value.complex.r);
+      mpfr_init (x);
+      mpfr_init (y);
+      mpfr_init (div);
 
-      mpf_mul (x, op2->value.complex.r, op2->value.complex.r);
-      mpf_mul (y, op2->value.complex.i, op2->value.complex.i);
-      mpf_add (div, x, y);
+      /* FIXME: possible numerical problems.  */
+      mpfr_mul (x, op2->value.complex.r, op2->value.complex.r, GFC_RND_MODE);
+      mpfr_mul (y, op2->value.complex.i, op2->value.complex.i, GFC_RND_MODE);
+      mpfr_add (div, x, y, GFC_RND_MODE);
 
-      mpf_mul (x, op1->value.complex.r, op2->value.complex.r);
-      mpf_mul (y, op1->value.complex.i, op2->value.complex.i);
-      mpf_add (result->value.complex.r, x, y);
-      mpf_div (result->value.complex.r, result->value.complex.r, div);
+      mpfr_mul (x, op1->value.complex.r, op2->value.complex.r, GFC_RND_MODE);
+      mpfr_mul (y, op1->value.complex.i, op2->value.complex.i, GFC_RND_MODE);
+      mpfr_add (result->value.complex.r, x, y, GFC_RND_MODE);
+      mpfr_div (result->value.complex.r, result->value.complex.r, div,
+                GFC_RND_MODE);
 
-      mpf_mul (x, op1->value.complex.i, op2->value.complex.r);
-      mpf_mul (y, op1->value.complex.r, op2->value.complex.i);
-      mpf_sub (result->value.complex.i, x, y);
-      mpf_div (result->value.complex.i, result->value.complex.i, div);
+      mpfr_mul (x, op1->value.complex.i, op2->value.complex.r, GFC_RND_MODE);
+      mpfr_mul (y, op1->value.complex.r, op2->value.complex.i, GFC_RND_MODE);
+      mpfr_sub (result->value.complex.i, x, y, GFC_RND_MODE);
+      mpfr_div (result->value.complex.i, result->value.complex.i, div,
+                GFC_RND_MODE);
 
-      mpf_clear (x);
-      mpf_clear (y);
-      mpf_clear (div);
+      mpfr_clear (x);
+      mpfr_clear (y);
+      mpfr_clear (div);
 
       break;
 
@@ -1464,12 +867,7 @@ gfc_arith_divide (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
   if (rc == ARITH_OK)
     rc = gfc_range_check (result);
 
-  if (rc != ARITH_OK)
-    gfc_free_expr (result);
-  else
-    *resultp = result;
-
-  return rc;
+  return check_result (rc, op1, result, resultp);
 }
 
 
@@ -1478,30 +876,31 @@ gfc_arith_divide (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 static void
 complex_reciprocal (gfc_expr * op)
 {
-  mpf_t mod, a, result_r, result_i;
+  mpfr_t mod, a, re, im;
 
-  mpf_init (mod);
-  mpf_init (a);
+  gfc_set_model (op->value.complex.r);
+  mpfr_init (mod);
+  mpfr_init (a);
+  mpfr_init (re);
+  mpfr_init (im);
 
-  mpf_mul (mod, op->value.complex.r, op->value.complex.r);
-  mpf_mul (a, op->value.complex.i, op->value.complex.i);
-  mpf_add (mod, mod, a);
+  /* FIXME:  another possible numerical problem.  */
+  mpfr_mul (mod, op->value.complex.r, op->value.complex.r, GFC_RND_MODE);
+  mpfr_mul (a, op->value.complex.i, op->value.complex.i, GFC_RND_MODE);
+  mpfr_add (mod, mod, a, GFC_RND_MODE);
 
-  mpf_init (result_r);
-  mpf_div (result_r, op->value.complex.r, mod);
+  mpfr_div (re, op->value.complex.r, mod, GFC_RND_MODE);
 
-  mpf_init (result_i);
-  mpf_neg (result_i, op->value.complex.i);
-  mpf_div (result_i, result_i, mod);
+  mpfr_neg (im, op->value.complex.i, GFC_RND_MODE);
+  mpfr_div (im, im, mod, GFC_RND_MODE);
 
-  mpf_set (op->value.complex.r, result_r);
-  mpf_set (op->value.complex.i, result_i);
+  mpfr_set (op->value.complex.r, re, GFC_RND_MODE);
+  mpfr_set (op->value.complex.i, im, GFC_RND_MODE);
 
-  mpf_clear (result_r);
-  mpf_clear (result_i);
-
-  mpf_clear (mod);
-  mpf_clear (a);
+  mpfr_clear (re);
+  mpfr_clear (im);
+  mpfr_clear (mod);
+  mpfr_clear (a);
 }
 
 
@@ -1510,32 +909,37 @@ complex_reciprocal (gfc_expr * op)
 static void
 complex_pow_ui (gfc_expr * base, int power, gfc_expr * result)
 {
-  mpf_t temp_r, temp_i, a;
+  mpfr_t re, im, a;
 
-  mpf_set_ui (result->value.complex.r, 1);
-  mpf_set_ui (result->value.complex.i, 0);
+  gfc_set_model (base->value.complex.r);
+  mpfr_init (re);
+  mpfr_init (im);
+  mpfr_init (a);
 
-  mpf_init (temp_r);
-  mpf_init (temp_i);
-  mpf_init (a);
+  mpfr_set_ui (result->value.complex.r, 1, GFC_RND_MODE);
+  mpfr_set_ui (result->value.complex.i, 0, GFC_RND_MODE);
 
   for (; power > 0; power--)
     {
-      mpf_mul (temp_r, base->value.complex.r, result->value.complex.r);
-      mpf_mul (a, base->value.complex.i, result->value.complex.i);
-      mpf_sub (temp_r, temp_r, a);
-
-      mpf_mul (temp_i, base->value.complex.r, result->value.complex.i);
-      mpf_mul (a, base->value.complex.i, result->value.complex.r);
-      mpf_add (temp_i, temp_i, a);
-
-      mpf_set (result->value.complex.r, temp_r);
-      mpf_set (result->value.complex.i, temp_i);
+      mpfr_mul (re, base->value.complex.r, result->value.complex.r,
+                GFC_RND_MODE);
+      mpfr_mul (a, base->value.complex.i, result->value.complex.i,
+                GFC_RND_MODE);
+      mpfr_sub (re, re, a, GFC_RND_MODE);
+
+      mpfr_mul (im, base->value.complex.r, result->value.complex.i,
+                GFC_RND_MODE);
+      mpfr_mul (a, base->value.complex.i, result->value.complex.r,
+                GFC_RND_MODE);
+      mpfr_add (im, im, a, GFC_RND_MODE);
+
+      mpfr_set (result->value.complex.r, re, GFC_RND_MODE);
+      mpfr_set (result->value.complex.i, im, GFC_RND_MODE);
     }
 
-  mpf_clear (temp_r);
-  mpf_clear (temp_i);
-  mpf_clear (a);
+  mpfr_clear (re);
+  mpfr_clear (im);
+  mpfr_clear (a);
 }
 
 
@@ -1547,7 +951,7 @@ gfc_arith_power (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
   int power, apower;
   gfc_expr *result;
   mpz_t unity_z;
-  mpf_t unity_f;
+  mpfr_t unity_f;
   arith rc;
 
   rc = ARITH_OK;
@@ -1558,43 +962,30 @@ gfc_arith_power (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
   result = gfc_constant_result (op1->ts.type, op1->ts.kind, &op1->where);
 
   if (power == 0)
-    {                          /* Handle something to the zeroth power */
+    {
+      /* Handle something to the zeroth power.  Since we're dealing
+        with integral exponents, there is no ambiguity in the
+        limiting procedure used to determine the value of 0**0.  */
       switch (op1->ts.type)
        {
        case BT_INTEGER:
-         if (mpz_sgn (op1->value.integer) == 0)
-           rc = ARITH_0TO0;
-         else
-           mpz_set_ui (result->value.integer, 1);
-
+         mpz_set_ui (result->value.integer, 1);
          break;
 
        case BT_REAL:
-         if (mpf_sgn (op1->value.real) == 0)
-           rc = ARITH_0TO0;
-         else
-           mpf_set_ui (result->value.real, 1);
-
+         mpfr_set_ui (result->value.real, 1, GFC_RND_MODE);
          break;
 
        case BT_COMPLEX:
-         if (mpf_sgn (op1->value.complex.r) == 0
-             && mpf_sgn (op1->value.complex.i) == 0)
-           rc = ARITH_0TO0;
-         else
-           {
-             mpf_set_ui (result->value.complex.r, 1);
-             mpf_set_ui (result->value.complex.r, 0);
-           }
-
+         mpfr_set_ui (result->value.complex.r, 1, GFC_RND_MODE);
+         mpfr_set_ui (result->value.complex.i, 0, GFC_RND_MODE);
          break;
 
        default:
          gfc_internal_error ("gfc_arith_power(): Bad base");
        }
     }
-
-  if (power != 0)
+  else
     {
       apower = power;
       if (power < 0)
@@ -1616,22 +1007,24 @@ gfc_arith_power (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
          break;
 
        case BT_REAL:
-         mpf_pow_ui (result->value.real, op1->value.real, apower);
+         mpfr_pow_ui (result->value.real, op1->value.real, apower,
+                       GFC_RND_MODE);
 
          if (power < 0)
            {
-             mpf_init_set_ui (unity_f, 1);
-             mpf_div (result->value.real, unity_f, result->value.real);
-             mpf_clear (unity_f);
+              gfc_set_model (op1->value.real);
+             mpfr_init (unity_f);
+             mpfr_set_ui (unity_f, 1, GFC_RND_MODE);
+             mpfr_div (result->value.real, unity_f, result->value.real,
+                        GFC_RND_MODE);
+             mpfr_clear (unity_f);
            }
-
          break;
 
        case BT_COMPLEX:
          complex_pow_ui (op1, apower, result);
          if (power < 0)
            complex_reciprocal (result);
-
          break;
 
        default:
@@ -1642,12 +1035,7 @@ gfc_arith_power (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
   if (rc == ARITH_OK)
     rc = gfc_range_check (result);
 
-  if (rc != ARITH_OK)
-    gfc_free_expr (result);
-  else
-    *resultp = result;
-
-  return rc;
+  return check_result (rc, op1, result, resultp);
 }
 
 
@@ -1659,7 +1047,7 @@ gfc_arith_concat (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
   gfc_expr *result;
   int len;
 
-  result = gfc_constant_result (BT_CHARACTER, gfc_default_character_kind (),
+  result = gfc_constant_result (BT_CHARACTER, gfc_default_character_kind,
                                &op1->where);
 
   len = op1->value.character.length + op2->value.character.length;
@@ -1696,7 +1084,7 @@ gfc_compare_expr (gfc_expr * op1, gfc_expr * op2)
       break;
 
     case BT_REAL:
-      rc = mpf_cmp (op1->value.real, op2->value.real);
+      rc = mpfr_cmp (op1->value.real, op2->value.real);
       break;
 
     case BT_CHARACTER:
@@ -1722,9 +1110,8 @@ gfc_compare_expr (gfc_expr * op1, gfc_expr * op2)
 static int
 compare_complex (gfc_expr * op1, gfc_expr * op2)
 {
-
-  return (mpf_cmp (op1->value.complex.r, op2->value.complex.r) == 0
-         && mpf_cmp (op1->value.complex.i, op2->value.complex.i) == 0);
+  return (mpfr_cmp (op1->value.complex.r, op2->value.complex.r) == 0
+         && mpfr_cmp (op1->value.complex.i, op2->value.complex.i) == 0);
 }
 
 
@@ -1773,7 +1160,7 @@ gfc_arith_eq (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 {
   gfc_expr *result;
 
-  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind (),
+  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind,
                                &op1->where);
   result->value.logical = (op1->ts.type == BT_COMPLEX) ?
     compare_complex (op1, op2) : (gfc_compare_expr (op1, op2) == 0);
@@ -1788,7 +1175,7 @@ gfc_arith_ne (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 {
   gfc_expr *result;
 
-  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind (),
+  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind,
                                &op1->where);
   result->value.logical = (op1->ts.type == BT_COMPLEX) ?
     !compare_complex (op1, op2) : (gfc_compare_expr (op1, op2) != 0);
@@ -1803,7 +1190,7 @@ gfc_arith_gt (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 {
   gfc_expr *result;
 
-  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind (),
+  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind,
                                &op1->where);
   result->value.logical = (gfc_compare_expr (op1, op2) > 0);
   *resultp = result;
@@ -1817,7 +1204,7 @@ gfc_arith_ge (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 {
   gfc_expr *result;
 
-  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind (),
+  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind,
                                &op1->where);
   result->value.logical = (gfc_compare_expr (op1, op2) >= 0);
   *resultp = result;
@@ -1831,7 +1218,7 @@ gfc_arith_lt (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 {
   gfc_expr *result;
 
-  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind (),
+  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind,
                                &op1->where);
   result->value.logical = (gfc_compare_expr (op1, op2) < 0);
   *resultp = result;
@@ -1845,7 +1232,7 @@ gfc_arith_le (gfc_expr * op1, gfc_expr * op2, gfc_expr ** resultp)
 {
   gfc_expr *result;
 
-  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind (),
+  result = gfc_constant_result (BT_LOGICAL, gfc_default_logical_kind,
                                &op1->where);
   result->value.logical = (gfc_compare_expr (op1, op2) <= 0);
   *resultp = result;
@@ -2043,7 +1430,6 @@ reduce_binary (arith (*eval) (gfc_expr *, gfc_expr *, gfc_expr **),
               gfc_expr * op1, gfc_expr * op2,
               gfc_expr ** result)
 {
-
   if (op1->expr_type == EXPR_CONSTANT && op2->expr_type == EXPR_CONSTANT)
     return eval (op1, op2, result);
 
@@ -2091,7 +1477,7 @@ eval_intrinsic (gfc_intrinsic_op operator,
        goto runtime;
 
       temp.ts.type = BT_LOGICAL;
-      temp.ts.kind = gfc_default_logical_kind ();
+      temp.ts.kind = gfc_default_logical_kind;
 
       unary = 1;
       break;
@@ -2105,7 +1491,7 @@ eval_intrinsic (gfc_intrinsic_op operator,
        goto runtime;
 
       temp.ts.type = BT_LOGICAL;
-      temp.ts.kind = gfc_default_logical_kind ();
+      temp.ts.kind = gfc_default_logical_kind;
 
       unary = 0;
       break;
@@ -2127,7 +1513,7 @@ eval_intrinsic (gfc_intrinsic_op operator,
       if (op1->ts.type == BT_COMPLEX || op2->ts.type == BT_COMPLEX)
        {
          temp.ts.type = BT_LOGICAL;
-         temp.ts.kind = gfc_default_logical_kind();
+         temp.ts.kind = gfc_default_logical_kind;
          goto runtime;
        }
 
@@ -2139,7 +1525,7 @@ eval_intrinsic (gfc_intrinsic_op operator,
        {
          unary = 0;
          temp.ts.type = BT_LOGICAL;
-         temp.ts.kind = gfc_default_logical_kind();
+         temp.ts.kind = gfc_default_logical_kind;
          break;
        }
 
@@ -2157,10 +1543,10 @@ eval_intrinsic (gfc_intrinsic_op operator,
 
       temp.expr_type = EXPR_OP;
       gfc_clear_ts (&temp.ts);
-      temp.operator = operator;
+      temp.value.op.operator = operator;
 
-      temp.op1 = op1;
-      temp.op2 = op2;
+      temp.value.op.op1 = op1;
+      temp.value.op.op2 = op2;
 
       gfc_type_convert_binary (&temp);
 
@@ -2169,7 +1555,7 @@ eval_intrinsic (gfc_intrinsic_op operator,
          || operator == INTRINSIC_LE || operator == INTRINSIC_LT)
        {
          temp.ts.type = BT_LOGICAL;
-         temp.ts.kind = gfc_default_logical_kind ();
+         temp.ts.kind = gfc_default_logical_kind;
        }
 
       unary = 0;
@@ -2180,7 +1566,7 @@ eval_intrinsic (gfc_intrinsic_op operator,
        goto runtime;
 
       temp.ts.type = BT_CHARACTER;
-      temp.ts.kind = gfc_default_character_kind ();
+      temp.ts.kind = gfc_default_character_kind;
 
       unary = 0;
       break;
@@ -2230,10 +1616,10 @@ runtime:
   result->ts = temp.ts;
 
   result->expr_type = EXPR_OP;
-  result->operator = operator;
+  result->value.op.operator = operator;
 
-  result->op1 = op1;
-  result->op2 = op2;
+  result->value.op.op1 = op1;
+  result->value.op.op2 = op2;
 
   result->where = op1->where;
 
@@ -2246,9 +1632,9 @@ static gfc_expr *
 eval_type_intrinsic0 (gfc_intrinsic_op operator, gfc_expr *op)
 {
   if (op == NULL)
-    gfc_internal_error("eval_type_intrinsic0(): op NULL");
+    gfc_internal_error ("eval_type_intrinsic0(): op NULL");
 
-  switch(operator)
+  switch (operator)
     {
     case INTRINSIC_GE:
     case INTRINSIC_LT:
@@ -2257,7 +1643,7 @@ eval_type_intrinsic0 (gfc_intrinsic_op operator, gfc_expr *op)
     case INTRINSIC_EQ:
     case INTRINSIC_NE:
       op->ts.type = BT_LOGICAL;
-      op->ts.kind = gfc_default_logical_kind();
+      op->ts.kind = gfc_default_logical_kind;
       break;
 
     default:
@@ -2273,7 +1659,6 @@ eval_type_intrinsic0 (gfc_intrinsic_op operator, gfc_expr *op)
 static int
 gfc_zero_size_array (gfc_expr * e)
 {
-
   if (e->expr_type != EXPR_ARRAY)
     return 0;
 
@@ -2288,7 +1673,6 @@ gfc_zero_size_array (gfc_expr * e)
 static gfc_expr *
 reduce_binary0 (gfc_expr * op1, gfc_expr * op2)
 {
-
   if (gfc_zero_size_array (op1))
     {
       gfc_free_expr (op2);
@@ -2316,13 +1700,13 @@ eval_intrinsic_f2 (gfc_intrinsic_op operator,
   if (op2 == NULL)
     {
       if (gfc_zero_size_array (op1))
-       return eval_type_intrinsic0(operator, op1);
+       return eval_type_intrinsic0 (operator, op1);
     }
   else
     {
       result = reduce_binary0 (op1, op2);
       if (result != NULL)
-       return eval_type_intrinsic0(operator, result);
+       return eval_type_intrinsic0 (operator, result);
     }
 
   f.f2 = eval;
@@ -2489,15 +1873,9 @@ gfc_expr *
 gfc_convert_real (const char *buffer, int kind, locus * where)
 {
   gfc_expr *e;
-  const char *t;
 
   e = gfc_constant_result (BT_REAL, kind, where);
-  /* a leading plus is allowed, but not by mpf_set_str */
-  if (buffer[0] == '+')
-    t = buffer + 1;
-  else
-    t = buffer;
-  mpf_set_str (e->value.real, t, 10);
+  mpfr_set_str (e->value.real, buffer, 10, GFC_RND_MODE);
 
   return e;
 }
@@ -2512,8 +1890,8 @@ gfc_convert_complex (gfc_expr * real, gfc_expr * imag, int kind)
   gfc_expr *e;
 
   e = gfc_constant_result (BT_COMPLEX, kind, &real->where);
-  mpf_set (e->value.complex.r, real->value.real);
-  mpf_set (e->value.complex.i, imag->value.real);
+  mpfr_set (e->value.complex.r, real->value.real, GFC_RND_MODE);
+  mpfr_set (e->value.complex.i, imag->value.real, GFC_RND_MODE);
 
   return e;
 }
@@ -2527,12 +1905,11 @@ gfc_convert_complex (gfc_expr * real, gfc_expr * imag, int kind)
 static void
 arith_error (arith rc, gfc_typespec * from, gfc_typespec * to, locus * where)
 {
-
   gfc_error ("%s converting %s to %s at %L", gfc_arith_error (rc),
             gfc_typename (from), gfc_typename (to), where);
 
-  /* TODO: Do something about the error, ie underflow rounds to 0,
-     throw exception, return NaN, etc.  */
+  /* TODO: Do something about the error, ie, throw exception, return
+     NaN, etc.  */
 }
 
 /* Convert integers to integers.  */
@@ -2550,9 +1927,16 @@ gfc_int2int (gfc_expr * src, int kind)
   if ((rc = gfc_check_integer_range (result->value.integer, kind))
       != ARITH_OK)
     {
-      arith_error (rc, &src->ts, &result->ts, &src->where);
-      gfc_free_expr (result);
-      return NULL;
+      if (rc == ARITH_ASYMMETRIC)
+        {
+          gfc_warning ("%s at %L", gfc_arith_error (rc), &src->where);
+        }
+      else
+        {
+          arith_error (rc, &src->ts, &result->ts, &src->where);
+          gfc_free_expr (result);
+          return NULL;
+        }
     }
 
   return result;
@@ -2569,7 +1953,7 @@ gfc_int2real (gfc_expr * src, int kind)
 
   result = gfc_constant_result (BT_REAL, kind, &src->where);
 
-  mpf_set_z (result->value.real, src->value.integer);
+  mpfr_set_z (result->value.real, src->value.integer, GFC_RND_MODE);
 
   if ((rc = gfc_check_real_range (result->value.real, kind)) != ARITH_OK)
     {
@@ -2592,10 +1976,10 @@ gfc_int2complex (gfc_expr * src, int kind)
 
   result = gfc_constant_result (BT_COMPLEX, kind, &src->where);
 
-  mpf_set_z (result->value.complex.r, src->value.integer);
-  mpf_set_ui (result->value.complex.i, 0);
+  mpfr_set_z (result->value.complex.r, src->value.integer, GFC_RND_MODE);
+  mpfr_set_ui (result->value.complex.i, 0, GFC_RND_MODE);
 
-  if ((rc = gfc_check_real_range (result->value.complex.i, kind)) != ARITH_OK)
+  if ((rc = gfc_check_real_range (result->value.complex.r, kind)) != ARITH_OK)
     {
       arith_error (rc, &src->ts, &result->ts, &src->where);
       gfc_free_expr (result);
@@ -2616,7 +2000,7 @@ gfc_real2int (gfc_expr * src, int kind)
 
   result = gfc_constant_result (BT_INTEGER, kind, &src->where);
 
-  mpz_set_f (result->value.integer, src->value.real);
+  gfc_mpfr_to_mpz (result->value.integer, src->value.real);
 
   if ((rc = gfc_check_integer_range (result->value.integer, kind))
       != ARITH_OK)
@@ -2640,9 +2024,17 @@ gfc_real2real (gfc_expr * src, int kind)
 
   result = gfc_constant_result (BT_REAL, kind, &src->where);
 
-  mpf_set (result->value.real, src->value.real);
+  mpfr_set (result->value.real, src->value.real, GFC_RND_MODE);
 
-  if ((rc = gfc_check_real_range (result->value.real, kind)) != ARITH_OK)
+  rc = gfc_check_real_range (result->value.real, kind);
+
+  if (rc == ARITH_UNDERFLOW)
+    {
+      if (gfc_option.warn_underflow)
+        gfc_warning ("%s at %L", gfc_arith_error (rc), &src->where);
+      mpfr_set_ui (result->value.real, 0, GFC_RND_MODE);
+    }
+  else if (rc != ARITH_OK)
     {
       arith_error (rc, &src->ts, &result->ts, &src->where);
       gfc_free_expr (result);
@@ -2663,10 +2055,18 @@ gfc_real2complex (gfc_expr * src, int kind)
 
   result = gfc_constant_result (BT_COMPLEX, kind, &src->where);
 
-  mpf_set (result->value.complex.r, src->value.real);
-  mpf_set_ui (result->value.complex.i, 0);
+  mpfr_set (result->value.complex.r, src->value.real, GFC_RND_MODE);
+  mpfr_set_ui (result->value.complex.i, 0, GFC_RND_MODE);
+
+  rc = gfc_check_real_range (result->value.complex.r, kind);
 
-  if ((rc = gfc_check_real_range (result->value.complex.i, kind)) != ARITH_OK)
+  if (rc == ARITH_UNDERFLOW)
+    {
+      if (gfc_option.warn_underflow)
+        gfc_warning ("%s at %L", gfc_arith_error (rc), &src->where);
+      mpfr_set_ui (result->value.complex.r, 0, GFC_RND_MODE);
+    }
+  else if (rc != ARITH_OK)
     {
       arith_error (rc, &src->ts, &result->ts, &src->where);
       gfc_free_expr (result);
@@ -2687,7 +2087,7 @@ gfc_complex2int (gfc_expr * src, int kind)
 
   result = gfc_constant_result (BT_INTEGER, kind, &src->where);
 
-  mpz_set_f (result->value.integer, src->value.complex.r);
+  gfc_mpfr_to_mpz (result->value.integer, src->value.complex.r);
 
   if ((rc = gfc_check_integer_range (result->value.integer, kind))
       != ARITH_OK)
@@ -2711,9 +2111,17 @@ gfc_complex2real (gfc_expr * src, int kind)
 
   result = gfc_constant_result (BT_REAL, kind, &src->where);
 
-  mpf_set (result->value.real, src->value.complex.r);
+  mpfr_set (result->value.real, src->value.complex.r, GFC_RND_MODE);
 
-  if ((rc = gfc_check_real_range (result->value.real, kind)) != ARITH_OK)
+  rc = gfc_check_real_range (result->value.real, kind);
+
+  if (rc == ARITH_UNDERFLOW)
+    {
+      if (gfc_option.warn_underflow)
+        gfc_warning ("%s at %L", gfc_arith_error (rc), &src->where);
+      mpfr_set_ui (result->value.real, 0, GFC_RND_MODE);
+    }
+  if (rc != ARITH_OK)
     {
       arith_error (rc, &src->ts, &result->ts, &src->where);
       gfc_free_expr (result);
@@ -2734,12 +2142,33 @@ gfc_complex2complex (gfc_expr * src, int kind)
 
   result = gfc_constant_result (BT_COMPLEX, kind, &src->where);
 
-  mpf_set (result->value.complex.r, src->value.complex.r);
-  mpf_set (result->value.complex.i, src->value.complex.i);
+  mpfr_set (result->value.complex.r, src->value.complex.r, GFC_RND_MODE);
+  mpfr_set (result->value.complex.i, src->value.complex.i, GFC_RND_MODE);
+
+  rc = gfc_check_real_range (result->value.complex.r, kind);
 
-  if ((rc = gfc_check_real_range (result->value.complex.r, kind)) != ARITH_OK
-      || (rc =
-         gfc_check_real_range (result->value.complex.i, kind)) != ARITH_OK)
+  if (rc == ARITH_UNDERFLOW)
+    {
+      if (gfc_option.warn_underflow)
+        gfc_warning ("%s at %L", gfc_arith_error (rc), &src->where);
+      mpfr_set_ui (result->value.complex.r, 0, GFC_RND_MODE);
+    }
+  else if (rc != ARITH_OK)
+    {
+      arith_error (rc, &src->ts, &result->ts, &src->where);
+      gfc_free_expr (result);
+      return NULL;
+    }
+
+  rc = gfc_check_real_range (result->value.complex.i, kind);
+
+  if (rc == ARITH_UNDERFLOW)
+    {
+      if (gfc_option.warn_underflow)
+        gfc_warning ("%s at %L", gfc_arith_error (rc), &src->where);
+      mpfr_set_ui (result->value.complex.i, 0, GFC_RND_MODE);
+    }
+  else if (rc != ARITH_OK)
     {
       arith_error (rc, &src->ts, &result->ts, &src->where);
       gfc_free_expr (result);