OSDN Git Service

2011-05-01 Jerry DeLisle <jvdelisle@gcc.gnu.org>
[pf3gnuchains/gcc-fork.git] / libgfortran / io / write_float.def
index d51c8ed..7f3cedd 100644 (file)
@@ -1,33 +1,28 @@
-/* Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+/* Copyright (C) 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
    Contributed by Andy Vaught
    Write float code factoring to this file by Jerry DeLisle   
    F2003 I/O support contributed by Jerry DeLisle
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
-In addition to the permissions in the GNU General Public License, the
-Free Software Foundation gives you unlimited permission to link the
-compiled version of this file into combinations with other programs,
-and to distribute those combinations without any restriction coming
-from the use of this file.  (The General Public License restrictions
-do apply in other respects; for example, they cover modification of
-the file, and distribution when not linked into a combine
-executable.)
-
 Libgfortran 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 General Public License for more details.
 
-You should have received a copy of the GNU General Public License
-along with Libgfortran; see the file COPYING.  If not, write to
-the Free Software Foundation, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.  */
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 
@@ -66,17 +61,15 @@ calculate_sign (st_parameter_dt *dtp, int negative_flag)
 
 /* Output a real number according to its format which is FMT_G free.  */
 
-static void
+static try
 output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size, 
              int sign_bit, bool zero_flag, int ndigits, int edigits)
 {
   char *out;
   char *digits;
-  int e;
-  char expchar;
+  int e, w, d, p, i;
+  char expchar, rchar;
   format_token ft;
-  int w;
-  int d;
   /* Number of digits before the decimal point.  */
   int nbefore;
   /* Number of zeros after the decimal point.  */
@@ -87,13 +80,14 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
   int nzero_real;
   int leadzero;
   int nblanks;
-  int i;
   sign_t sign;
 
   ft = f->format;
   w = f->u.real.w;
   d = f->u.real.d;
+  p = dtp->u.p.scale_factor;
 
+  rchar = '5';
   nzero_real = -1;
 
   /* We should always know the field width and precision.  */
@@ -113,13 +107,7 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
 
   /* Make sure zero comes out as 0.0e0.   */
   if (zero_flag)
-    {
-      e = 0;
-      if (compile_options.sign_zero == 1)
-       sign = calculate_sign (dtp, sign_bit);
-      else
-       sign = calculate_sign (dtp, 0);
-    }
+    e = 0;
 
   /* Normalize the fractional component.  */
   buffer[2] = buffer[1];
@@ -129,7 +117,14 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
   switch (ft)
     {
     case FMT_F:
-      nbefore = e + dtp->u.p.scale_factor;
+      if (d == 0 && e <= 0 && p == 0)
+       {
+         memmove (digits + 1, digits, ndigits - 1);
+         digits[0] = '0';
+         e++;
+       }
+
+      nbefore = e + p;
       if (nbefore < 0)
        {
          nzero = -nbefore;
@@ -150,34 +145,34 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
     case FMT_E:
     case FMT_D:
       i = dtp->u.p.scale_factor;
-      if (d <= 0 && i == 0)
+      if (d <= 0 && p == 0)
        {
          generate_error (&dtp->common, LIBERROR_FORMAT, "Precision not "
                          "greater than zero in format specifier 'E' or 'D'");
-         return;
+         return FAILURE;
        }
-      if (i <= -d || i >= d + 2)
+      if (p <= -d || p >= d + 2)
        {
          generate_error (&dtp->common, LIBERROR_FORMAT, "Scale factor "
                          "out of range in format specifier 'E' or 'D'");
-         return;
+         return FAILURE;
        }
 
       if (!zero_flag)
-       e -= i;
-      if (i < 0)
+       e -= p;
+      if (p < 0)
        {
          nbefore = 0;
-         nzero = -i;
-         nafter = d + i;
+         nzero = -p;
+         nafter = d + p;
        }
-      else if (i > 0)
+      else if (p > 0)
        {
-         nbefore = i;
+         nbefore = p;
          nzero = 0;
-         nafter = (d - i) + 1;
+         nafter = (d - p) + 1;
        }
-      else /* i == 0 */
+      else /* p == 0 */
        {
          nbefore = 0;
          nzero = 0;
@@ -224,24 +219,82 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
       internal_error (&dtp->common, "Unexpected format token");
     }
 
-  /* Round the value.  */
+  if (zero_flag)
+    goto skip;
+  /* Round the value.  The value being rounded is an unsigned magnitude.
+     The ROUND_COMPATIBLE is rounding away from zero when there is a tie.  */
+  switch (dtp->u.p.current_unit->round_status)
+    {
+      case ROUND_ZERO: /* Do nothing and truncation occurs.  */
+       goto skip;
+      case ROUND_UP:
+       if (sign_bit)
+         goto skip;
+       rchar = '0';
+       /* Scan for trailing zeros to see if we really need to round it.  */
+       for(i = nbefore + nafter; i < ndigits; i++)
+         {
+           if (digits[i] != '0')
+             goto do_rnd;
+         }
+       goto skip;
+      case ROUND_DOWN:
+       if (!sign_bit)
+         goto skip;
+       rchar = '0';
+       break;
+      case ROUND_NEAREST:
+       /* Round compatible unless there is a tie. A tie is a 5 with
+          all trailing zero's.  */
+       i = nafter + nbefore;
+       if (digits[i] == '5')
+         {
+           for(i++ ; i < ndigits; i++)
+             {
+               if (digits[i] != '0')
+                 goto do_rnd;
+             }
+           /* It is a  tie so round to even.  */
+           switch (digits[nafter + nbefore - 1])
+             {
+               case '1':
+               case '3':
+               case '5':
+               case '7':
+               case '9':
+                 /* If odd, round away from zero to even.  */
+                 break;
+               default:
+                 /* If even, skip rounding, truncate to even.  */
+                 goto skip;
+             }
+         }
+        /* Fall through.  */ 
+      case ROUND_PROCDEFINED:
+      case ROUND_UNSPECIFIED:
+      case ROUND_COMPATIBLE:
+       rchar = '5';
+       /* Just fall through and do the actual rounding.  */
+    }
+    
+  do_rnd:
   if (nbefore + nafter == 0)
     {
       ndigits = 0;
-      if (nzero_real == d && digits[0] >= '5')
-        {
-          /* We rounded to zero but shouldn't have */
-          nzero--;
-          nafter = 1;
-          digits[0] = '1';
-          ndigits = 1;
-        }
+      if (nzero_real == d && digits[0] >= rchar)
+       {
+         /* We rounded to zero but shouldn't have */
+         nzero--;
+         nafter = 1;
+         digits[0] = '1';
+         ndigits = 1;
+       }
     }
   else if (nbefore + nafter < ndigits)
     {
-      ndigits = nbefore + nafter;
-      i = ndigits;
-      if (digits[i] >= '5')
+      i = ndigits = nbefore + nafter;
+      if (digits[i] >= rchar)
        {
          /* Propagate the carry.  */
          for (i--; i >= 0; i--)
@@ -256,9 +309,10 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
 
          if (i < 0)
            {
-             /* The carry overflowed.  Fortunately we have some spare space
-                at the start of the buffer.  We may discard some digits, but
-                this is ok because we already know they are zero.  */
+             /* The carry overflowed.  Fortunately we have some spare
+                space at the start of the buffer.  We may discard some
+                digits, but this is ok because we already know they are
+                zero.  */
              digits--;
              digits[0] = '1';
              if (ft == FMT_F)
@@ -286,6 +340,8 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
        }
     }
 
+  skip:
+
   /* Calculate the format of the exponent field.  */
   if (expchar)
     {
@@ -317,24 +373,21 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
   else
     edigits = 0;
 
-  /* Pick a field size if none was specified.  */
-  if (w <= 0)
-    w = nbefore + nzero + nafter + (sign != S_NONE ? 2 : 1);
-
-  /* Create the ouput buffer.  */
-  out = write_block (dtp, w);
-  if (out == NULL)
-    return;
-
-  /* Zero values always output as positive, even if the value was negative
-     before rounding.  */
+  /* Scan the digits string and count the number of zeros.  If we make it
+     all the way through the loop, we know the value is zero after the
+     rounding completed above.  */
   for (i = 0; i < ndigits; i++)
     {
       if (digits[i] != '0')
        break;
     }
+
+  /* To format properly, we need to know if the rounded result is zero and if
+     so, we set the zero_flag which may have been already set for
+     actual zero.  */
   if (i == ndigits)
     {
+      zero_flag = true;
       /* The output is zero, so set the sign according to the sign bit unless
         -fno-sign-zero was specified.  */
       if (compile_options.sign_zero == 1)
@@ -343,16 +396,46 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
        sign = calculate_sign (dtp, 0);
     }
 
+  /* Pick a field size if none was specified, taking into account small
+     values that may have been rounded to zero.  */
+  if (w <= 0)
+    {
+      if (zero_flag)
+       w = d + (sign != S_NONE ? 2 : 1) + (d == 0 ? 1 : 0);
+      else
+       {
+         w = nbefore + nzero + nafter + (sign != S_NONE ? 2 : 1);
+         w = w == 1 ? 2 : w;
+       }
+    }
+  
   /* Work out how much padding is needed.  */
   nblanks = w - (nbefore + nzero + nafter + edigits + 1);
   if (sign != S_NONE)
     nblanks--;
 
+  if (dtp->u.p.g0_no_blanks)
+    {
+      w -= nblanks;
+      nblanks = 0;
+    }
+
+  /* Create the ouput buffer.  */
+  out = write_block (dtp, w);
+  if (out == NULL)
+    return FAILURE;
+
   /* Check the value fits in the specified field width.  */
-  if (nblanks < 0 || edigits == -1)
+  if (nblanks < 0 || edigits == -1 || w == 1 || (w == 2 && sign != S_NONE))
     {
+      if (unlikely (is_char4_unit (dtp)))
+       {
+         gfc_char4_t *out4 = (gfc_char4_t *) out;
+         memset4 (out4, '*', w);
+         return FAILURE;
+       }
       star_fill (out, w);
-      return;
+      return FAILURE;
     }
 
   /* See if we have space for a zero before the decimal point.  */
@@ -364,6 +447,101 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
   else
     leadzero = 0;
 
+  /* For internal character(kind=4) units, we duplicate the code used for
+     regular output slightly modified.  This needs to be maintained
+     consistent with the regular code that follows this block.  */
+  if (unlikely (is_char4_unit (dtp)))
+    {
+      gfc_char4_t *out4 = (gfc_char4_t *) out;
+      /* Pad to full field width.  */
+
+      if ( ( nblanks > 0 ) && !dtp->u.p.no_leading_blank)
+       {
+         memset4 (out4, ' ', nblanks);
+         out4 += nblanks;
+       }
+
+      /* Output the initial sign (if any).  */
+      if (sign == S_PLUS)
+       *(out4++) = '+';
+      else if (sign == S_MINUS)
+       *(out4++) = '-';
+
+      /* Output an optional leading zero.  */
+      if (leadzero)
+       *(out4++) = '0';
+
+      /* Output the part before the decimal point, padding with zeros.  */
+      if (nbefore > 0)
+       {
+         if (nbefore > ndigits)
+           {
+             i = ndigits;
+             memcpy4 (out4, digits, i);
+             ndigits = 0;
+             while (i < nbefore)
+               out4[i++] = '0';
+           }
+         else
+           {
+             i = nbefore;
+             memcpy4 (out4, digits, i);
+             ndigits -= i;
+           }
+
+         digits += i;
+         out4 += nbefore;
+       }
+
+      /* Output the decimal point.  */
+      *(out4++) = dtp->u.p.current_unit->decimal_status
+                   == DECIMAL_POINT ? '.' : ',';
+
+      /* Output leading zeros after the decimal point.  */
+      if (nzero > 0)
+       {
+         for (i = 0; i < nzero; i++)
+           *(out4++) = '0';
+       }
+
+      /* Output digits after the decimal point, padding with zeros.  */
+      if (nafter > 0)
+       {
+         if (nafter > ndigits)
+           i = ndigits;
+         else
+           i = nafter;
+
+         memcpy4 (out4, digits, i);
+         while (i < nafter)
+           out4[i++] = '0';
+
+         digits += i;
+         ndigits -= i;
+         out4 += nafter;
+       }
+
+      /* Output the exponent.  */
+      if (expchar)
+       {
+         if (expchar != ' ')
+           {
+             *(out4++) = expchar;
+             edigits--;
+           }
+         snprintf (buffer, size, "%+0*d", edigits, e);
+         memcpy4 (out4, buffer, edigits);
+       }
+
+      if (dtp->u.p.no_leading_blank)
+       {
+         out4 += edigits;
+         memset4 (out4, ' ' , nblanks);
+         dtp->u.p.no_leading_blank = 0;
+       }
+      return SUCCESS;
+    } /* End of character(kind=4) internal unit code.  */
+
   /* Pad to full field width.  */
 
   if ( ( nblanks > 0 ) && !dtp->u.p.no_leading_blank)
@@ -403,11 +581,9 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
       digits += i;
       out += nbefore;
     }
+
   /* Output the decimal point.  */
-  if (dtp->common.flags & IOPARM_DT_HAS_F2003)
-    *(out++) = dtp->u.p.decimal_status == DECIMAL_POINT ? '.' : ',';
-  else
-    *(out++) = '.';
+  *(out++) = dtp->u.p.current_unit->decimal_status == DECIMAL_POINT ? '.' : ',';
 
   /* Output leading zeros after the decimal point.  */
   if (nzero > 0)
@@ -441,22 +617,21 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
          *(out++) = expchar;
          edigits--;
        }
-#if HAVE_SNPRINTF
       snprintf (buffer, size, "%+0*d", edigits, e);
-#else
-      sprintf (buffer, "%+0*d", edigits, e);
-#endif
       memcpy (out, buffer, edigits);
     }
+
   if (dtp->u.p.no_leading_blank)
     {
       out += edigits;
       memset( out , ' ' , nblanks );
       dtp->u.p.no_leading_blank = 0;
     }
+
 #undef STR
 #undef STR1
 #undef MIN_FIELD_WIDTH
+  return SUCCESS;
 }
 
 
@@ -467,69 +642,127 @@ write_infnan (st_parameter_dt *dtp, const fnode *f, int isnan_flag, int sign_bit
 {
   char * p, fin;
   int nb = 0;
+  sign_t sign;
+  int mark;
 
   if (f->format != FMT_B && f->format != FMT_O && f->format != FMT_Z)
     {
-         nb =  f->u.real.w;
-         
-         /* If the field width is zero, the processor must select a width 
-            not zero.  4 is chosen to allow output of '-Inf' or '+Inf' */
-            
-         if (nb == 0) nb = 4;
-         p = write_block (dtp, nb);
-          if (p == NULL)
-            return;
-         if (nb < 3)
+      sign = calculate_sign (dtp, sign_bit);
+      mark = (sign == S_PLUS || sign == S_MINUS) ? 8 : 7;
+
+      nb =  f->u.real.w;
+
+      /* If the field width is zero, the processor must select a width 
+        not zero.  4 is chosen to allow output of '-Inf' or '+Inf' */
+     
+      if ((nb == 0) || dtp->u.p.g0_no_blanks)
+       {
+         if (isnan_flag)
+           nb = 3;
+         else
+           nb = (sign == S_PLUS || sign == S_MINUS) ? 4 : 3;
+       }
+      p = write_block (dtp, nb);
+      if (p == NULL)
+       return;
+      if (nb < 3)
+       {
+         if (unlikely (is_char4_unit (dtp)))
            {
-             memset (p, '*',nb);
-             return;
+             gfc_char4_t *p4 = (gfc_char4_t *) p;
+             memset4 (p4, '*', nb);
            }
+         else
+           memset (p, '*', nb);
+         return;
+       }
+
+      if (unlikely (is_char4_unit (dtp)))
+       {
+         gfc_char4_t *p4 = (gfc_char4_t *) p;
+         memset4 (p4, ' ', nb);
+       }
+      else
+       memset(p, ' ', nb);
 
-         memset(p, ' ', nb);
-         if (!isnan_flag)
+      if (!isnan_flag)
+       {
+         if (sign_bit)
            {
-             if (sign_bit)
-               {
-               
-                 /* If the sign is negative and the width is 3, there is
-                    insufficient room to output '-Inf', so output asterisks */
-                    
-                 if (nb == 3)
-                   {
-                     memset (p, '*',nb);
-                     return;
-                   }
-                   
-                 /* The negative sign is mandatory */
-                   
-                 fin = '-';
-               }    
-             else
-             
-                 /* The positive sign is optional, but we output it for
-                    consistency */
-                 fin = '+';
-
-             if (nb > 8)
-             
-               /* We have room, so output 'Infinity' */
-               memcpy(p + nb - 8, "Infinity", 8);
+             /* If the sign is negative and the width is 3, there is
+                insufficient room to output '-Inf', so output asterisks */
+             if (nb == 3)
+               {
+                 if (unlikely (is_char4_unit (dtp)))
+                   {
+                     gfc_char4_t *p4 = (gfc_char4_t *) p;
+                     memset4 (p4, '*', nb);
+                   }
+                 else
+                   memset (p, '*', nb);
+                 return;
+               }
+             /* The negative sign is mandatory */
+             fin = '-';
+           }    
+         else
+           /* The positive sign is optional, but we output it for
+              consistency */
+           fin = '+';
+           
+         if (unlikely (is_char4_unit (dtp)))
+           {
+             gfc_char4_t *p4 = (gfc_char4_t *) p;
+
+             if (nb > mark)
+               /* We have room, so output 'Infinity' */
+               memcpy4 (p4 + nb - 8, "Infinity", 8);
              else
-             
-               /* For the case of width equals 8, there is not enough room
-                  for the sign and 'Infinity' so we go with 'Inf' */
-               memcpy(p + nb - 3, "Inf", 3);
+               /* For the case of width equals mark, there is not enough room
+                  for the sign and 'Infinity' so we go with 'Inf' */
+               memcpy4 (p4 + nb - 3, "Inf", 3);
 
+             if (sign == S_PLUS || sign == S_MINUS)
+               {
+                 if (nb < 9 && nb > 3)
+                   /* Put the sign in front of Inf */
+                   p4[nb - 4] = (gfc_char4_t) fin;
+                 else if (nb > 8)
+                   /* Put the sign in front of Infinity */
+                   p4[nb - 9] = (gfc_char4_t) fin;
+               }
+             return;
+           }
+
+         if (nb > mark)
+           /* We have room, so output 'Infinity' */
+           memcpy(p + nb - 8, "Infinity", 8);
+         else
+           /* For the case of width equals 8, there is not enough room
+              for the sign and 'Infinity' so we go with 'Inf' */
+           memcpy(p + nb - 3, "Inf", 3);
+
+         if (sign == S_PLUS || sign == S_MINUS)
+           {
              if (nb < 9 && nb > 3)
                p[nb - 4] = fin;  /* Put the sign in front of Inf */
              else if (nb > 8)
                p[nb - 9] = fin;  /* Put the sign in front of Infinity */
            }
+       }
+      else
+        {
+         if (unlikely (is_char4_unit (dtp)))
+           {
+             gfc_char4_t *p4 = (gfc_char4_t *) p;
+             memcpy4 (p4 + nb - 3, "NaN", 3);
+           }
          else
            memcpy(p + nb - 3, "NaN", 3);
-         return;
        }
+      return;
     }
+}
 
 
 /* Returns the value of 10**d.  */
@@ -574,34 +807,54 @@ CALCULATE_EXP(16)
    m >= 10**d-0.5                              Ew.d[Ee]
 
    notes: for Gw.d ,  n' ' means 4 blanks
-          for Gw.dEe, n' ' means e+2 blanks  */
+         for Gw.dEe, n' ' means e+2 blanks
+         for rounding modes adjustment, r, See Fortran F2008 10.7.5.2.2
+         the asm volatile is required for 32-bit x86 platforms.  */
 
 #define OUTPUT_FLOAT_FMT_G(x) \
 static void \
 output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
                      GFC_REAL_ ## x m, char *buffer, size_t size, \
-                     int sign_bit, bool zero_flag, int ndigits, int edigits) \
+                     int sign_bit, bool zero_flag, int ndigits, \
+                      int edigits, int comp_d) \
 { \
   int e = f->u.real.e;\
   int d = f->u.real.d;\
   int w = f->u.real.w;\
   fnode *newf;\
-  GFC_REAL_ ## x exp_d;\
+  GFC_REAL_ ## x rexp_d, r = 0.5;\
   int low, high, mid;\
   int ubound, lbound;\
-  char *p;\
+  char *p, pad = ' ';\
   int save_scale_factor, nb = 0;\
+  try result;\
 \
   save_scale_factor = dtp->u.p.scale_factor;\
-  newf = get_mem (sizeof (fnode));\
+  newf = (fnode *) get_mem (sizeof (fnode));\
+\
+  switch (dtp->u.p.current_unit->round_status)\
+    {\
+      case ROUND_ZERO:\
+       r = sign_bit ? 1.0 : 0.0;\
+       break;\
+      case ROUND_UP:\
+       r = 1.0;\
+       break;\
+      case ROUND_DOWN:\
+       r = 0.0;\
+       break;\
+      default:\
+       break;\
+    }\
 \
-  exp_d = calculate_exp_ ## x (d);\
-  if ((m > 0.0 && m < 0.1 - 0.05 / exp_d) || (m >= exp_d - 0.5 ) ||\
-      ((m == 0.0) && !(compile_options.allow_std & GFC_STD_F2003)))\
+  rexp_d = calculate_exp_ ## x (-d);\
+  if ((m > 0.0 && ((m < 0.1 - 0.1 * r * rexp_d) || (rexp_d * (m + r) >= 1.0)))\
+      || ((m == 0.0) && !(compile_options.allow_std\
+                         & (GFC_STD_F2003 | GFC_STD_F2008))))\
     { \
       newf->format = FMT_E;\
       newf->u.real.w = w;\
-      newf->u.real.d = d;\
+      newf->u.real.d = d - comp_d;\
       newf->u.real.e = e;\
       nb = 0;\
       goto finish;\
@@ -615,11 +868,10 @@ output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
 \
   while (low <= high)\
     { \
-      GFC_REAL_ ## x temp;\
+      volatile GFC_REAL_ ## x temp;\
       mid = (low + high) / 2;\
 \
-      temp = 0.1 * calculate_exp_ ## x (mid) - 0.5\
-            * calculate_exp_ ## x (mid - d - 1);\
+      temp = (calculate_exp_ ## x (mid - 1) * (1 - r * rexp_d));\
 \
       if (m < temp)\
         { \
@@ -639,37 +891,40 @@ output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
           low = mid + 1;\
         }\
       else\
-        break;\
+       {\
+         mid++;\
+         break;\
+       }\
     }\
 \
-  if (e < 0)\
-    nb = 4;\
-  else\
-    nb = e + 2;\
-\
+  nb = e <= 0 ? 4 : e + 2;\
+  nb = nb >= w ? w - 1 : nb;\
   newf->format = FMT_F;\
-  newf->u.real.w = f->u.real.w - nb;\
-\
-  if (m == 0.0)\
-    newf->u.real.d = d - 1;\
-  else\
-    newf->u.real.d = - (mid - d - 1);\
-\
+  newf->u.real.w = w - nb;\
+  newf->u.real.d = m == 0.0 ? d - 1 : -(mid - d - 1) ;\
   dtp->u.p.scale_factor = 0;\
 \
  finish:\
-  output_float (dtp, newf, buffer, size, sign_bit, zero_flag, ndigits, \
-               edigits);\
+  result = output_float (dtp, newf, buffer, size, sign_bit, zero_flag, \
+                        ndigits, edigits);\
   dtp->u.p.scale_factor = save_scale_factor;\
 \
-  free_mem(newf);\
+  free (newf);\
 \
-  if (nb > 0)\
-    { \
+  if (nb > 0 && !dtp->u.p.g0_no_blanks)\
+    {\
       p = write_block (dtp, nb);\
       if (p == NULL)\
        return;\
-      memset (p, ' ', nb);\
+      if (result == FAILURE)\
+        pad = '*';\
+      if (unlikely (is_char4_unit (dtp)))\
+       {\
+         gfc_char4_t *p4 = (gfc_char4_t *) p;\
+         memset4 (p4, pad, nb);\
+       }\
+      else \
+       memset (p, pad, nb);\
     }\
 }\
 
@@ -690,7 +945,7 @@ OUTPUT_FLOAT_FMT_G(16)
 
 /* Define a macro to build code for write_float.  */
 
-  /* Note: Before output_float is called, sprintf is used to print to buffer the
+  /* Note: Before output_float is called, snprintf is used to print to buffer the
      number in the format +D.DDDDe+ddd. For an N digit exponent, this gives us
      (MIN_FIELD_WIDTH-5)-N digits after the decimal point, plus another one
      before the decimal point.
@@ -711,8 +966,6 @@ OUTPUT_FLOAT_FMT_G(16)
        equal to the precision. The exponent always contains at least two
        digits; if the value is zero, the exponent is 00.  */
 
-#ifdef HAVE_SNPRINTF
-
 #define DTOA \
 snprintf (buffer, size, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
          "e", ndigits - 1, tmp);
@@ -721,16 +974,12 @@ snprintf (buffer, size, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
 snprintf (buffer, size, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
          "Le", ndigits - 1, tmp);
 
-#else
-
-#define DTOA \
-sprintf (buffer, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
-        "e", ndigits - 1, tmp);
-
-#define DTOAL \
-sprintf (buffer, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
-        "Le", ndigits - 1, tmp);
 
+#if defined(GFC_REAL_16_IS_FLOAT128)
+#define DTOAQ \
+__qmath_(quadmath_snprintf) (buffer, sizeof buffer, \
+                            "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
+                            "Qe", ndigits - 1, tmp);
 #endif
 
 #define WRITE_FLOAT(x,y)\
@@ -744,13 +993,6 @@ sprintf (buffer, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
            return;\
          }\
        tmp = sign_bit ? -tmp : tmp;\
-       if (f->u.real.d == 0 && f->format == FMT_F)\
-         {\
-           if (tmp < 0.5)\
-             tmp = 0.0;\
-           else if (tmp < 1.0)\
-             tmp = tmp + 0.5;\
-         }\
        zero_flag = (tmp == 0.0);\
 \
        DTOA ## y\
@@ -760,19 +1002,20 @@ sprintf (buffer, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
                        edigits);\
        else \
          output_float_FMT_G_ ## x (dtp, f, tmp, buffer, size, sign_bit, \
-                                   zero_flag, ndigits, edigits);\
+                                   zero_flag, ndigits, edigits, comp_d);\
 }\
 
 /* Output a real number according to its format.  */
 
 static void
-write_float (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
+write_float (st_parameter_dt *dtp, const fnode *f, const char *source, \
+            int len, int comp_d)
 {
 
-#if defined(HAVE_GFC_REAL_16) && __LDBL_DIG__ > 18
-# define MIN_FIELD_WIDTH 46
+#if defined(HAVE_GFC_REAL_16) || __LDBL_DIG__ > 18
+# define MIN_FIELD_WIDTH 49
 #else
-# define MIN_FIELD_WIDTH 31
+# define MIN_FIELD_WIDTH 32
 #endif
 #define STR(x) STR1(x)
 #define STR1(x) #x
@@ -789,23 +1032,8 @@ write_float (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
      to handle the largest number of exponent digits expected.  */
   edigits=4;
 
-  if (f->format == FMT_F || f->format == FMT_EN || f->format == FMT_G 
-      || ((f->format == FMT_D || f->format == FMT_E)
-      && dtp->u.p.scale_factor != 0))
-    {
-      /* Always convert at full precision to avoid double rounding.  */
-      ndigits = MIN_FIELD_WIDTH - 4 - edigits;
-    }
-  else
-    {
-      /* The number of digits is known, so let printf do the rounding.  */
-      if (f->format == FMT_ES)
-       ndigits = f->u.real.d + 1;
-      else
-       ndigits = f->u.real.d;
-      if (ndigits > MIN_FIELD_WIDTH - 4 - edigits)
-       ndigits = MIN_FIELD_WIDTH - 4 - edigits;
-    }
+  /* Always convert at full precision to avoid double rounding.  */
+    ndigits = MIN_FIELD_WIDTH - 4 - edigits;
 
   switch (len)
     {
@@ -824,7 +1052,11 @@ write_float (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
 #endif
 #ifdef HAVE_GFC_REAL_16
     case 16:
+# ifdef GFC_REAL_16_IS_FLOAT128
+      WRITE_FLOAT(16,Q)
+# else
       WRITE_FLOAT(16,L)
+# endif
       break;
 #endif
     default: