与其他语言相比,Fortran对数值准确性有固有限制吗?

EMi*_*ler 5 perl fortran floating-accuracy double-precision

在进行简单的编程练习时,我创建了一个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
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

Fortran中注释掉的行是我需要使用的循环才能正常退出.请注意,阈值设置为1E-4,我觉得这很可怜.

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

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

以下是截断的输出:

Perl输出

    170.00000000
    204.00000000
    238.00000000
    272.00000000
    306.00000000
    340.00000000
Run Code Online (Sandbox Code Playgroud)

...

   1564.00000000
   1598.00000000
   1632.00000000
   1666.00000000
   1700.00000000
Run Code Online (Sandbox Code Playgroud)

Fortran输出

    170.00000000
    204.00000051
    238.00000101
    272.00000152
    306.00000203
    340.00000253
Run Code Online (Sandbox Code Playgroud)

...

   1564.00002077
   1598.00002128
   1632.00002179
   1666.00002229
   1700.00002280
Run Code Online (Sandbox Code Playgroud)

如果Fortran的准确性很糟糕,Fortran的速度和并行化的便利性有什么用呢?让我想起三种做事方式:

  1. 正确的方式

  2. 错误的方法

  3. 最大功率方式

"这不是错误的方式吗?"

"是的!但更快!"

除了开玩笑,我一定是做错了.

与其他语言相比,Fortran对数值准确性有固有的限制,还是我(很可能)是错误的?

我的编译器是gfortran(gcc版本4.1.2),Perl v5.12.1,在双核AMD Opteron @ 1 GHZ上.

yst*_*sth 15

您的作业意外地将值转换为单精度,然后再转换为双精度.

请尝试将0.1 *BE 0.1D0 *,你应该看到您的问题解决.

  • @CmdrGuard:是的,但最接近.1的单精度值比最接近.1的双精度值更不准确.例如,下面的0.1被提升为double,但这并不能弥补丢失的精度:`WRITE(*,"(F20.18)")(0.1-0.1D0)` (2认同)

M. *_* B. 7

正如已经回答的那样,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
Run Code Online (Sandbox Code Playgroud)

这对于整数也很重要,例如,如果需要大的值.有关整数的相关问题,请参阅Fortran:整数*4与整数(4)与整数(kind = 4)Fortran中的长整数

  • @fB:@MSB是正确的断言kind = 8不可移植,你说得对,它是标准的Fortran 90.语句的不可移植性产生了因为kind = 8是一个有效的选择器的解释8的实现依赖于实现.当然,大多数当前的编译器(我相信)会将其解释为8字节整数,但标准并不能保证这一点.所有Fortran编译器都会以同样的方式解释这一点并不总是如此,并且Fortran程序员的一个关注点是跨时间的可移植性. (3认同)