PR libfortran/64770 Segfault when trying to open existing file with status="new".
[gcc.git] / libgfortran / io / write_float.def
index a157f0b63287d187980378bb61bc9679963d27f7..1bcd8159a22085f064db98dea32a2fcf82016c85 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2013 Free Software Foundation, Inc.
+/* Copyright (C) 2007-2015 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
@@ -125,8 +125,6 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
   int nzero;
   /* Number of digits after the decimal point.  */
   int nafter;
-  /* Number of zeros after the decimal point, whatever the precision.  */
-  int nzero_real;
   int leadzero;
   int nblanks;
   int ndigits, edigits;
@@ -138,7 +136,6 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
   p = dtp->u.p.scale_factor;
 
   rchar = '5';
-  nzero_real = -1;
 
   /* We should always know the field width and precision.  */
   if (d < 0)
@@ -191,7 +188,7 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
              if (nafter < 0)
                nafter = 0;
              nafter = d;
-             nzero = nzero_real = 0;
+             nzero = 0;
            }
          else /* p < 0  */
            {
@@ -211,14 +208,13 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
                  nafter = d + nbefore;
                  nbefore = 0;
                }
-             nzero_real = nzero;
              if (nzero > d)
                nzero = d;
            }
        }
       else
        {
-         nzero = nzero_real = 0;
+         nzero = 0;
          nafter = d;
        }
 
@@ -373,7 +369,7 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
   updown:
 
   rchar = '0';
-  if (w > 0 && d == 0 && p == 0)
+  if (ft != FMT_F && w > 0 && d == 0 && p == 0)
     nbefore = 1;
   /* Scan for trailing zeros to see if we really need to round it.  */
   for(i = nbefore + nafter; i < ndigits; i++)
@@ -386,13 +382,14 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
   do_rnd:
  
   if (nbefore + nafter == 0)
