在进行一个简单的编程练习时,我制作了一个 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 的准确性很差,那么 Fortran 的速度和易于并行化有什么好处?提醒我做事的三种方式:
正确的方式
错误的方法
最大功率方式
“那不是走错路了吗?”
“是啊!但更快!”
除了开玩笑,我一定做错了什么。
与其他语言相比,Fortran 在数值准确性方面是否存在固有限制,还是我(很可能)是错误的?
我的编译器是 gfortran(gcc 版本 4.1.2),Perl v5.12.1,在双核 AMD Opteron @ 1 GHZ 上。