------- Comment #4 from dominiq at lps dot ens dot fr 2007-05-29 20:25 ------- Following the Steve Kargl's suggestion in
http://gcc.gnu.org/ml/gcc-patches/2007-05/msg01945.html I have done the following test: [archimede] test/fortran> cat > sec_prec_1.f90 implicit none integer j, k, l, m, n integer i, ifa, ifb, ifc real a, b, c i = 0 ; ifa = 0 ; ifb = 0 ; ifc = 0 do m = 0, 23 do l = 0, 59 do k = 0, 59 do j = 0, 999 i = i + 1 a = 3600.0*real(m)+ 60.0*real(l) + real(k) + 0.001*real(j) b = 0.001*real(j) + real(k) + 60.0*real(l) + 3600.0*real(m) c = (3600000*m + 60000*l + 1000*k + j)/1000.0 if (b /= c) ifa = ifa + 1 if (a /= c) ifb = ifb + 1 if (a /= b) ifc = ifc + 1 end do end do end do end do print *, i, ifa, ifb, ifc end On PPC OSX and AMD64 Linux (with and without -O), I get: 86400000 19312016 17523130 2683738 It seems hard to decide what is the right solution, expect that the same has to be used in the test cases. On a Pentium Linux, I get the same result if I use -ffloat-store. Without this flag I get: 86400000 1934 1934 0 without -O and 86400000 86313600 86313600 0 with -O. Since I understand that in the later cases, the right hand side is computed as real(10), hence a and b are the same after rounding to real(4). However I don't see why the optimization has such a dramatic effect. I have also run the following test: [archimede] test/fortran> cat > sec_prec.f90 integer i, j, k, l, m, n real a, b, c k = 59 l = 59 m = 23 do i = 0, 9 a = 86399.0 + 0.001*real(990+i) b = 0.001*real(990+i) + real(k) + 60.0*real(l) + 3600.0*real(m) c = (86399990 + i)/1000.0 print '(I3,3(1PG20.10))', i, a, b, c end do end which gives: 0 86399.99219 86399.99219 86399.99219 1 86399.99219 86399.99219 86399.99219 2 86399.99219 86399.99219 86399.99219 3 86399.99219 86399.99219 86399.99219 4 86399.99219 86399.99219 86399.99219 5 86399.99219 86399.99219 86399.99219 6 86399.99219 86400.00000 86400.00000 7 86400.00000 86400.00000 86400.00000 8 86400.00000 86400.00000 86400.00000 9 86400.00000 86400.00000 86400.00000 i.e., the last 3/4 ms gives 86400.0, hence the result of secnds(0.0) is inside the interval [0.0,86400.0] and not inside [0.0,86400.0[ as I naively expected. This is the cryptic point (2) in my comment #1. A side effect is that if your are within this time frame and do t1=secnds(0.0); t2=secnds(t1) you get 86400.0 instead of 0.0. A possible solution is to add a t1=min(86399.996,t1) to force the output in the semiopen interval. With the reversal of the time computation and putting everything in float, it leads to the following patch: --- gcc-4.3-20070525/libgfortran/intrinsics/date_and_time.c Fri Apr 6 18:47:23 2007 +++ gcc-4.3-20070526/libgfortran/intrinsics/date_and_time.c Mon May 28 21:40:46 2007 @@ -341,12 +341,15 @@ free_mem (avalues); - temp1 = 3600.0 * (GFC_REAL_4)values[4] + - 60.0 * (GFC_REAL_4)values[5] + - (GFC_REAL_4)values[6] + - 0.001 * (GFC_REAL_4)values[7]; - temp2 = fmod (*x, 86400.0); - temp2 = (temp1 - temp2 >= 0.0) ? temp2 : (temp2 - 86400.0); + temp1 = 0.001f * (GFC_REAL_4)values[7] + + (GFC_REAL_4)values[6] + + 60.0f * (GFC_REAL_4)values[5] + + 3600.0f * (GFC_REAL_4)values[4]; + + /* Fix the round-off errors for the 3ms before midnight. */ + temp1= (86399.996f>=temp1)? temp1 : 86399.996f; + temp2 = fmod (*x, 86400.0f); + temp2 = (temp1 - temp2 >= 0.0f) ? temp2 : (temp2 - 86400.0f); return temp1 - temp2; } I have implemented part of it (without the reversal for temp1) on my last build on OSX and it seems to work. For the other platforms I rely on binaries, so I need some outside help to test the patch on them!-) -- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=32057