This is the mail archive of the
fortran@gcc.gnu.org
mailing list for the GNU Fortran project.
[patch, libgfortran][4.3 regression] PR32554 Bug in P formatting
- From: Jerry DeLisle <jvdelisle at verizon dot net>
- To: Fortran List <fortran at gcc dot gnu dot org>
- Cc: gcc-patches <gcc-patches at gcc dot gnu dot org>
- Date: Sat, 30 Jun 2007 12:54:49 -0700
- Subject: [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))