为什么我必须隐式指定 Fortran 中函数的双精度 return 值?
Why do I have to specify implicitly for a double precision return value of a function in Fortran?
我是 Fortran 新手,正在尝试 common
块。我的代码很简单
program main
implicit double precision (p)
real * 8 :: x, y
common /yvalue/ y
x = 3d0
y = 3d0
print *, power(x)
end program main
function power(x)
implicit none
real * 8 :: power
real * 8 :: x, y
common /yvalue/ y
power = x ** y
end function power
它有效,但如果我注释掉第二行,它隐式声明以 p
开头的变量为双精度,编译器会抱怨以下内容
Error: Return type mismatch of function ‘power’ at (1) (REAL(4)/REAL(8))
我确实明白 return 值 power
默认情况下是单精度变量,但为什么在函数中将 power
声明为双精度变量还不够?为什么在 main
中写 real * 8 power
也不起作用?
如评论中所述,只需在其自身范围内和调用它的范围内声明函数即可解决您的问题。但是,我也想劝阻您使用常见的隐式类型和完全非标准的 real*8。因此,这是您的程序的更现代方言版本
ian@eris:~/work/Whosebug$ cat power.f90
Program power_program
Implicit None
Integer, Parameter :: wp = Selected_real_kind( 14, 70 )
Real( wp ) :: x, y
x = 3.0_wp
y = 3.0_wp
! Return type and kind of the function power in scope
! due to the implicit interface
Write( *, '( 3( a, 1x, f0.6, 1x ) )' ) &
'x =', x, 'y = ', y, 'x**y = ', power( x, y )
Contains
Pure Function power( x, y ) Result( r )
Real( wp ) :: r
Real( wp ), Intent( In ) :: x
Real( wp ), Intent( In ) :: y
r = x ** y
End Function power
End Program power_program
ian@eris:~/work/Whosebug$ gfortran -std=f2003 -Wall -Wextra -O power.f90
ian@eris:~/work/Whosebug$ ./a.out
x = 3.000000 y = 3.000000 x**y = 27.000000
ian@eris:~/work/Whosebug$
当您尝试在代码中调用的过程(函数或子例程)位于外部您的program
主体并且不属于任何module
,它被命名为外部函数(或子例程)。
Fortran 是一种静态类型语言,因此必须在编译时知道所有变量和函数的类型。所以,如果你想在你的程序中引用一个外部函数,必须有一种方法让程序知道它的 return 类型。您有 3 个(不好的)选项,我会列出它们,从最差的开始:
- WORST:依赖隐式类型规则,该规则恰好将外部函数的 return 类型与与其在调用者中的标识符关联的类型相匹配(正如您在示例中所做的那样)。
为什么你不应该那样做? 因为它是癌症。它使代码的含义变得模糊,你无法知道这个名称引用的是什么。在某些情况下,它甚至可能看起来就像一个数组变量,而不是一个函数。此外,编译器在这种情况下不会检查参数一致性,因此如果您没有打开特定的编译器选项,代码将在 runtime 处失败,或者更糟的是,会给出错误结果。此外,如今隐式键入很少有用,大多数时候是自找麻烦。始终使用 implicit none
!
如您所述,根据隐式类型的默认规则,任何名称以 p
开头的变量都将默认为 real
类型(在您的编译器中,它是 real(4)
).当您将函数结果声明为 real*8
,而您的编译器将其解释为 real(8)
(请参阅最后的注释)时,就会出现错误。
- BAD:在调用者说明区声明函数的名称和类型。
你这样做就像声明一个变量一样:
program main
implicit none
real*8 :: x, y, power
顺便说一下,属性 external
可能适用于像您这样的外部程序。不仅仅是给过程一些属性(可以作为实际参数传递,与内部过程的歧义消除),它会使标识符的来源更清楚。
program main
implicit none
real*8 :: x, y, power
external :: power
为什么你不应该那样做?编译器也没有检查参数。这严重限制了您与外部函数通信的选项:参数不能是假定形状、假定秩、多态、参数化、coarray,也不能在被调用方声明为 allocatable
、optional
、pointer
、target
、asynchronous
、volatile
或 value
; return 类型不能是数组、指针或可分配的;该函数不能作为参数传递,为 elemental
,如果为 pure
,则不能在此类上下文中使用。而这一切的原因是缺少 显式接口 。
- ACCEPTABLE: 在调用者中为你的外部函数指定一个
interface
。
像这样:
program main
implicit none
interface
real*8 function power(y)
real*8 :: y
end function
end interface
这样,编译器就能知道声明的所有细节,我提到的所有限制都将不适用。完全自由和代码清晰!
为什么你不应该那样做?因为有更好的方法,那就是使用modules
!好吧,如果您不能使用模块,那么在上下文中执行此操作是完全可以的,例如使用已经存在的大型旧代码时。缺点是,你在两个不同的地方有几乎相同的代码,而且它们必须始终匹配。
奖励:更好:使用模块。
program main
use :: aux_module
implicit none
real*8 :: x, y
common /yvalue/ y
x = 3d0
y = 3d0
print *, power(x)
end
module aux_module
implicit none
contains
function power(x)
real*8 :: power
real*8 :: x, y
common /yvalue/ y
power = x ** y
end
end
为什么你绝对应该这样做?因为对于模块,接口是自动和隐式可用的(更少的代码重复,没有限制);模块可以单独重新编译和更新而不会破坏代码。此外,您可以在模块范围内声明共享变量,避免使用 common
声明。您的代码的更好版本是:
program main
use aux_module
implicit none
real*8 :: x
x = 3d0
y = 3d0
print *, power(x)
end
module aux_module
implicit none
real*8 :: y
contains
function power(x)
real*8 :: power
real*8 :: x
power = x ** y
end
end
甚至可以选择在 contains
之后将您的函数直接包含到 program
中。仅当您不打算在其他程序单元中重用此功能时才建议这样做。 @IanBush 的 涵盖了这种情况。
最后说明:查看 this answer 了解为什么语法 real*8
是非标准的并且应该避免。
我是 Fortran 新手,正在尝试 common
块。我的代码很简单
program main
implicit double precision (p)
real * 8 :: x, y
common /yvalue/ y
x = 3d0
y = 3d0
print *, power(x)
end program main
function power(x)
implicit none
real * 8 :: power
real * 8 :: x, y
common /yvalue/ y
power = x ** y
end function power
它有效,但如果我注释掉第二行,它隐式声明以 p
开头的变量为双精度,编译器会抱怨以下内容
Error: Return type mismatch of function ‘power’ at (1) (REAL(4)/REAL(8))
我确实明白 return 值 power
默认情况下是单精度变量,但为什么在函数中将 power
声明为双精度变量还不够?为什么在 main
中写 real * 8 power
也不起作用?
如评论中所述,只需在其自身范围内和调用它的范围内声明函数即可解决您的问题。但是,我也想劝阻您使用常见的隐式类型和完全非标准的 real*8。因此,这是您的程序的更现代方言版本
ian@eris:~/work/Whosebug$ cat power.f90
Program power_program
Implicit None
Integer, Parameter :: wp = Selected_real_kind( 14, 70 )
Real( wp ) :: x, y
x = 3.0_wp
y = 3.0_wp
! Return type and kind of the function power in scope
! due to the implicit interface
Write( *, '( 3( a, 1x, f0.6, 1x ) )' ) &
'x =', x, 'y = ', y, 'x**y = ', power( x, y )
Contains
Pure Function power( x, y ) Result( r )
Real( wp ) :: r
Real( wp ), Intent( In ) :: x
Real( wp ), Intent( In ) :: y
r = x ** y
End Function power
End Program power_program
ian@eris:~/work/Whosebug$ gfortran -std=f2003 -Wall -Wextra -O power.f90
ian@eris:~/work/Whosebug$ ./a.out
x = 3.000000 y = 3.000000 x**y = 27.000000
ian@eris:~/work/Whosebug$
当您尝试在代码中调用的过程(函数或子例程)位于外部您的program
主体并且不属于任何module
,它被命名为外部函数(或子例程)。
Fortran 是一种静态类型语言,因此必须在编译时知道所有变量和函数的类型。所以,如果你想在你的程序中引用一个外部函数,必须有一种方法让程序知道它的 return 类型。您有 3 个(不好的)选项,我会列出它们,从最差的开始:
- WORST:依赖隐式类型规则,该规则恰好将外部函数的 return 类型与与其在调用者中的标识符关联的类型相匹配(正如您在示例中所做的那样)。
为什么你不应该那样做? 因为它是癌症。它使代码的含义变得模糊,你无法知道这个名称引用的是什么。在某些情况下,它甚至可能看起来就像一个数组变量,而不是一个函数。此外,编译器在这种情况下不会检查参数一致性,因此如果您没有打开特定的编译器选项,代码将在 runtime 处失败,或者更糟的是,会给出错误结果。此外,如今隐式键入很少有用,大多数时候是自找麻烦。始终使用 implicit none
!
如您所述,根据隐式类型的默认规则,任何名称以 p
开头的变量都将默认为 real
类型(在您的编译器中,它是 real(4)
).当您将函数结果声明为 real*8
,而您的编译器将其解释为 real(8)
(请参阅最后的注释)时,就会出现错误。
- BAD:在调用者说明区声明函数的名称和类型。
你这样做就像声明一个变量一样:
program main
implicit none
real*8 :: x, y, power
顺便说一下,属性 external
可能适用于像您这样的外部程序。不仅仅是给过程一些属性(可以作为实际参数传递,与内部过程的歧义消除),它会使标识符的来源更清楚。
program main
implicit none
real*8 :: x, y, power
external :: power
为什么你不应该那样做?编译器也没有检查参数。这严重限制了您与外部函数通信的选项:参数不能是假定形状、假定秩、多态、参数化、coarray,也不能在被调用方声明为 allocatable
、optional
、pointer
、target
、asynchronous
、volatile
或 value
; return 类型不能是数组、指针或可分配的;该函数不能作为参数传递,为 elemental
,如果为 pure
,则不能在此类上下文中使用。而这一切的原因是缺少 显式接口 。
- ACCEPTABLE: 在调用者中为你的外部函数指定一个
interface
。
像这样:
program main
implicit none
interface
real*8 function power(y)
real*8 :: y
end function
end interface
这样,编译器就能知道声明的所有细节,我提到的所有限制都将不适用。完全自由和代码清晰!
为什么你不应该那样做?因为有更好的方法,那就是使用modules
!好吧,如果您不能使用模块,那么在上下文中执行此操作是完全可以的,例如使用已经存在的大型旧代码时。缺点是,你在两个不同的地方有几乎相同的代码,而且它们必须始终匹配。
奖励:更好:使用模块。
program main
use :: aux_module
implicit none
real*8 :: x, y
common /yvalue/ y
x = 3d0
y = 3d0
print *, power(x)
end
module aux_module
implicit none
contains
function power(x)
real*8 :: power
real*8 :: x, y
common /yvalue/ y
power = x ** y
end
end
为什么你绝对应该这样做?因为对于模块,接口是自动和隐式可用的(更少的代码重复,没有限制);模块可以单独重新编译和更新而不会破坏代码。此外,您可以在模块范围内声明共享变量,避免使用 common
声明。您的代码的更好版本是:
program main
use aux_module
implicit none
real*8 :: x
x = 3d0
y = 3d0
print *, power(x)
end
module aux_module
implicit none
real*8 :: y
contains
function power(x)
real*8 :: power
real*8 :: x
power = x ** y
end
end
甚至可以选择在 contains
之后将您的函数直接包含到 program
中。仅当您不打算在其他程序单元中重用此功能时才建议这样做。 @IanBush 的
最后说明:查看 this answer 了解为什么语法 real*8
是非标准的并且应该避免。