different results with pgf90, gfortran and ifort

Hi,

I have a pretty big code and want to figure out, why I have some differences with different compilers. Therefore, I have compiled a small sub-problem that only reads the data, makes the initialization and writes the output files. With ifort and gfortran I get the same results, but the results from PGI differ. (the significant digits in double precision do match with all three compilers in this case, but there are bigger discrepancies when more parts of the code are used)

ifort and gfortran result:
0.2116919158172203424E+04

pgf90 result:
0.2116919158172202970E+04

the formula for this value is:

       etfun = .5d0 * rhoV2   
     &      +  e_ltnt         
     &      +  etrans         
     &      +  vib_en        
     &      +  erot

with the identical values:
rhov2 : 0.4246880000000000109E+04
e_ltnt : -0.1281190399897235288E+02
etrans : 0.3774588278644195327E+01
vib_en : 0.8256235492705331022E-04
erot : 0.2516391330176059871E+01

The code is compiled and run on an Intel Xeon CPU with 64bit and the following compiler flags:

pgf90 -g -O0 -Mpreprocess
ifort -fpp -72 -O0 -g
gfortran -cpp -g -O0 -p -ffixed-form

Does anyone see my mistake? Or does anyone know compiler flags for ifort, gfortran and PGI that should make use of the same floating point arithmetic and lead to the same results? Or maybe someone can explain why the above behaviour is correct?

Many thanks in advance!

Hi jockel,

Or does anyone know compiler flags for ifort, gfortran and PGI that should make use of the same floating point arithmetic and lead to the same results?

There could be a number of things going on here so I decided to write up my own little test case and was able to recreate your results:

% cat test1.f90 
program testprec
  
      real(8) :: rhov2, e_ltnt, etrans, vib_en, erot, etfun 

      rhov2= 0.4246880000000000109E+04_8
      e_ltnt=-0.1281190399897235288E+02_8
      etrans=0.3774588278644195327E+01_8
      vib_en=0.8256235492705331022E-04_8
      erot=0.2516391330176059871E+01_8

       etfun = .5d0 * rhoV2    &
           +  e_ltnt           &
           +  etrans           &
           +  vib_en           &
           +  erot           
       WRITE(*,'(e26.19)') etfun

end program testprec

% pgfortran -O0 test1.f90 -o pgi.out
% ifort -O0 test1.f90 -o intel.out
% pgi.out
 0.2116919158172202970E+04
% intel.out
 0.2116919158172203424E+04

Since you’re not using optimization, it narrows down the possible differences. You can try setting each compilers IEEE754 precision flag (PGI: -Kieee Intel: -fp=model precise) but it doesn’t matter here. Hence, I took a look at the assembly code (PGI: -Mkeepsam -Manno or -S, Intel: -S )

PGI:

##        etfun = .5d0 * rhoV2    &
##            +  e_ltnt           &
##            +  etrans           &
##            +  vib_en           &
##            +  erot           
        vaddsd  %xmm3, %xmm2, %xmm2   !  etrans = vib_en+etrans
        vaddsd  %xmm4, %xmm2, %xmm2   !  etrans = erot + etrans
        vmulsd  .C1_294(%rip), %xmm0, %xmm0  ! rhoV2 = rhoV2 * 0.5d
        vaddsd  %xmm1, %xmm0, %xmm0  ! rhoV2 = rhoV2 + e_ltnt
        vaddsd  %xmm0, %xmm2, %xmm0  ! rhoV2 = rhoV2 + etrans
        vmovsd  %xmm0, -8(%rbp)    ! etfun = rhoV2
##  lineno: 17

Intel:

        mulsd     %xmm1, %xmm0                                  #11.21  ! rhoV2 = rhoV2*0.5
        movsd     -136(%rbp), %xmm1                             #11.21 ! get e_ltnt 
        addsd     %xmm1, %xmm0                                  #12.12  ! rhoV2 = rhoV2+ e_ltnt 
        movsd     -128(%rbp), %xmm1                             #12.12 ! get etrans 
        addsd     %xmm1, %xmm0                                  #13.12 ! rhoV2 = rhoV2 +  etrans 
        movsd     -120(%rbp), %xmm1                             #13.12 ! get vib_en
        addsd     %xmm1, %xmm0                                  #14.12  ! rhoV2 = rhoV2 + vib_en
        movsd     -112(%rbp), %xmm1                             #14.12  ! get  erot
        addsd     %xmm1, %xmm0                                  #11.8   ! rhoV2 = rhoV2 + erot
        movsd     %xmm0, -104(%rbp)                             #11.8  ! etfun = rhoV2

PGI and Intel are evaluating your expression in different orders. PGI tends to go from right to left, while Intel goes from left to right. The difference in the order of operations is causing difference. Both are correct since both adhere to operator precedence and associativity, just a different correct.

To make them agree, you need to force the compilers to evaluate the expression in the same way by using parentheses.

% cat test2.f90 
program testprec
  
      real(8) :: rhov2, e_ltnt, etrans, vib_en, erot, etfun 

      rhov2= 0.4246880000000000109E+04_8
      e_ltnt=-0.1281190399897235288E+02_8
      etrans=0.3774588278644195327E+01_8
      vib_en=0.8256235492705331022E-04_8
      erot=0.2516391330176059871E+01_8

       etfun = ((((.5d0 * rhoV2)    &
           +  e_ltnt)           &
           +  etrans)           &
           +  vib_en)           &
           +  erot           
       WRITE(*,'(e26.19)') etfun
end program testprec

% ifort -O0 test2.f90 -o intel2.out
% pgfortran -O0 test2.f90 -o pgi2.out
% pgi2.out 
 0.2116919158172203424E+04
% intel2.out 
 0.2116919158172203424E+04

Hope this helps,
Mat