为什么我必须隐式指定Fortran中函数的双精度返回值?

问题描述 投票:1回答:2

我是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))

我确实认为返回值power默认是一个单精度变量,但为什么在函数中将power声明为双精度是不够的?为什么在real * 8 powermain也不行?

fortran precision fortran90
2个回答
2
投票

当您尝试在代码中调用的过程(函数或子例程)位于program的主体外部并且也不属于任何module时,它被命名为外部函数(或子例程)。

Fortran是一种静态类型语言,因此必须在编译时知道所有变量和函数的类型。因此,如果要在程序中引用外部函数,必须有一种方法让程序知道它的返回类型。你有3个(坏)选项,我会列出它们,从最坏的开始:


  1. 最糟糕的情况:依赖于隐式输入规则,该规则恰好与外部函数的返回类型与调用者中与其标识符关联的类型相匹配(如您在示例中所做的那样)。

为什么你不应该这样做?因为它是癌症。它使代码的含义模糊不清,你无法知道这个名称的引用。在某些情况下,它甚至可能看起来像一个数组变量,而不是函数。此外,在这种情况下,编译器不检查参数一致性,因此如果您没有打开特定的编译器选项,代码将在运行时失败,或者更糟糕的是,将给出错误的结果。此外,隐式打字现在很少有用,大部分时间它都是一个问题。总是使用implicit none

正如您所指出的,通过隐式类型的默认规则,任何名称以p开头的变量都将是默认的real类型(在编译器中,它是real(4))。当您将函数结果声明为real*8时,您的编译器将其解释为real(8)(请参阅最终注释),则会出现错误。


  1. BAD:在调用者的规范区域中声明函数的名称和类型。

你就像你声明一个变量一样,就像这样:

program main
implicit none
real*8 :: x, y, power

顺便说一下,属性external可以应用于像你这样的外部程序。除了给过程提供一些属性(可以作为实际参数传递,从内部过程消除歧义),它将使标识符的起源更清晰。

program main
implicit none
real*8 :: x, y, power
external :: power

为什么你不应该这样做?编译器也没有参数检查。这严重限制了您与外部函数通信的选项:参数不能被假定为 - 形状,假设 - 等级,多态,参数化,共同,或者在被调用方声明为allocatableoptionalpointertargetasynchronousvolatilevalue ;返回类型不能是数组,指针或可分配的;该函数不能作为参数传递,如elemental,如果pure,则不能在此类上下文中使用。所有这一切的原因是缺乏明确的界面。


  1. 可接受:在调用者中为外部函数指定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

program之后,甚至可以选择将您的功能直接包含在contains中。仅当您不打算在其他程序单元中重复使用此功能时,才建议使用此选项。 @ IanBush的answer涵盖了这个案例。

最后注意:看看this answer,看看为什么语法real*8是非标准的,应该避免。


3
投票

正如评论中所述,只是声明函数不仅在其自己的范围内,而且在其调用的范围内将解决您的问题。但是我也想阻止你使用普通的,隐式的输入和完全非标准的真实* 8。因此,这是一个更现代的方言的程序版本

ian@eris:~/work/stackoverflow$ 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/stackoverflow$ gfortran -std=f2003 -Wall -Wextra -O power.f90
ian@eris:~/work/stackoverflow$ ./a.out
x = 3.000000 y =  3.000000 x**y =  27.000000
ian@eris:~/work/stackoverflow$ 
© www.soinside.com 2019 - 2024. All rights reserved.