Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
325 views
in Technique[技术] by (71.8m points)

fortran - Best practice when working with double precision magic numbers

Do I necessarily need to specify D (e.g., 1.234D+00) at the end of all magic numbers (literal constants) if I've already declared everything double precision anyway?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Short answer: Yes, you do.

Long answer: By default, real literals are single precision unless otherwise specified. Assigning single precision literals to double precision variables incurs precision loss; that is, single precision literals are evaluated first as single precision then assigned to the higher-precision variable. I'm too lazy to retrieve the F2003 Handbook from the other room but I suspect that single-to-double assignment sets the low significance mantissa bits to zero. Either that or it's left up to the vendor.

Regardless, here's a demonstration of what happens when you mix precision between literals and variables (note that 0.1 can't be stored cleanly in binary floating point):

!> Demonstrate the effects of D and E suffixes on precision of literals
program whatkind
    use iso_fortran_env, only: output_unit, REAL32, REAL64
    implicit none

    real (kind=REAL64) :: dtest

10 format('Literal ', A, ' is of kind ', I2)
20 format(/, A)
30 format(/, 'Value stored in ', A, ' precision generated with ', A,    &
          ' precision literals:')
40 format('Literal is ', A)

    continue

    write(output_unit, 10) '1.0', kind(1.0)
    write(output_unit, 10) '1.0E0', kind(1.0E0)
    write(output_unit, 10) '1.0D0', kind(1.0D0)
    write(output_unit, 10) '1.0_REAL32', kind(1.0_REAL32)
    write(output_unit, 10) '1.0_REAL64', kind(1.0_REAL64)

    write(output_unit, 20) 'Raw tenths tests:'

    dtest = 0.1
    write(output_unit, 30) 'double', 'single'
    write(output_unit, 40) '0.1'
    write(output_unit, *) dtest

    dtest = 0.1D0
    write(output_unit, 30) 'double', 'double'
    write(output_unit, 40) '0.1D0'
    write(output_unit, *) dtest

    dtest = 1.0 / 10.0
    write(output_unit, 30) 'double', 'single'
    write(output_unit, 40) '0.1'
    write(output_unit, 40) '1.0 / 10.0'
    write(output_unit, *) dtest

    dtest = 1.0_REAL64 / 10.0_REAL64
    write(output_unit, 30) 'double', 'double'
    write(output_unit, 40) '1.0_REAL64 / 10.0_REAL64'
    write(output_unit, *) dtest

    dtest = 1.0_REAL32 / 10.0_REAL32
    write(output_unit, 30) 'double', 'single'
    write(output_unit, 40) '1.0_REAL32 / 10.0_REAL32'
    write(output_unit, *) dtest

    dtest = 1.0_REAL64 / 10.0_REAL32
    write(output_unit, 30) 'double', 'mixed'
    write(output_unit, 40) '1.0_REAL64 / 10.0_REAL32'
    write(output_unit, *) dtest

    dtest = 1.0_REAL32 / 10.0_REAL64
    write(output_unit, 30) 'double', 'mixed'
    write(output_unit, 40) '1.0_REAL32 / 10.0_REAL64'
    write(output_unit, *) dtest

end program whatkind

The results of this are:

Literal 1.0 is of kind  4
Literal 1.0E0 is of kind  4
Literal 1.0D0 is of kind  8
Literal 1.0_REAL32 is of kind  4
Literal 1.0_REAL64 is of kind  8

Raw tenths tests:

Value stored in double precision generated with single precision literals:
Literal is 0.1
  0.10000000149011612     

Value stored in double precision generated with double precision literals:
Literal is 0.1D0
  0.10000000000000001     

Value stored in double precision generated with single precision literals:
Literal is 0.1
Literal is 1.0 / 10.0
  0.10000000149011612     

Value stored in double precision generated with double precision literals:
Literal is 1.0_REAL64 / 10.0_REAL64
  0.10000000000000001     

Value stored in double precision generated with single precision literals:
Literal is 1.0_REAL32 / 10.0_REAL32
  0.10000000149011612     

Value stored in double precision generated with mixed precision literals:
Literal is 1.0_REAL64 / 10.0_REAL32
  0.10000000000000001     

Value stored in double precision generated with mixed precision literals:
Literal is 1.0_REAL32 / 10.0_REAL64
  0.10000000000000001 

You see how in cases where all the literals are single precision (including those with no explicit precision set) there is low significance 'noise' stored in the double precision variable.

I find it interesting that operations on mixed precision literals seems to promote all the literals to higher precision before the operation is performed. Someone with more language-spec-fu might be able to explain that.

My advice: When in doubt, be explicit. It's safer and I think it's worth the extra keystrokes.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...