我是Fortran的新手,用一个从Celsius返回Farenheit的函数编写一些练习代码
program Console1
implicit none
real, parameter :: ikind = selected_real_kind(p=15)
real (kind = ikind):: c,f,o,faren
print *, "enter a temperature in degrees celsius"
read *, c
write(*,10) "farenheit =", faren(c)
10 format(a,f10.8)
end program Console1
function faren(c)
real, parameter :: ikind = selected_real_kind(p=15)
real (kind = ikind):: c,f
faren = (9/5)*c + 32
end function faren
我收到错误#7977:函数引用的类型与函数定义的类型不匹配。
因此,如果我将function faren(c)
更改为real function faren(c)
我得到了同样的错误,但类型是一样的吗?
我错过了什么吗?我是否必须在主程序中定义该功能?
答案 0 :(得分:2)
除了已经注意到的结构/代码安排之外,还有几个问题。
首先,KIND
是一个整数,因此您想要更改
real, parameter :: ikind = selected_real_kind(p=15)
到
integer, parameter :: ikind = selected_real_kind(p=15)
理想情况下,您只想在一个地方(即模块中)定义它,并从主程序和函数中引用它,但代码应该没问题,因为它是用于测试目的。
第二个经常让Fortran(和Python2)的新手绊倒的问题是实数和整数是不同的类型,通常不可互换。
faren = (9/5)*c + 32
简化为
faren = (1)*c + 32
因为整数除法有整数结果; 9/5 = 1
Fortran对数值(这就是语言的整个点)很挑剔,所以你可能想要的是:faren = (9.0 / 5.0) * c + 32.0
或者更准确地说,如果使用faren
的特定精度定义ikind
,
faren = (real(9.0,ikind) / real_(5.0,ikind)) * c + real(32.0,ikind)
或
faren = (9.0_ikind / 5.0_ikind) * c + 32.0_ikind
这种语法往往会让人们的头脑爆炸。欢迎来到现代fortran;)
最后一期涉及Fortran I / O的恐怖事件。从设计的角度来看,您需要知道用户期望的结果,并确保输出格式可以显示它们。 c
的合法输入值范围是-273.15(给予或接受)某个上限,它取决于代码的用例。如果你正在处理烹饪温度,你可能不会超过400.0;如果你正在做融合研究,你可能会走得更远。 8位十进制数字是否有用或可信?在这种情况下,我们只是测试代码,因此我们可能不需要输出中的大量精度;您想要将输出格式更改为:
10 format(a,es10.2)
或
10 format(a,g16.8)
您需要确保总字段宽度(点前的数字)可以包含小数部分(点后面的数字)以及数字的整数部分,以及显示符号和指数所需的空间。对于科学记数法,四个字符被尾数符号,小数点,E' E吃掉。和指数的标志。刚开始使用*
的输出格式可能更安全;同时与数字和格式化斗争令人沮丧。
答案 1 :(得分:1)
这是一个很好的努力和简单的开始,通过细微差别,这是一个很好的问题。
我个人会使用实数来计算数学,而不是9/5,并使用模块。在这个例子中,你可以传入一个实数或一个双精度到C2Faren,接口/过程将挑选出是使用真实还是双重版本。然后你有几个选项,以防你需要不同的精度。
如果您使用混合语言,也可以使用ISO_C_BINDING ...
MODULE MyTEMPS
PRIVATE
DOUBLE PRECISION, PARAMETER :: C2F_ScaleFact = 1.8D0
DOUBLE PRECISION, PARAMETER :: F2C_ScaleFact = /(1.0D0 / 1.8D0)/
DOUBLE PRECISION, PARAMETER :: F2C_Offset = 32.0D0
PUBLIC Faren2C
INTERFACE C2Faren
MODULE PROCEDURE C2Faren_Real, C2Faren_DBL
END INTERFACE
CONTAINS
!========= REAL VERISON =========
REAL FUNCTION C2Faren_Real(c)
IMPLICIT NONE
real, INTENT(IN ) :: c
C2Faren_Real = ( C*F2C_ScaleFact ) + F2C_Offset
RETURN
END FUNCTION C2Faren_Real
!========= DOUBLE VERSION =========
DOUBLE PRECISION FUNCTION C2Faren_DBL(c)
IMPLICIT NONE
DOUBLE PRECISION , INTENT(IN ) :: c
C2Faren_DBL = ( C*F2C_ScaleFact ) + F2C_Offset
RETURN
END FUNCTION C2Faren_DBL
!========= REAL VERSION (Faren to Centigrade) =========
REAL FUNCTION faren2C(Faren)
IMPLICIT NONE
REAL, INTENT(IN ) :: Faren
faren2C = (faren - F2C_Offset) / F2C_ScaleFact
RETURN
END FUNCTION faren2C
END MODULE MyTEMPS
然后你的程序通过第二行使用模块......
program Console1
USE MyTEMPS !<== Here
implicit none
real :: c, f
DOUBLE PRECISION :: Dc, Df ! No way to get Df to C or DC in the module (yet)!
print *, "enter a temperature in degrees celsius"
read *, c
write(*,10) "farenheit =", C2faren(c)
10 format(a,f10.6)
Dc = C
write(*,12) "farenheit =", C2faren(Dc)
12 format("DBL:",A,f10.6)
F = Dc
write(*,14) "Centigrade =", faren2C(F)
14 format("DBL:",A,f10.6)
end program Console1
所以/和模块的主要优点是当你最终想要在各种程序中使用这些东西并测试和整理模块一次...通常人们把这种东西(很多模块)在库中,当模块有很多功能时。
你也可以将真实的参数:: ikind = selected_real_kind(p = 15)放入一个模块中,并在程序和函数中使用它,你会在那里。你真的很亲密,而且主要是风格和实用性问题。
对于英特尔Fortran,您可以使用REAL(KIND = 4)和REAL(KIND = 8)......我这样做,但这不能移植到gfortran,所以使用ISO_C_BINDING或者只是一个更好的习惯使用REAL和DOUBLE PRECISION。
答案 2 :(得分:1)
模块很棒但是如果你有一个非常简单的代码,另一种工作方式是将子程序和函数放在主程序中。诀窍是将它们放在单词contains:
之后program xxx
stuff
contains
subroutine yyy
function zzz
end program xxx
通过这种方式,这些功能可以查看主程序的内容,因此您不必重新声明您的参数,并且您可能会收到更有意义的错误消息。
既然你是新手,我有很多资源,我从分享中学到了很多东西: http://www.uv.es/dogarcar/man/IntrFortran90.pdf