代码之家  ›  专栏  ›  技术社区  ›  EMiller

与其他语言相比,FORTRAN在数字精度上是否存在固有限制?

  •  5
  • EMiller  · 技术社区  · 15 年前

    在进行一个简单的编程练习时,我生成了一个while循环(fortran中的do循环),该循环用于在实际变量达到精确值时退出。

    我注意到,由于使用的精度,从来没有满足过相等性,循环变得无限。当然,这并非闻所未闻,我们建议不要为了相等而比较两个数字,最好看两个数字之间的绝对差是否小于设定的阈值。

    我发现令人失望的是,为了让循环正确退出,我必须将这个阈值设置得很低,即使变量的精度为两倍。此外,当我在Perl中重写这个循环的“蒸馏”版本时,我对数值精度没有任何问题,循环退出得很好。

    由于产生问题的代码非常小,在Perl和Fortran中,我想在这里重新生成它,以防我忽略了一个重要的细节:

    FORTRAN码

    PROGRAM precision_test
    IMPLICIT NONE
    
    ! Data Dictionary
    INTEGER :: count = 0 ! Number of times the loop has iterated
    REAL(KIND=8) :: velocity
    REAL(KIND=8), PARAMETER :: MACH_2_METERS_PER_SEC = 340.0
    
    velocity = 0.5 * MACH_2_METERS_PER_SEC ! Initial Velocity
    DO
            WRITE (*, 300) velocity
            300 FORMAT (F20.8)
            IF (count == 50) EXIT
            IF (velocity == 5.0 * MACH_2_METERS_PER_SEC) EXIT
    !       IF (abs(velocity - (5.0 * MACH_2_METERS_PER_SEC)) < 1E-4) EXIT
            velocity = velocity + 0.1 * MACH_2_METERS_PER_SEC
            count = count + 1 
    END DO
    
    END PROGRAM precision_test
    

    Perl码

    #! /usr/bin/perl -w
    use strict;
    
    my $mach_2_meters_per_sec = 340.0;
    
    my $velocity = 0.5 * $mach_2_meters_per_sec;
    
    while (1) {
            printf "%20.8f\n", $velocity;   
            exit if ($velocity == 5.0 * $mach_2_meters_per_sec);
            $velocity = $velocity + 0.1 * $mach_2_meters_per_sec;
    }
    

    Fortran中的注释行是循环正常退出所需的。注意,阈值设置为1e-4,我觉得这很可悲。

    变量的名称来自于我执行的基于自学的编程练习,没有任何相关性。

    其目的是当速度变量达到1700时,循环停止。

    以下是截断的输出:

    Perl输出

        170.00000000
        204.00000000
        238.00000000
        272.00000000
        306.00000000
        340.00000000
    

       1564.00000000
       1598.00000000
       1632.00000000
       1666.00000000
       1700.00000000
    

    FORTRAN输出

        170.00000000
        204.00000051
        238.00000101
        272.00000152
        306.00000203
        340.00000253
    

       1564.00002077
       1598.00002128
       1632.00002179
       1666.00002229
       1700.00002280
    

    如果Fortran的精确性很差,那么它的速度和并行化的易用性有什么好处呢?让我想起三种方法:

    1. 正确的方法

    2. 错误的方式

    3. 最大功率方式

    “这不是错误的方式吗?”

    “是啊!但是更快!”

    别开玩笑了,我一定是做错了什么。

    与其他语言相比,fortran在数字精度上有固有的局限性吗,还是我(很可能)是错误的?

    我的编译器是gfortran(gcc版本4.1.2),perl v5.12.1,在一个双核AMDOpteron@1GHz上。

    2 回复  |  直到 15 年前
        1
  •  15
  •   ysth    15 年前

    您的工作分配意外地将值转换为单精度,然后再转换回双精度。

    试着做你的 0.1 * 0.1D0 * 你应该看到你的问题解决了。

        2
  •  7
  •   Community CDub    8 年前

    如前所述,Fortran中的“普通”浮点常量将默认为默认的实数类型,这可能是单精度的。这几乎是一个典型的错误。

    另外,使用“kind=8”是不可移植的——它将为您提供gfortran的双精度,但不适用于其他编译器。在Fortran>=90中,指定变量和常量精度的安全、可移植的方法是使用内部函数,并请求所需的精度。然后在精度很重要的常量上指定“种类”。一个方便的方法是定义自己的符号。例如:

    integer, parameter :: DR_K = selected_real_kind (14)
    
    REAL(DR_K), PARAMETER :: MACH_2_METERS_PER_SEC = 340.0_DR_K
    
    real (DR_K) :: mass, velocity, energy
    
    energy = 0.5_DR_K * mass * velocity**2
    

    这对整数也很重要,例如,如果需要较大的值。有关整数的相关问题,请参见 Fortran: integer*4 vs integer(4) vs integer(kind=4) Long ints in Fortran