在进行一个简单的编程练习时,我生成了一个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在数字精度上有固有的局限性吗,还是我(很可能)是错误的?
我的编译器是gfortran(gcc版本4.1.2),perl v5.12.1,在一个双核AMDOpteron@1GHz上。