Best practice when working with double precision magic numbers

前端 未结 1 1040
春和景丽
春和景丽 2020-12-11 10:25

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 doubl

相关标签:
1条回答
  • 2020-12-11 10:59

    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.

    0 讨论(0)
提交回复
热议问题