可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm working on a project that needs to implement few numerical methods in Fortran. For this, I need to write some recursive functions. Here is my code.
!
! File: main.F95
!
RECURSIVE FUNCTION integrate(n) RESULT(rv)
IMPLICIT NONE
DOUBLE PRECISION :: rv
INTEGER, INTENT(IN) :: n
DOUBLE PRECISION, PARAMETER :: minusone = -1.0
IF (n == 1) THEN
rv = 10 !exp(minusone)
RETURN
ELSE
rv = 1 - (n * integrate(n - 1))
RETURN
END IF
END FUNCTION integrate
RECURSIVE FUNCTION factorial(n) RESULT(res)
INTEGER res, n
IF (n .EQ. 0) THEN
res = 1
ELSE
res = n * factorial(n - 1)
END IF
END
PROGRAM main
DOUBLE PRECISION :: rv1
PRINT *, factorial(5)
PRINT *, integrate(2)
!READ *, rv1
END PROGRAM main
For this program the output is:
NaN
1
If I change the order of the print statements (line 30 & 31), the output will be:
1
-19.000000
Output should be (for the original print statement order):
120
-19
I took the factorial function from the Wikipedia Fortran 95 language features page.
- Compiler : gfortran 4.5.3 with Cygwin
- IDE: Netbeans 7.0.1
- Platform: Windows 7
回答1:
Your functions are written correctly. The problem is in the main program, where you do not explicitly declare the type of integrate
and factorial
functions, so you have implicit typing, in which case factorial
is assumed REAL
and integrate
is assumed INTEGER
. For some reason, your compiler did not warn you about type mismatch. Mine did:
$ gfortran recurs.f90
recurs.f90:26.22:
PRINT *, integrate(2)
1
Error: Return type mismatch of function 'integrate' at (1) (INTEGER(4)/REAL(8))
recurs.f90:27.22:
PRINT *, factorial(5)
1
Error: Return type mismatch of function 'factorial' at (1) (REAL(4)/INTEGER(4))
You should change your main program to:
PROGRAM main
IMPLICIT NONE
DOUBLE PRECISION, EXTERNAL :: integrate
INTEGER, EXTERNAL :: factorial
PRINT *, factorial(5)
PRINT *, integrate(2)
END PROGRAM main
Notice the IMPLICIT NONE
line. This declaration statement will disable any implicit typing, and the compiler would throw an error if not all variables and functions are explicitly declared. This is a very important line in every Fortran program, and if you had it, you would've figured out your problem yourself, because it would force you to explicitly declare everything in your program.
The output now is:
120
-19.0000000000000
as expected.
As a side note, the DOUBLE PRECISION
type declaration is not as flexible as using REAL
with KIND
parameter specified instead, e.g. anREAL(KIND=myRealKind)
. See answers to this question about how to use KIND
properly: Fortran 90 kind parameter.
回答2:
As one of the comments mentions, a better solution is to put your subroutines and functions into a module, then use that module from your main program. This will make the interface of those procedures known to the caller -- "explicit" in Fortran terminology. Not only will the compiler correctly handle the type of the function, it will be able to check type-agreement between the arguments in the call and the arguments in the callee ("dummy arguments") for consistency.
If you use as many debugging options as possible the compiler will help you find mistakes. With gfortran, try: -O2 -fimplicit-none -Wall -Wline-truncation -Wcharacter-truncation -Wsurprising -Waliasing -Wimplicit-interface -Wunused-parameter -fwhole-file -fcheck=all -std=f2008 -pedantic -fbacktrace
module factorial_procs
IMPLICIT NONE
contains
RECURSIVE FUNCTION integrate(n) RESULT(rv)
DOUBLE PRECISION :: rv
INTEGER, INTENT(IN) :: n
IF (n == 1) THEN
rv = 10
RETURN
ELSE
rv = 1 - (n * integrate(n - 1))
RETURN
END IF
END FUNCTION integrate
RECURSIVE FUNCTION factorial(n) RESULT(res)
INTEGER res, n
IF (n .EQ. 0) THEN
res = 1
ELSE
res = n * factorial(n - 1)
END IF
END
end module factorial_procs
PROGRAM main
use factorial_procs
implicit none
PRINT *, factorial(5)
PRINT *, integrate(2)
END PROGRAM main
You'll probably find that you can only calculate factorials of very small integers by straight forward multiplication using regular integers. One fix is to use a larger integer type, e.g.,
integer, parameter :: BigInt_K = selected_int_kind (18)
Just as you could modernize and use selected_real_kind instead of Double Precision.
回答3:
I would like to highlight some points while using RECURSIVE functions or non recursive function.
1. Make sure the function has an explicit interface with the calling program. This can be achieved by putting function in a module and USE association. This is explained in aforementioned answer.
2. you can use INTERFACE to build an explicit interface with the main calling program, this is useful when you have a very few numbers of function which have implicit interface, like in your case. The example is given below.
PROGRAM main
IMPLICIT NONE
INTERFACE
FUNCTION factorial(n)
INTEGER:: factorial
INTEGER, INTENT(IN):: n
END FUNCTION factorial
FUNCTION integrate(n)
DOUBLE PRECISION:: integrate
INTEGER, INTENT(IN):: n
END FUNCTION integrate
END INTERFACE
PRINT *, factorial(5)
PRINT *, integrate(2)
END PROGRAM main
note it always better to define kind parameter and then using KIND clause as!explained by @milancurcic
There is another very simple way to solve your problem:
Just define factorial and itegrate in the main program as follows and you are ready to go
PROGRAM main
IMPLICIT NONE
DOUBLE PRECISION :: integrate
INTEGER:: factorial
PRINT *, factorial(5)
PRINT *, integrate(2)
END PROGRAM main