【发布时间】:2021-10-26 05:46:18
【问题描述】:
我正在尝试在 Fortran 中实现 Bisection 方法子例程来解决计算科学程序,而 Fortran 正在做一些奇怪的事情。因此,程序的目标是找到某个参数e0 的超越方程的解,该参数在 for 循环中的每一步都更新并传递给子程序。
问题是:
-
e0未按应有的方式更新。e0的值在一次迭代中从 0.1799... 变为 0.8999...,而理论上它应该需要程序 9 次迭代才能到达那里。为什么会这样? - 打印语句未按应有的方式打印(请参见下面的输出)。我们期望“out 1”打印,一些“in”打印(当子程序
f1被子程序Bisection调用时打印),然后是“out 2”打印(使用新的 e0 值),一些“in”打印等。但我们看到第一个“out 1”打印,一些“in”打印,然后只有“out”打印。这是否意味着子程序f1仅在“out 1”和“out 2”之间调用? (应该在每次“输出”打印之间调用)
我使用 Fortran77 进行数值求解已经有几年了,从来没有遇到过这样的事情,但是我已经快 6 个月没有编程了,所以我可能错过了一件重要的事情。
代码:
program roots
implicit none
double precision A,B,eps,e0,e1
integer i,niter
external f1
A = 1d-1
B = 9d-1
eps = 10d-6
open(9,file='dades.dat',status='old')
do i=1,9
e0 = i*(B-A)/10 + A
c Here is the first print. 'Out' meaning outside the subroutine
print *, 'out', i, e0
call Bisection(A,B,eps,f1,niter,e1,e0)
write (9,'(2(f10.5))') e0,e1
end do
close(9)
end program roots
subroutine Bisection(A,B,eps,f,niter,xroot,e0)
implicit none
double precision A,B,eps,xroot,fuc,fua,e0
integer niter,i
niter = nint(log((B-A)/eps)/log(2.))+1
do i=1,niter
xroot = (A+B)/2
c Here the subroutine which uses e0 is called twice
call f(xroot,fuc,e0)
call f(A,fua,e0)
if (fuc .eq. 0) return
if (fuc*fua .lt. 0.) then
B = xroot
else
A = xroot
end if
end do
return
end subroutine Bisection
subroutine f1(x,f,e0)
implicit none
double precision x,f,e0
c Here is the second print. 'In' meaning inside the subroutine
print *, 'in', e0
f = e0+1
end subroutine f1
输出:
out 1 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
in 0.17999999999999999
out 2 0.89999511718750003
out 3 0.89999572753906254
out 4 0.89999633789062505
out 5 0.89999694824218746
out 6 0.89999755859374997
out 7 0.89999816894531248
out 8 0.89999877929687500
out 9 0.89999938964843751
【问题讨论】:
-
我删除了fortran77 标签。代码至少为 Fortran 90。
-
“这在这个例子中看不到” - 如果您要查看这个,请创建一个确实显示问题的例子。
-
新的 F90+ 代码中任何常见块的问题的解决方案是:不要使用常见块。没有令人信服的理由使用空白通用来解决这个编程问题,这可能会减少您在这里帮助您的人可能会看到的工作量。请考虑使用“现代”编程结构。 (并且调用子程序
f和fun等会让很多人感到困惑 -
在进入子程序之前,你只需要在
outdo-loop 中设置e0。e0永远不会在子例程中更新。 -
@JordiFuentes 在过去的 25 年中不应该在新代码中使用容易出错的公共块。如果您确实必须拥有全局数据(这不是一个好主意),请使用可以执行与 common 类似的事情但不易出错的模块。最好的方法是通过参数列表,就像您在更新中所做的那样,但是您仍然应该真正了解模块并将所有子程序合二为一——这是现代的、不易出错的方式;你现在正在做的是在没有安全带或刹车的情况下以 100 公里/小时的速度行驶。
标签: fortran