From: brady_fht on
Hello,

I need some help understanding floating point operations. I am seeing
a small difference in a program when it is compiled for 32 and 64 bit
machines. Here is the program:

--------------------------------------------------------
PROGRAM DPStudy
IMPLICIT NONE
REAL(8) :: FVF
REAL(8) :: THR
REAL(8) :: Dum1, Dum2

FVF = 3.3D0
THR = 3.0D0

Dum1 = FVF * THR * DSQRT(THR)
Dum2 = FVF * DSQRT(THR) * THR

1 FORMAT(A, ES28.22)
WRITE(*,*) ''
WRITE(*,1) 'Dum1 = ', Dum1
WRITE(*,1) 'Dum2 = ', Dum2
WRITE(*,*) ''
STOP
ENDPROGRAM DPStudy
--------------------------------------------------------

Pretty simple. I am using gfortran 4.3.3 on Ubuntu 9.04. The following
shows the different compiler options I use and the resulting output
from the above program:


--------------------------------------------------------
COMPILER:
gfortran -m32 -O0 main.f90

OUTPUT:
Dum1 = 1.7147302994931884256857E+01
Dum2 = 1.7147302994931884256857E+01
--------------------------------------------------------

--------------------------------------------------------
COMPILER:
gfortran -m64 -O0 main.f90

OUTPUT:
Dum1 = 1.7147302994931880704144E+01
Dum2 = 1.7147302994931884256857E+01
--------------------------------------------------------

--------------------------------------------------------
COMPILER:
gfortran -m32 -O1 main.f90

OUTPUT:
Dum1 = 1.7147302994931880704144E+01
Dum2 = 1.7147302994931884256857E+01
--------------------------------------------------------

--------------------------------------------------------
COMPILER:
gfortran -m64 -O1 main.f90

OUTPUT:
Dum1 = 1.7147302994931880704144E+01
Dum2 = 1.7147302994931884256857E+01
--------------------------------------------------------


So it appears the order of multiplication yields a different result
which doesn't surprise me all that much. However, it looks like when
optimization is turned off for the 32 bit case the compiler re-orders
the order of multiplication. I tried going through the gfortran
documentation to understand what happens when -O1 is used but I
couldn't find a single flag that triggers this behavior. Can anybody
help me understand what is going on here?

For what it's worth - similar behavior was observed using the Intel
Fortran Compiler 11.1 for Windows.

Thank you!
From: robin on
"brady_fht" <brady.m.adams(a)gmail.com> wrote in message
news:0ee467f7-747a-4b6b-a7cf-2c8174ea062a(a)e28g2000vbd.googlegroups.com...
| Hello,
|
| I need some help understanding floating point operations. I am seeing
| a small difference in a program when it is compiled for 32 and 64 bit
| machines. Here is the program:

In the first place you specified a number kind (viz. 8).
That is implementation dependent and hence non-portable.
Whether it means the same to both compilers, I don't know,
but I suspect that it doesn't.

In the second place, Runs #2-4 have given you
two results that agree to 16 significant digits,
which is what I would expect for 64-bit float precision.

As for the slightly differing results in run #2 --
further investigtion is suggested. Have you tried,
for example, substituting SQRT for DSQRT?

| --------------------------------------------------------
| PROGRAM DPStudy
| IMPLICIT NONE
| REAL(8) :: FVF
| REAL(8) :: THR
| REAL(8) :: Dum1, Dum2
|
| FVF = 3.3D0
| THR = 3.0D0
|
| Dum1 = FVF * THR * DSQRT(THR)
| Dum2 = FVF * DSQRT(THR) * THR
|
| 1 FORMAT(A, ES28.22)
| WRITE(*,*) ''
| WRITE(*,1) 'Dum1 = ', Dum1
| WRITE(*,1) 'Dum2 = ', Dum2
| WRITE(*,*) ''
| STOP
| ENDPROGRAM DPStudy
| --------------------------------------------------------
|
| Pretty simple. I am using gfortran 4.3.3 on Ubuntu 9.04. The following
| shows the different compiler options I use and the resulting output
| from the above program:
|
|
| --------------------------------------------------------
| COMPILER:
| gfortran -m32 -O0 main.f90
|
| OUTPUT:
| Dum1 = 1.7147302994931884256857E+01
| Dum2 = 1.7147302994931884256857E+01
| --------------------------------------------------------
|
| --------------------------------------------------------
| COMPILER:
| gfortran -m64 -O0 main.f90
|
| OUTPUT:
| Dum1 = 1.7147302994931880704144E+01
| Dum2 = 1.7147302994931884256857E+01
| --------------------------------------------------------
|
| --------------------------------------------------------
| COMPILER:
| gfortran -m32 -O1 main.f90
|
| OUTPUT:
| Dum1 = 1.7147302994931880704144E+01
| Dum2 = 1.7147302994931884256857E+01
| --------------------------------------------------------
|
| --------------------------------------------------------
| COMPILER:
| gfortran -m64 -O1 main.f90
|
| OUTPUT:
| Dum1 = 1.7147302994931880704144E+01
| Dum2 = 1.7147302994931884256857E+01
| --------------------------------------------------------
|
|
| So it appears the order of multiplication yields a different result
| which doesn't surprise me all that much. However, it looks like when
| optimization is turned off for the 32 bit case the compiler re-orders
| the order of multiplication. I tried going through the gfortran
| documentation to understand what happens when -O1 is used but I
| couldn't find a single flag that triggers this behavior. Can anybody
| help me understand what is going on here?
|
| For what it's worth - similar behavior was observed using the Intel
| Fortran Compiler 11.1 for Windows.
|
| Thank you!


