From a3f02fe44c80ce4f76b9f24cd17a4abbe412eb4b Mon Sep 17 00:00:00 2001
From: Jerry DeLisle <jvdelisle@gcc.gnu.org>
Date: Fri, 29 Apr 2011 14:56:02 +0000
Subject: [PATCH] re PR libfortran/48488 (Wrong default format for real
 numbers)

2011-04-29  Jerry DeLisle  <jvdelisle@gcc.gnu.org>
	    Janne Blomqvist  <jb@gcc.gnu.org>

	PR libgfortran/48488
	PR libgfortran/48602
	PR libgfortran/48615
	PR libgfortran/48684
	PR libgfortran/48787
	* io/write.c (write_d, write_e, write_f, write_en,
	write_es): Add precision compemsation parameter to call.
	(set_fnode_default): Adjust default widths to assure
	round trip on write and read. (write_real): Adjust call to write_float.
	(write_real_g0): Calculate compensation for extra precision and adjust
	call to write_float.
	* io/write_float.def (output_float_FMT_G_): Use volatile rather than
	asm volatile to avoid optimization issue. Correctly calculate the
	number of blanks (nb) to be appended and simplify calculation logic.
	(write_float): Increase MIN_FIELD_WIDTH by one to accomodate the new
	default widths. Eliminate the code that attempted to reduce the
	the precision used in later sprintf functions.  Add call parameter to
	compensate for extra precision.

Co-Authored-By: Janne Blomqvist <jb@gcc.gnu.org>

From-SVN: r173166
---
 libgfortran/ChangeLog          | 22 ++++++++++++
 libgfortran/io/write.c         | 36 ++++++++++++--------
 libgfortran/io/write_float.def | 62 +++++++++++-----------------------
 3 files changed, 63 insertions(+), 57 deletions(-)

diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog
index 11202c34eb8f..8a199fe2d154 100644
--- a/libgfortran/ChangeLog
+++ b/libgfortran/ChangeLog
@@ -1,3 +1,25 @@
+2011-04-29  Jerry DeLisle  <jvdelisle@gcc.gnu.org>
+	    Janne Blomqvist  <jb@gcc.gnu.org>
+
+	PR libgfortran/48488
+	PR libgfortran/48602
+	PR libgfortran/48615
+	PR libgfortran/48684
+	PR libgfortran/48787
+	* io/write.c (write_d, write_e, write_f, write_en,
+	write_es): Add precision compemsation parameter to call.
+	(set_fnode_default): Adjust default widths to assure
+	round trip on write and read. (write_real): Adjust call to write_float.
+	(write_real_g0): Calculate compensation for extra precision and adjust
+	call to write_float. 
+	* io/write_float.def (output_float_FMT_G_): Use volatile rather than
+	asm volatile to avoid optimization issue. Correctly calculate the
+	number of blanks (nb) to be appended and simplify calculation logic.
+	(write_float): Increase MIN_FIELD_WIDTH by one to accomodate the new
+	default widths. Eliminate the code that attempted to reduce the
+	the precision used in later sprintf functions.  Add call parameter to
+	compensate for extra precision.
+	
 2011-04-20  Jim Meyering  <meyering@redhat.com>
 
 	* intrinsics/move_alloc.c (move_alloc): Remove useless