+    /* Handle the case Fw.0 and value < 1.0 */
     {
       ndigits = 0;
-      if (nzero_real == d && digits[0] >= rchar)
+      if (digits[0] >= rchar)
        {
          /* We rounded to zero but shouldn't have */
-         nzero--;
-         nafter = 1;
+         nbefore = 1;
+         digits--;
          digits[0] = '1';
          ndigits = 1;
        }
@@ -961,6 +958,34 @@ __qmath_(quadmath_snprintf) (buffer, size, "%+-#.*Qf", \
 #endif
 
 
+#if defined(GFC_REAL_16_IS_FLOAT128)
+#define ISFINITE2Q(val) finiteq(val)
+#endif
+#define ISFINITE2(val) isfinite(val)
+#define ISFINITE2L(val) isfinite(val)
+
+#define ISFINITE(suff,val) TOKENPASTE(ISFINITE2,suff)(val)
+
+
+#if defined(GFC_REAL_16_IS_FLOAT128)
+#define SIGNBIT2Q(val) signbitq(val)
+#endif
+#define SIGNBIT2(val) signbit(val)
+#define SIGNBIT2L(val) signbit(val)
+
+#define SIGNBIT(suff,val) TOKENPASTE(SIGNBIT2,suff)(val)
+
+
+#if defined(GFC_REAL_16_IS_FLOAT128)
+#define ISNAN2Q(val) isnanq(val)
+#endif
+#define ISNAN2(val) isnan(val)
+#define ISNAN2L(val) isnan(val)
+
+#define ISNAN(suff,val) TOKENPASTE(ISNAN2,suff)(val)
+
+
+
 /* Generate corresponding I/O format for FMT_G and output.
    The rules to translate FMT_G to FMT_E or FMT_F from DEC fortran
    LRM (table 11-2, Chapter 11, "I/O Formatting", P11-25) is:
@@ -990,13 +1015,14 @@ output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
   int d = f->u.real.d;\
   int w = f->u.real.w;\
   fnode newf;\
-  GFC_REAL_ ## x rexp_d, r = 0.5;\
+  GFC_REAL_ ## x exp_d, r = 0.5, r_sc;\
   int low, high, mid;\
   int ubound, lbound;\
   char *p, pad = ' ';\
   int save_scale_factor, nb = 0;\
   bool result;\
   int nprinted, precision;\
+  volatile GFC_REAL_ ## x temp;\
 \
   save_scale_factor = dtp->u.p.scale_factor;\
 \
@@ -1015,10 +1041,13 @@ output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
        break;\
     }\
 \
-  rexp_d = calculate_exp_ ## x (-d);\
-  if ((m > 0.0 && ((m < 0.1 - 0.1 * r * rexp_d) || (rexp_d * (m + r) >= 1.0)))\
+  exp_d = calculate_exp_ ## x (d);\
+  r_sc = (1 - r / exp_d);\
+  temp = 0.1 * r_sc;\
+  if ((m > 0.0 && ((m < temp) || (r >= (exp_d - m))))\
       || ((m == 0.0) && !(compile_options.allow_std\
-                         & (GFC_STD_F2003 | GFC_STD_F2008))))\
+                         & (GFC_STD_F2003 | GFC_STD_F2008)))\
+      ||  d == 0)\
     { \
       newf.format = FMT_E;\
       newf.u.real.w = w;\
@@ -1038,10 +1067,9 @@ output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
 \
   while (low <= high)\
     { \
-      volatile GFC_REAL_ ## x temp;\
       mid = (low + high) / 2;\
 \
-      temp = (calculate_exp_ ## x (mid - 1) * (1 - r * rexp_d));\
+      temp = (calculate_exp_ ## x (mid - 1) * r_sc);\
 \
       if (m < temp)\
         { \
@@ -1121,14 +1149,36 @@ OUTPUT_FLOAT_FMT_G(16,L)
 /* EN format is tricky since the number of significant digits depends
    on the magnitude.  Solve it by first printing a temporary value and
    figure out the number of significant digits from the printed
-   exponent.  */
+   exponent.  Values y, 0.95*10.0**e <= y <10.0**e, are rounded to
+   10.0**e even when the final result will not be rounded to 10.0**e.
+   For these values the exponent returned by atoi has to be decremented
+   by one. The values y in the ranges
+       (1000.0-0.5*10.0**(-d))*10.0**(3*n) <= y < 10.0*(3*(n+1))  
+        (100.0-0.5*10.0**(-d))*10.0**(3*n) <= y < 10.0*(3*n+2)
+         (10.0-0.5*10.0**(-d))*10.0**(3*n) <= y < 10.0*(3*n+1)
+   are correctly rounded respectively to 1.0...0*10.0*(3*(n+1)),
+   100.0...0*10.0*(3*n), and 10.0...0*10.0*(3*n), where 0...0
+   represents d zeroes, by the lines 279 to 297. */
 
 #define EN_PREC(x,y)\
 {\
-    GFC_REAL_ ## x tmp;                                \
-    tmp = * (GFC_REAL_ ## x *)source;                          \
-    if (isfinite (tmp))                                                \
-      nprinted = DTOA(y,0,tmp);                                        \
+    volatile GFC_REAL_ ## x tmp, one = 1.0;\
+    tmp = * (GFC_REAL_ ## x *)source;\
+    if (ISFINITE (y,tmp))\
+      {\
+       nprinted = DTOA(y,0,tmp);\
+       int e = atoi (&buffer[4]);\
+       if (buffer[1] == '1')\
+         {\
+           tmp = (calculate_exp_ ## x (-e)) * tmp;\
+           tmp = one - (tmp < 0 ? -tmp : tmp); \
+           if (tmp > 0)\
+             e = e - 1;\
+         }\
+       nbefore = e%3;\
+       if (nbefore < 0)\
+         nbefore = 3 + nbefore;\
+      }\
     else\
       nprinted = -1;\
 }\
@@ -1140,6 +1190,7 @@ determine_en_precision (st_parameter_dt *dtp, const fnode *f,
   int nprinted;
   char buffer[10];
   const size_t size = 10;
+  int nbefore; /* digits before decimal point - 1.  */
 
   switch (len)
     {
@@ -1172,16 +1223,6 @@ determine_en_precision (st_parameter_dt *dtp, const fnode *f,
   if (nprinted == -1)
     return -1;
 
-  int e = atoi (&buffer[5]);
-  int nbefore; /* digits before decimal point - 1.  */
-  if (e >= 0)
-    nbefore = e % 3;
-  else
-    {
-      nbefore = (-e) % 3;
-      if (nbefore != 0)
-       nbefore = 3 - nbefore;
-    }
   int prec = f->u.real.d + nbefore;
   if (dtp->u.p.current_unit->round_status != ROUND_UNSPECIFIED
       && dtp->u.p.current_unit->round_status != ROUND_PROCDEFINED)
@@ -1194,10 +1235,10 @@ determine_en_precision (st_parameter_dt *dtp, const fnode *f,
 {\
        GFC_REAL_ ## x tmp;\
        tmp = * (GFC_REAL_ ## x *)source;\
-       sign_bit = signbit (tmp);\
-       if (!isfinite (tmp))\
+       sign_bit = SIGNBIT (y,tmp);\
+       if (!ISFINITE (y,tmp))\
          { \
-           write_infnan (dtp, f, isnan (tmp), sign_bit);\
+           write_infnan (dtp, f, ISNAN (y,tmp), sign_bit);\
            return;\
          }\
        tmp = sign_bit ? -tmp : tmp;\
@@ -1236,7 +1277,13 @@ write_float (st_parameter_dt *dtp, const fnode *f, const char *source, \
      trailing null, and finally some extra digits depending on the
      requested precision.  */
   const size_t size = 4932 + 3 + precision;
-  char buffer[size];
+#define BUF_STACK_SZ 5000
+  char buf_stack[BUF_STACK_SZ];
+  char *buffer;
+  if (size > BUF_STACK_SZ)
+     buffer = xmalloc (size);
+  else
+     buffer = buf_stack;
 
   switch (len)
     {
@@ -1265,4 +1312,6 @@ write_float (st_parameter_dt *dtp, const fnode *f, const char *source, \
     default:
       internal_error (NULL, "bad real kind");
     }
+  if (size > BUF_STACK_SZ)
+     free (buffer);
 }