From: Gordon Sande on
On 2010-05-18 12:39:29 -0300, brady_fht <brady.m.adams(a)gmail.com> said:

> Hello,
>
> I need some help understanding floating point operations. I am seeing
> a small difference in a program when it is compiled for 32 and 64 bit
> machines.

What is a 32 or 84 bit machine? Usually it is a discussion of the length
of memory addresses and has nothing to do with arithmetic.

> Here is the program:
>
> --------------------------------------------------------
> PROGRAM DPStudy
> IMPLICIT NONE
> REAL(8) :: FVF
> REAL(8) :: THR
> REAL(8) :: Dum1, Dum2
>
> FVF = 3.3D0
> THR = 3.0D0
>
> Dum1 = FVF * THR * DSQRT(THR)
> Dum2 = FVF * DSQRT(THR) * THR
>
> 1 FORMAT(A, ES28.22)
> WRITE(*,*) ''
> WRITE(*,1) 'Dum1 = ', Dum1
> WRITE(*,1) 'Dum2 = ', Dum2
> WRITE(*,*) ''
> STOP
> ENDPROGRAM DPStudy
> --------------------------------------------------------
>
> Pretty simple. I am using gfortran 4.3.3 on Ubuntu 9.04. The following
> shows the different compiler options I use and the resulting output
> from the above program:
>
>
> --------------------------------------------------------
> COMPILER:
> gfortran -m32 -O0 main.f90
>
> OUTPUT:
> Dum1 = 1.7147302994931884256857E+01
> Dum2 = 1.7147302994931884256857E+01
> --------------------------------------------------------
>
> --------------------------------------------------------
> COMPILER:
> gfortran -m64 -O0 main.f90
>
> OUTPUT:
> Dum1 = 1.7147302994931880704144E+01
> Dum2 = 1.7147302994931884256857E+01
> --------------------------------------------------------
>
> --------------------------------------------------------
> COMPILER:
> gfortran -m32 -O1 main.f90
>
> OUTPUT:
> Dum1 = 1.7147302994931880704144E+01
> Dum2 = 1.7147302994931884256857E+01
> --------------------------------------------------------
>
> --------------------------------------------------------
> COMPILER:
> gfortran -m64 -O1 main.f90
>
> OUTPUT:
> Dum1 = 1.7147302994931880704144E+01
> Dum2 = 1.7147302994931884256857E+01
> --------------------------------------------------------
>
>
> So it appears the order of multiplication yields a different result
> which doesn't surprise me all that much.

Without parens, what order do you think is "correct"? Can you justify
your claim? Have you tried it with explicit parens?

> However, it looks like when
> optimization is turned off for the 32 bit case the compiler re-orders
> the order of multiplication. I tried going through the gfortran
> documentation to understand what happens when -O1 is used but I
> couldn't find a single flag that triggers this behavior. Can anybody
> help me understand what is going on here?

Most flags controlling low level compilation issuses for the GNU compilers
are given to the back end and are not language specific. Look up GCC.

> For what it's worth - similar behavior was observed using the Intel
> Fortran Compiler 11.1 for Windows.
>
> Thank you!


From: glen herrmannsfeldt on
brady_fht <brady.m.adams(a)gmail.com> wrote:

> I need some help understanding floating point operations. I am seeing
> a small difference in a program when it is compiled for 32 and 64 bit
> machines. Here is the program:

On most machines now in use, double precision floating point
has a 53 bit significand, for 53*log10(2.) or about 16 digits.

(snip)