diff --git a/libgfortran/io/write.c b/libgfortran/io/write.c
index 5338162bc6ce..bf02ad8b1aa0 100644
--- a/libgfortran/io/write.c
+++ b/libgfortran/io/write.c
@@ -1155,35 +1155,35 @@ write_z (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
 void
 write_d (st_parameter_dt *dtp, const fnode *f, const char *p, int len)
 {
-  write_float (dtp, f, p, len);
+  write_float (dtp, f, p, len, 0);
 }
 
 
 void
 write_e (st_parameter_dt *dtp, const fnode *f, const char *p, int len)
 {
-  write_float (dtp, f, p, len);
+  write_float (dtp, f, p, len, 0);
 }
 
 
 void
 write_f (st_parameter_dt *dtp, const fnode *f, const char *p, int len)
 {
-  write_float (dtp, f, p, len);
+  write_float (dtp, f, p, len, 0);
 }
 
 
 void
 write_en (st_parameter_dt *dtp, const fnode *f, const char *p, int len)
 {
-  write_float (dtp, f, p, len);
+  write_float (dtp, f, p, len, 0);
 }
 
 
 void
 write_es (st_parameter_dt *dtp, const fnode *f, const char *p, int len)
 {
-  write_float (dtp, f, p, len);
+  write_float (dtp, f, p, len, 0);
 }
 
 
@@ -1432,8 +1432,8 @@ set_fnode_default (st_parameter_dt *dtp, fnode *f, int length)
   switch (length)
     {
     case 4:
-      f->u.real.w = 15;
-      f->u.real.d = 8;
+      f->u.real.w = 16;
+      f->u.real.d = 9;
       f->u.real.e = 2;
       break;
     case 8:
@@ -1442,13 +1442,13 @@ set_fnode_default (st_parameter_dt *dtp, fnode *f, int length)
       f->u.real.e = 3;
       break;
     case 10:
-      f->u.real.w = 29;
-      f->u.real.d = 20;
+      f->u.real.w = 30;
+      f->u.real.d = 21;
       f->u.real.e = 4;
       break;
     case 16:
-      f->u.real.w = 44;
-      f->u.real.d = 35;
+      f->u.real.w = 45;
+      f->u.real.d = 36;
       f->u.real.e = 4;
       break;
     default:
@@ -1468,7 +1468,7 @@ write_real (st_parameter_dt *dtp, const char *source, int length)
   int org_scale = dtp->u.p.scale_factor;
   dtp->u.p.scale_factor = 1;
   set_fnode_default (dtp, &f, length);
-  write_float (dtp, &f, source , length);
+  write_float (dtp, &f, source , length, 1);
   dtp->u.p.scale_factor = org_scale;
 }
 
@@ -1476,12 +1476,20 @@ write_real (st_parameter_dt *dtp, const char *source, int length)
 void
 write_real_g0 (st_parameter_dt *dtp, const char *source, int length, int d)
 {
-  fnode f ;
+  fnode f;
+  int comp_d; 
   set_fnode_default (dtp, &f, length);
   if (d > 0)
     f.u.real.d = d;
+
+  /* Compensate for extra digits when using scale factor, d is not
+     specified, and the magnitude is such that E editing is used.  */
+  if (dtp->u.p.scale_factor > 0 && d == 0)
+    comp_d = 1;
+  else
+    comp_d = 0;
   dtp->u.p.g0_no_blanks = 1;
-  write_float (dtp, &f, source , length);
+  write_float (dtp, &f, source , length, comp_d);
   dtp->u.p.g0_no_blanks = 0;
 }
 
diff --git a/libgfortran/io/write_float.def b/libgfortran/io/write_float.def
index 2bced6ffec40..2e2b4d87bf47 100644
--- a/libgfortran/io/write_float.def
+++ b/libgfortran/io/write_float.def
@@ -289,8 +289,9 @@ output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
     }
   else if (nbefore + nafter < ndigits)
     {
-      ndigits = nbefore + nafter;
-      i = ndigits;
+      i = ndigits = nbefore + nafter;
+      if (d == 0 && digits[1] == '0')
+	goto skip;
       if (digits[i] >= rchar)
 	{
 	  /* Propagate the carry.  */
@@ -812,7 +813,8 @@ CALCULATE_EXP(16)
 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;\
@@ -850,7 +852,7 @@ output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
     { \
       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;\
@@ -864,11 +866,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 = (calculate_exp_ ## x (mid - 1) * (1 - r * rexp_d));\
-      asm volatile ("" : "+m" (temp));\
 \
       if (m < temp)\
         { \
@@ -894,22 +895,11 @@ output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
 	}\
     }\
 \
-  if (e > 4)\
-    e = 4;\
-  if (e < 0)\
-    nb = 4;\
-  else\
-    nb = e + 2;\
-\
-  nb = nb >= w ? 0 : nb;\
+  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:\
@@ -931,7 +921,7 @@ output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
 	  gfc_char4_t *p4 = (gfc_char4_t *) p;\
 	  memset4 (p4, pad, nb);\
 	}\
-      else\
+      else \
 	memset (p, pad, nb);\
     }\
 }\
@@ -1010,19 +1000,20 @@ __qmath_(quadmath_snprintf) (buffer, sizeof buffer, \
 			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 48
+# 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
@@ -1039,23 +1030,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)
     {
-- 
GitLab