Fortran 程序没有正确更新变量的值
Fortran program doesn't update a variable's value properly
我正在尝试在 Fortran 中实现二分法子例程来解决计算科学程序,而 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
深入查看代码后,我终于找到了问题所在。问题是变量 A
和 B
都是输入和输出,因为它们在子程序内部被修改。因此,当第一次调用子程序时,它会更改 A
和 B
的值(这与代码开头设置的值不同)。其余都是由此引发的问题。
解决方案是在每次迭代开始时重置值,如下所示:
program roots
implicit none
double precision A,B,eps,e0,e1
integer i,niter
external f1
eps = 10d-6
open(9,file='dades.dat',status='old')
do i=1,9
A = 1d-1
B = 9d-1
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
我正在尝试在 Fortran 中实现二分法子例程来解决计算科学程序,而 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
深入查看代码后,我终于找到了问题所在。问题是变量 A
和 B
都是输入和输出,因为它们在子程序内部被修改。因此,当第一次调用子程序时,它会更改 A
和 B
的值(这与代码开头设置的值不同)。其余都是由此引发的问题。
解决方案是在每次迭代开始时重置值,如下所示:
program roots
implicit none
double precision A,B,eps,e0,e1
integer i,niter
external f1
eps = 10d-6
open(9,file='dades.dat',status='old')
do i=1,9
A = 1d-1
B = 9d-1
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