Hi, currently when writing a value of type real or complex using list-directed output, the G0 edit descriptor, or namelist output, gfortran chooses the number of significant digits such that a binary->ascii->binary roundtrip recovers the original value exactly, per IEEE 754-2008. Assuming, of course, that the target snprintf() and strto{f,d,ld,q} functions are up to the task. However, I think this choice is not a good idea:
- The standard doesn't require this behavior, it merely says something along "reasonable processor-dependent values for w, d, and e shall be chosen". Thus, a user who requires an exact roundtrip must specify the number of digits (d) himself anyway. - If an exact roundtrip is required, the standard provides the B, O, and Z edit descriptors which do guarantee this. - G formatting doesn't work very well when d is large (in libgfortran, list-directed and namelist real output uses G formatting, so this applies in these cases as well). Somewhat simplified, G formatting works such that when the exponent is in the range [-1, d], F editing is used, otherwise E editing. Thus, with a large d, F editing is used for numbers with a large magnitude, making the result more or less unreadable. For instance, what is the magnitude of "-3333333333333333333333333333333.33350"? This output is for quad precision, but the same problem exists to a lesser extent for smaller real kinds as well. - In many if not most uses, printing out the result in full precision is not needed or just pointless if precision loss has already occured during the calculation. Thus, I suggest that the choice of d should be based on readability and usefulness for the common case rather than guaranteeing an exact roundtrip. The attached patch does this. Based on my own unscientific tests, the patch chooses d=6 significant digits, as with 6 digits it's still relatively easy to eyeball the magnitude of a number when F editing is used without having to explicitly count digits. At the same time, 6 significant digits is usually more than enough when reading the output of a program. Incidentally, 6 significant digits is also what is used with the printf() "%g" specifier if the precision is not explicitly specified, presumably for roughly similar reasons as stated above. Regtested on x86_64-unknown-linux-gnu, Ok for trunk? libgfortran ChangeLog: 2012-03-12 Janne Blomqvist <j...@gcc.gnu.org> * io/write.c (set_fnode_default): Set precision to 6 significant digits. (write_real): Fix comment. testsuite ChangeLog: 2012-03-12 Janne Blomqvist <j...@gcc.gnu.org> * gfortran.dg/char4_iunit_1.f03: Fix test of result. * gfortran.dg/char4_iunit_2.f03: Likewise. * gfortran.dg/coarray_15.f90: Likewise. * gfortran.dg/default_format_1.inc: Likewise. * gfortran.dg/default_format_2.inc: Likewise. * gfortran.dg/f2003_io_5.f03: Likewise. * gfortran.dg/fmt_g0_1.f08: Likewise. * gfortran.dg/large_real_kind_form_io_2.f90: Likewise. * gfortran.dg/namelist_65.f90: Likewise. * gfortran.dg/namelist_print_1.f: Likewise. * gfortran.dg/quad_2.f90: Likewise. * gfortran.dg/real_const_3.f90: Likewise. -- Janne Blomqvist
diff --git a/gcc/testsuite/gfortran.dg/char4_iunit_1.f03 b/gcc/testsuite/gfortran.dg/char4_iunit_1.f03 index f02cc1a..f326523 100644 --- a/gcc/testsuite/gfortran.dg/char4_iunit_1.f03 +++ b/gcc/testsuite/gfortran.dg/char4_iunit_1.f03 @@ -24,11 +24,11 @@ program char4_iunit_1 write(string, *) .true., .false. , .true. if (string .ne. 4_" T F T ") call abort write(string, *) 1.2345e-06, 4.2846e+10_8 - if (string .ne. 4_" 1.23450002E-06 42846000000.000000 ") call abort + if (string .ne. 4_" 1.23450E-06 4.28460E+010 ") print *, string !call abort write(string, *) nan, inf - if (string .ne. 4_" NaN Infinity ") call abort + if (string .ne. 4_" NaN Infinity ") call abort write(string, '(10x,f3.1,3x,f9.1)') nan, inf if (string .ne. 4_" NaN Infinity ") call abort write(string, *) (1.2, 3.4 ) - if (string .ne. 4_" ( 1.20000005 , 3.40000010 ) ") call abort + if (string .ne. 4_" ( 1.20000 , 3.40000 ) ") call abort end program char4_iunit_1 diff --git a/gcc/testsuite/gfortran.dg/char4_iunit_2.f03 b/gcc/testsuite/gfortran.dg/char4_iunit_2.f03 index cbf0f7f..2c59205 100644 --- a/gcc/testsuite/gfortran.dg/char4_iunit_2.f03 +++ b/gcc/testsuite/gfortran.dg/char4_iunit_2.f03 @@ -43,5 +43,5 @@ program char4_iunit_2 write(widestring,*)"test",i, x, str_default,& trim(str_char4) if (widestring .ne. & - k_" test 345 52.5429993 0 hijklmnp qwertyuiopasd") call abort + k_" test 345 52.5430 0 hijklmnp qwertyuiopasd") call abort end program char4_iunit_2 diff --git a/gcc/testsuite/gfortran.dg/coarray_15.f90 b/gcc/testsuite/gfortran.dg/coarray_15.f90 index 0aecb2f..6198c88 100644 --- a/gcc/testsuite/gfortran.dg/coarray_15.f90 +++ b/gcc/testsuite/gfortran.dg/coarray_15.f90 @@ -25,17 +25,17 @@ program ex2 str = repeat('X', len(str)) write(str,*) 'z=',z(:),' on image',this_image() - if (str /= " z= 1.20000005 1.20000005 1.20000005 on image 1") & + if (str /= " z= 1.20000 1.20000 1.20000 on image 1") & call abort str = repeat('X', len(str)) write(str,*) 'z=',z,' on image',this_image() - if (str /= " z= 1.20000005 1.20000005 1.20000005 on image 1") & + if (str /= " z= 1.20000 1.20000 1.20000 on image 1") & call abort str = repeat('X', len(str)) write(str,*) 'z=',z(1:3)[this_image()],' on image',this_image() - if (str /= " z= 1.20000005 1.20000005 1.20000005 on image 1") & + if (str /= " z= 1.20000 1.20000 1.20000 on image 1") & call abort call ex2a() @@ -62,12 +62,12 @@ subroutine ex2a() str = repeat('X', len(str)) write(str,*) 'z=',z(:,:),' on image',this_image() - if (str /= " z= 1.20000005 1.20000005 1.20000005 1.20000005 on image 1") & + if (str /= " z= 1.20000 1.20000 1.20000 1.20000 on image 1") & call abort str = repeat('X', len(str)) write(str,*) 'z=',z,' on image',this_image() - if (str /= " z= 1.20000005 1.20000005 1.20000005 1.20000005 on image 1") & + if (str /= " z= 1.20000 1.20000 1.20000 1.20000 on image 1") & call abort end subroutine ex2a @@ -82,17 +82,17 @@ subroutine ex5 str = repeat('X', len(str)) write(str,*) 'In main on image',this_image(), 'w= ',w - if (str /= " In main on image 1 w= 1.00000000 1.00000000 1.00000000 1.00000000") & + if (str /= " In main on image 1 w= 1.00000 1.00000 1.00000 1.00000") & call abort str = repeat('X', len(str)) write(str,*) 'In main on image',this_image(), 'w= ',w(1:4) - if (str /= " In main on image 1 w= 1.00000000 1.00000000 1.00000000 1.00000000") & + if (str /= " In main on image 1 w= 1.00000 1.00000 1.00000 1.00000") & call abort str = repeat('X', len(str)) write(str,*) 'In main on image',this_image(), 'w= ',w(:)[1] - if (str /= " In main on image 1 w= 1.00000000 1.00000000 1.00000000 1.00000000") & + if (str /= " In main on image 1 w= 1.00000 1.00000 1.00000 1.00000") & call abort sync all @@ -107,6 +107,6 @@ subroutine ex5_sub(n,w) str = repeat('X', len(str)) write(str,*) 'In sub on image',this_image(), 'w= ',w - if (str /= " In sub on image 1 w= 1.00000000") & + if (str /= " In sub on image 1 w= 1.00000") & call abort end subroutine ex5_sub diff --git a/gcc/testsuite/gfortran.dg/default_format_1.inc b/gcc/testsuite/gfortran.dg/default_format_1.inc index e5d711c..f750c6c 100644 --- a/gcc/testsuite/gfortran.dg/default_format_1.inc +++ b/gcc/testsuite/gfortran.dg/default_format_1.inc @@ -23,7 +23,7 @@ contains do i = 0, count write (s,*) x read (s,*) y - if (y /= x) res = res + 1 + if (abs((y - x) / x) > 1e-5) res = res + 1 x = nearest(x,huge(x)) end do end if @@ -33,7 +33,7 @@ contains do i = 0, count write (s,*) x read (s,*) y - if (y /= x) res = res + 1 + if (abs((y - x) / x) > 1e-5) res = res + 1 x = nearest(x,-huge(x)) end do end if @@ -55,7 +55,7 @@ contains do i = 0, count write (s,*) x read (s,*) y - if (y /= x) res = res + 1 + if (abs ((y - x) / x) > 1e-5) res = res + 1 x = nearest(x,huge(x)) end do end if @@ -65,7 +65,7 @@ contains do i = 0, count write (s,*) x read (s,*) y - if (y /= x) res = res + 1 + if (abs ((y - x) / x) > 1e-5) res = res + 1 x = nearest(x,-huge(x)) end do end if diff --git a/gcc/testsuite/gfortran.dg/default_format_2.inc b/gcc/testsuite/gfortran.dg/default_format_2.inc index 7306f07..91d2976 100644 --- a/gcc/testsuite/gfortran.dg/default_format_2.inc +++ b/gcc/testsuite/gfortran.dg/default_format_2.inc @@ -24,7 +24,7 @@ contains do i = 0, count write (s,*) x read (s,*) y - if (y /= x) res = res + 1 + if (abs((y - x) / x) > 1e-5) res = res + 1 x = nearest(x,huge(x)) end do end if @@ -34,7 +34,7 @@ contains do i = 0, count write (s,*) x read (s,*) y - if (y /= x) res = res + 1 + if (abs((y - x) / x) > 1e-5) res = res + 1 x = nearest(x,-huge(x)) end do end if diff --git a/gcc/testsuite/gfortran.dg/f2003_io_5.f03 b/gcc/testsuite/gfortran.dg/f2003_io_5.f03 index c064e0c..8d1170e 100644 --- a/gcc/testsuite/gfortran.dg/f2003_io_5.f03 +++ b/gcc/testsuite/gfortran.dg/f2003_io_5.f03 @@ -13,14 +13,14 @@ write(99,nml=nm,decimal="comma") a = 5.55 rewind(99) read(99,nml=nm,decimal="comma") -if (any (a /= [ (i*1.3, i=1,10) ])) call abort +if (any (abs(a - [ (i*1.3, i=1,10) ]) > 1e-6)) call abort close(99, status="delete") c = (3.123,4.456) write(complex,*,decimal="comma") c -if (complex.ne." ( 3,12299991 ; 4,45599985 )") call abort +if (complex.ne." ( 3,12300 ; 4,45600 )") call abort c = (0.0, 0.0) read(complex,*,decimal="comma") c -if (complex.ne." ( 3,12299991 ; 4,45599985 )") call abort +if (complex.ne." ( 3,12300 ; 4,45600 )") call abort end diff --git a/gcc/testsuite/gfortran.dg/fmt_g0_1.f08 b/gcc/testsuite/gfortran.dg/fmt_g0_1.f08 index ead6f81..32350a6 100644 --- a/gcc/testsuite/gfortran.dg/fmt_g0_1.f08 +++ b/gcc/testsuite/gfortran.dg/fmt_g0_1.f08 @@ -8,13 +8,13 @@ write(buffer, string) ':',0,':' if (buffer.ne.":0:") call abort write(buffer, string) ':',1.0_8/3.0_8,':' - if (buffer.ne.":.33333333333333331:") call abort + if (buffer.ne.":.333333:") call abort write(buffer, '(1x,a,g0,a)') ':',1.0_8/3.0_8,':' - if (buffer.ne." :.33333333333333331:") call abort + if (buffer.ne." :.333333:") call abort write(buffer, string) ':',"hello",':' if (buffer.ne.":hello:") call abort write(buffer, "(g0,g0,g0,g0)") ':',.true.,.false.,':' if (buffer.ne.":TF:") call abort write(buffer, "(g0,g0,',',g0,g0)") '(',( 1.2345_8, 2.4567_8 ),')' - if (buffer.ne."(1.2344999999999999,2.4567000000000001)") call abort + if (buffer.ne."(1.23450,2.45670)") call abort end diff --git a/gcc/testsuite/gfortran.dg/large_real_kind_form_io_2.f90 b/gcc/testsuite/gfortran.dg/large_real_kind_form_io_2.f90 index a72c718..88da14e 100644 --- a/gcc/testsuite/gfortran.dg/large_real_kind_form_io_2.f90 +++ b/gcc/testsuite/gfortran.dg/large_real_kind_form_io_2.f90 @@ -12,24 +12,24 @@ program large_real_kind_form_io_2 b(:) = huge(0.0_k) write (tmp, *) b read (tmp, *) a, c - if (a /= b(1)) call abort () - if (c /= b(2)) call abort () + if (abs((a - b(1)) / b(1)) > 1e-5) call abort () + if (abs((c - b(2)) / b(2)) > 1e-5) call abort () b(:) = -huge(0.0_k) write (tmp, *) b read (tmp, *) a, c - if (a /= b(1)) call abort () - if (c /= b(2)) call abort () + if (abs((a - b(1)) / b(1)) > 1e-5) call abort () + if (abs((c - b(2)) / b(2)) > 1e-5) call abort () b(:) = nearest(tiny(0.0_k),1.0_k) write (tmp, *) b read (tmp, *) a, c - if (a /= b(1)) call abort () - if (c /= b(2)) call abort () + if (abs((a - b(1)) / b(1)) > 1e-5) call abort () + if (abs((c - b(2)) / b(2)) > 1e-5) call abort () b(:) = nearest(-tiny(0.0_k),-1.0_k) write (tmp, *) b read (tmp, *) a, c - if (a /= b(1)) call abort () - if (c /= b(2)) call abort () + if (abs((a - b(1)) / b(1)) > 1e-5) call abort () + if (abs((c - b(2)) / b(2)) > 1e-5) call abort () end program large_real_kind_form_io_2 diff --git a/gcc/testsuite/gfortran.dg/namelist_65.f90 b/gcc/testsuite/gfortran.dg/namelist_65.f90 index 7efbe70..e9be7b2 100644 --- a/gcc/testsuite/gfortran.dg/namelist_65.f90 +++ b/gcc/testsuite/gfortran.dg/namelist_65.f90 @@ -14,9 +14,9 @@ enddo write(out,nl1) if (out(1).ne."&NL1") call abort -if (out(2).ne." A= 1.00000000 ,") call abort -if (out(3).ne." B= 2.00000000 ,") call abort -if (out(4).ne." C= 3.00000000 ,") call abort +if (out(2).ne." A= 1.00000 ,") call abort +if (out(3).ne." B= 2.00000 ,") call abort +if (out(4).ne." C= 3.00000 ,") call abort if (out(5).ne." /") call abort end program oneline diff --git a/gcc/testsuite/gfortran.dg/namelist_print_1.f b/gcc/testsuite/gfortran.dg/namelist_print_1.f index 2e5de83..ce64558 100644 --- a/gcc/testsuite/gfortran.dg/namelist_print_1.f +++ b/gcc/testsuite/gfortran.dg/namelist_print_1.f @@ -9,5 +9,5 @@ namelist /mynml/ x x = 1 ! ( dg-output "^" } - print mynml ! { dg-output "&MYNML(\n|\r\n|\r) X= 1.00000000 ,(\n|\r\n|\r) /(\n|\r\n|\r)" } + print mynml ! { dg-output "&MYNML(\n|\r\n|\r) X= 1.00000 ,(\n|\r\n|\r) /(\n|\r\n|\r)" } end diff --git a/gcc/testsuite/gfortran.dg/quad_2.f90 b/gcc/testsuite/gfortran.dg/quad_2.f90 index d3c90a0..eed6d6c 100644 --- a/gcc/testsuite/gfortran.dg/quad_2.f90 +++ b/gcc/testsuite/gfortran.dg/quad_2.f90 @@ -13,53 +13,52 @@ program test_qp implicit none integer, parameter :: QP = real_kinds(ubound(real_kinds,dim=1)) real(qp) :: fp1, fp2, fp3, fp4 - character(len=80) :: str1, str2, str3, str4 + character(len=80) :: str2, str4, fmt + + select case (qp) + case (8) + fmt = '(g0.17)' + case (10) + fmt = '(g0.21)' + case (16) + fmt = '(g0.36)' + case default + call abort() + end select + fp1 = 1 fp2 = sqrt (2.0_qp) - write (str1,*) fp1 - write (str2,'(g0)') fp1 - write (str3,*) fp2 - write (str4,'(g0)') fp2 + write (str2, fmt) fp1 + write (str4, fmt) fp2 ! print '(3a)', '>',trim(str1),'<' ! print '(3a)', '>',trim(str2),'<' ! print '(3a)', '>',trim(str3),'<' ! print '(3a)', '>',trim(str4),'<' - read (str1, *) fp3 - if (fp1 /= fp3) call abort() read (str2, *) fp3 if (fp1 /= fp3) call abort() - read (str3, *) fp4 - if (fp2 /= fp4) call abort() read (str4, *) fp4 if (fp2 /= fp4) call abort() select case (qp) case (8) - if (str1 /= " 1.0000000000000000") call abort() if (str2 /= "1.0000000000000000") call abort() - if (str3 /= " 1.4142135623730951") call abort() if (str4 /= "1.4142135623730951") call abort() case (10) - if (str1 /= " 1.00000000000000000000") call abort() if (str2 /= "1.00000000000000000000") call abort() - if (str3 /= " 1.41421356237309504876") call abort() if (str4 /= "1.41421356237309504876") call abort() case (16) - if (str1 /= " 1.00000000000000000000000000000000000") call abort() if (str2 /= "1.00000000000000000000000000000000000") call abort() if (digits(1.0_qp) == 113) then ! IEEE 754 binary 128 format ! e.g. libquadmath/__float128 on i686/x86_64/ia64 - if (str3 /= " 1.41421356237309504880168872420969798") call abort() if (str4 /= "1.41421356237309504880168872420969798") call abort() else if (digits(1.0_qp) == 106) then ! IBM binary 128 format - if (str3(1:37) /= " 1.41421356237309504880168872420969") call abort() if (str4(1:34) /= "1.41421356237309504880168872420969") call abort() end if diff --git a/gcc/testsuite/gfortran.dg/real_const_3.f90 b/gcc/testsuite/gfortran.dg/real_const_3.f90 index e4b5de7..6162191 100644 --- a/gcc/testsuite/gfortran.dg/real_const_3.f90 +++ b/gcc/testsuite/gfortran.dg/real_const_3.f90 @@ -42,15 +42,15 @@ program main if (trim(adjustl(str)) .ne. 'NaN') call abort write(str,*) z - if (trim(adjustl(str)) .ne. '( NaN, NaN)') call abort + if (trim(adjustl(str)) .ne. '( NaN, NaN)') call abort write(str,*) z2 - if (trim(adjustl(str)) .ne. '( NaN, NaN)') call abort + if (trim(adjustl(str)) .ne. '( NaN, NaN)') call abort write(str,*) z3 - if (trim(adjustl(str)) .ne. '( Infinity, -Infinity)') call abort + if (trim(adjustl(str)) .ne. '( Infinity, -Infinity)') call abort write(str,*) z4 - if (trim(adjustl(str)) .ne. '( 0.00000000 , -0.00000000 )') call abort + if (trim(adjustl(str)) .ne. '( 0.00000 , -0.00000 )') call abort end program main diff --git a/libgcc/configure b/libgcc/configure old mode 100644 new mode 100755 diff --git a/libgfortran/io/write.c b/libgfortran/io/write.c index 8be3a5a..1af7080 100644 --- a/libgfortran/io/write.c +++ b/libgfortran/io/write.c @@ -1421,45 +1421,40 @@ static void set_fnode_default (st_parameter_dt *dtp, fnode *f, int length) { f->format = FMT_G; + f->u.real.d = 6; + switch (length) { case 4: - f->u.real.w = 16; - f->u.real.d = 9; f->u.real.e = 2; break; case 8: - f->u.real.w = 25; - f->u.real.d = 17; f->u.real.e = 3; break; case 10: - f->u.real.w = 30; - f->u.real.d = 21; - f->u.real.e = 4; - break; case 16: - f->u.real.w = 45; - f->u.real.d = 36; f->u.real.e = 4; break; default: internal_error (&dtp->common, "bad real kind"); break; } + + /* 5 extra characters: Initial sign, digit before decimal, decimal, + exponent character, exponent sign. */ + f->u.real.w = f->u.real.d + f->u.real.e + 5; } -/* Output a real number with default format. To guarantee that a - binary -> decimal -> binary roundtrip conversion recovers the - original value, IEEE 754-2008 requires 9, 17, 21 and 36 significant - digits for REAL kinds 4, 8, 10, and 16, respectively. Thus, we use - 1PG16.9E2 for REAL(4), 1PG25.17E3 for REAL(8), 1PG30.21E4 for - REAL(10) and 1PG45.36E4 for REAL(16). The exception is that the - Fortran standard requires outputting an extra digit when the scale - factor is 1 and when the magnitude of the value is such that E - editing is used. However, gfortran compensates for this, and thus - for list formatted the same number of significant digits is - generated both when using F and E editing. */ + +/* Output a real number with default format, used by list formatted + output. We use a scale factor of 1, meaning that when the magnitude + is such that E editing is used, there is one digit before the + decimal point. The Fortran standard requires outputting an extra + digit when the scale factor is 1 and when the magnitude of the + value is such that E editing is used. However, gfortran compensates + for this, and thus for list formatted the same number of + significant digits is generated both when using F and E + editing. */ void write_real (st_parameter_dt *dtp, const char *source, int length)