与其他语言相比,Fortran是否对数值精度有固有限制? [英] Does Fortran have inherent limitations on numerical accuracy compared to other languages?
问题描述
在进行简单的编程练习时,我创建了一个while循环(Fortran中的DO循环),当真实变量达到精确值时,它将退出。
<我注意到,由于使用了精度,平等从未被满足,循环变得无限。这当然不是闻所未闻,有人建议说,不要比较两个数字的平等性,最好看看两个数字之间的绝对差值是否小于设定的阈值。
我发现令人失望的是我必须设置这个阈值,即使对于双精度的变量,我的循环能够正确退出的程度也很低。此外,当我在Perl中重写了这个循环的蒸馏版本时,我没有数值准确性的问题,并且循环退出正常。
由于生成问题是如此之小,在Perl和Fortran中,我希望在这里重现它,以防止我忽略了一个重要细节:
Fortran代码
PROGRAM precision_test
IMPLICIT NONE
!数据字典
INTEGER :: count = 0!迭代次数
REAL(KIND = 8):: velocity
REAL(KIND = 8),PARAMETER :: MACH_2_METERS_PER_SEC = 340.0
velocity = 0.5 * MACH_2_METERS_PER_SEC !初始速度
DO
WRITE(*,300)速度
300 FORMAT(F20.8)
IF(count == 50)EXIT
IF(速度== 5.0 * MACH_2_METERS_PER_SEC)退出
! 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
结束程序precision_test
Perl代码
#! / usr / bin / perl -w
使用strict;
my $ mach_2_meters_per_sec = 340.0;
my $ velocity = 0.5 * $ mach_2_meters_per_sec;
while(1){
printf%20.8f\\\
,$ 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
-
正确的方法
-
错误的方式
-
最大功率方式
The Right Way
The Wrong Way
The Max Power Way
b
这不正是错误的方式吗?
是的,但速度更快!
所有开玩笑的人,我一定在做错事。
Fortran与其他语言相比,在数值精度上有固有限制吗?我的编译器是gfortran(gcc版本4.1.2),Perl v5.12.1,在双核AMD Opteron @ 1 GHZ上。
/ p>您的任务意外地将该值转换为单精度,然后返回到double。
尽量让你的 0.1 *
为 0.1D0 *
,你应该看到你的问题得到解决。 p>
While working on a simple programming exercise, I produced a while loop (DO loop in Fortran) that was meant to exit when a real variable had reached a precise value.
I noticed that due to the precision being used, the equality was never met and the loop became infinite. This is, of course, not unheard of and one is advised that, rather than comparing two numbers for equality, it is best see if the absolute difference between two numbers is less than a set threshold.
What I found disappointing was how low I had to set this threshold, even with variables at double precision, for my loop to exit properly. Furthermore, when I rewrote a "distilled" version of this loop in Perl, I had no problems with numerical accuracy and the loop exited fine.
Since the code to produce the problem is so small, in both Perl and Fortran, I'd like to reproduce it here in case I am glossing over an important detail:
Fortran Code
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 Code
#! /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;
}
The commented-out line in Fortran is what I would need to use for the loop to exit normally. Notice that the threshold is set to 1E-4, which I feel is quite pathetic.
The names of the variables come from the self-study-based programming exercise I was performing and don't have any relevance.
The intent is that the loop stops when the velocity variable reaches 1700.
Here are the truncated outputs:
Perl Output
170.00000000
204.00000000
238.00000000
272.00000000
306.00000000
340.00000000
...
1564.00000000
1598.00000000
1632.00000000
1666.00000000
1700.00000000
Fortran Output
170.00000000
204.00000051
238.00000101
272.00000152
306.00000203
340.00000253
...
1564.00002077
1598.00002128
1632.00002179
1666.00002229
1700.00002280
What good is Fortran's speed and ease of parallelization if its accuracy stinks? Reminds me of the three ways to do things:
"Isn't that just the wrong way?"
"Yeah! But faster!"
All kidding aside, I must be doing something wrong.
Does Fortran have inherent limitations on numerical accuracy compared to other languages, or am I (quite likely) the one at fault?
My compiler is gfortran (gcc version 4.1.2), Perl v5.12.1, on a Dual Core AMD Opteron @ 1 GHZ.
Your assignment is accidentally converting the value to single precision and then back to double.
Try making your 0.1 *
be 0.1D0 *
and you should see your problem fixed.
这篇关于与其他语言相比,Fortran是否对数值精度有固有限制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!