> Dum1 = FVF * THR * DSQRT(THR)
> Dum2 = FVF * DSQRT(THR) * THR

(snip)

The use of constant KIND values is non-portable, but most
likely in this case gives the double precision you expect.

Many compilers optimize constant expressions, or expressions
that they can figure out are constant, by computing the value
at compile time. Often that is different than the result that
would be obtained if the expressions were not constant.

You might try again, with

READ(*,*) FVF, THR

and then typing in the appropriate values.


(snip of gfortran -m32)

> --------------------------------------------------------
> COMPILER:
> gfortran -m64 -O0 main.f90

> OUTPUT:
> Dum1 = 1.7147302994931880704144E+01
> Dum2 = 1.7147302994931884256857E+01
> --------------------------------------------------------

As I said, it is possible that the compiler is doing the
calculation at compile time, more likely with -O1.

In any case, compile with the -S option and post the
resulting .s file. (I would remove the unneeded write
statements first.)

(snip of additional tests with the same result.)

> So it appears the order of multiplication yields a different result
> which doesn't surprise me all that much. However, it looks like when
> optimization is turned off for the 32 bit case the compiler re-orders
> the order of multiplication. I tried going through the gfortran
> documentation to understand what happens when -O1 is used but I
> couldn't find a single flag that triggers this behavior. Can anybody
> help me understand what is going on here?

There are a few possibilities. One, though not as likely as it
used to be, is using the x87 floating point instructions which
compute to 64 significant bits, and then rounding the result to 53.
That often is one LSB different than multiplying the 53 bit values.
On machines not built by Cray, floating point multiplication is
usually commutative, but not always associative.

> For what it's worth - similar behavior was observed using the Intel
> Fortran Compiler 11.1 for Windows.

The usual rule is that you should not depend on it being better
than that. The differences you see are in the least significant
bit, or close to it, for a 53 bit significand.

-- glen
From: Tobias Burnus on
On 05/18/2010 05:39 PM, brady_fht wrote:
> I need some help understanding floating point operations. I am seeing
> a small difference in a program when it is compiled for 32 and 64 bit
> machines. Here is the program:
>
> Pretty simple. I am using gfortran 4.3.3 on Ubuntu 9.04. The following
> shows the different compiler options I use and the resulting output
> from the above program:

> gfortran -m32 -O0 main.f90
> gfortran -m64 -O0 main.f90

By default GCC on x84 (ia32, i686, ...) uses the floating-point
coprocessor "x87" (-mfpmath=386) while on x86-64 (AMD64, Intel64, em64t,
....) by default SSE is used. While SSE uses proper rounding, the x87
processor has some excess precision, which is used for intermediate
results. One way to force the rounding is to store the intermediate
result in memory (opposed to the x87 registers); in GCC/gfortran you can
use -ffloat-store to force this.*

When you use -O1 (or higher), the compiler plugs in the values of the
variables and calculates the result at compile time (with proper rounding).

If you want to see what the compiler does, you can use the
-fdump-tree-all option, which dumps a (simplified, C-syntax-like)
internal representation. The "original" and "optimized" are probably
most interesting.

> For what it's worth - similar behavior was observed using the Intel
> Fortran Compiler 11.1 for Windows.

(Newer) Intel compilers default to SSE.

* = GCC 4.5 and 4.6 also offer -fexcess-precision=, but I think this
option is not yet supported for GCC's Fortran front end. Additionally,
GCC 4.5 and 4.6 have now a compiler compile-time configuration option,
which allows to enable SSE by default (--with-fpmath=sse).
For details, see http://gcc.gnu.org/gcc-4.5/changes.html

* * *

General remark: Don't look too closely at the last digits in
floating-point calculations and do not assume that changing the order
won't change the result. I would also suggest you to read the Goldberg
paper.

a) Goldberg paper:
http://www.google.de/search?q=Goldberg+What+Every+Computer+Scientist+Should+Know+About+Floating-Point+Arithmetic

b) Monniaux paper:
http://hal.archives-ouvertes.fr/hal-00128124

c) Semantics of Floating Point Math in GCC
http://gcc.gnu.org/wiki/FloatingPointMath

d) Note on the x87 coprocessor (for GCC)
http://gcc.gnu.org/wiki/x87note
http://gcc.gnu.org/wiki/Math_Optimization_Flags

e) IEEE 754:2008 "IEEE Standard for Floating-Point Arithmetic"
http://ieeexplore.ieee.org/servlet/opac?punumber=4610933
http://www.validlab.com/754R/drafts/archive/2006-10-04.pdf (last
publicly available draft I found)

Tobias