This is the mail archive of the fortran@gcc.gnu.org mailing list for the GNU Fortran project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch, libgfortran][4.3 regression] PR32554 Bug in P formatting


:ADDPATCH fortran:

Hi all,

The initial problem here is that gfortran was truncating by one with snprintf and overflowing the buffer by one with sprintf. Surprising we were not getting an error before enabling snprintf. Using snprintf uncovered this latent bug.

Investigating further I found that the problem could be avoided by adjusting the calculation of the the number of digits, ndigits. I simply reduced this by one. However, examining the buffer after the calls to snprintf showed that the width was varying because the number of exponent digits, edigits, varied.

Exploring this, I tried fixing the size of edigits to see what happens. I discovered that edigits does not need to be calculated at all. If it is set to the largest expected size (4) snprintf (and sprintf) conveniently pads blanks at the end anyway. (The exponent is read back in downstream by atoi)

The result is seriously simplified code which should yield better performance.

Regression tested on x86-64-pc-Gnu/Linux.

Also NIST tested.

NOTE: I tested with sprintf and snprintf on my system by commenting out the conditional compiles to make sure both work OK.

I would prefer testing on other platforms such as Darwin. I will test on freebsd tonight.

OK for trunk?  We should probably back port this to 4.2 (though it does not
manifest) since it is a buffer overrun issue.

Regards,

Jerry

2007-06-30 Jerry DeLisle <jvdelisle@gcc.gnu.org>

	PR libgfortran/32554
	* io/write.c (output_float): Set edigits to a fixed size, avoiding
	variation in field width calculation and eliminate buffer overrun.
! { dg-do run }
! PR32554 Bug in P formatting
! Test case from the bug reporter
program gfcbug66
  real(8) :: x = 1.0e-100_8
  character(50) :: outstr
  write (outstr,'(1X,2E12.3)')    x, 2 * x
  if (outstr.ne."    0.100E-99   0.200E-99") call abort
  write (outstr,'(1X,1P,2E12.3)') x, 2 * x   ! Second printed number is wrong
  if (outstr.ne."    1.000-100   2.000-100") call abort
end program gfcbug66

Index: write.c
===================================================================
*** write.c	(revision 126131)
--- write.c	(working copy)
*************** output_float (st_parameter_dt *dtp, cons
*** 466,472 ****
    int nblanks;
    int i;
    sign_t sign;
-   double abslog;
  
    ft = f->format;
    w = f->u.real.w;
--- 466,471 ----
*************** output_float (st_parameter_dt *dtp, cons
*** 495,515 ****
  	value = value + 0.5;
      }
  
!   /* Printf always prints at least two exponent digits.  */
!   if (value == 0)
!     edigits = 2;
!   else
!     {
! #if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16)
!       abslog = fabs((double) log10l(value));
! #else
!       abslog = fabs(log10(value));
! #endif
!       if (abslog < 100)
! 	edigits = 2;
!       else
!         edigits = 1 + (int) log10(abslog);
!     }
  
    if (ft == FMT_F || ft == FMT_EN
        || ((ft == FMT_D || ft == FMT_E) && dtp->u.p.scale_factor != 0))
--- 494,502 ----
  	value = value + 0.5;
      }
  
!   /* printf pads blanks for us on the exponent so we just need it big enough
!      to handle the largest number of exponent digits expected.  */
!   edigits=4;
  
    if (ft == FMT_F || ft == FMT_EN
        || ((ft == FMT_D || ft == FMT_E) && dtp->u.p.scale_factor != 0))

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]