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

为什么我必须在Fortran中隐式指定函数的双精度返回值?,fortran,precision,fortran90,Fortran,Precision,Fortran90,我是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

我是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
开头的变量声明为双精度,编译器会抱怨如下

错误:函数“power”在(1)(实(4)/实(8))处的返回类型不匹配

我确实知道返回值
power
默认为单精度变量,但为什么在函数中将
power
声明为双精度是不够的?为什么在
main
中编写
real*8 power
也不起作用?

如评论中所述,简单地声明函数不仅在它自己的范围内,而且在它被调用的范围内,将解决您的问题。但是,我也不鼓励您使用普通的隐式类型和完全非标准的real*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$ 
当您试图在代码中调用的过程(函数或子例程)位于
程序
的主体之外,并且不是任何
模块
的一部分时,它被命名为外部函数(或子例程)

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


  • 最差:依赖一个隐式类型规则,该规则恰好将外部函数的返回类型与调用者中与其标识符关联的类型相匹配(就像您在示例中所做的那样)
  • 你为什么不应该那样做因为它是癌症。它使代码的含义变得模糊,你不知道这个名字指的是什么。在某些情况下,它甚至可能看起来像一个数组变量,而不是一个函数。此外,在这种情况下,编译器不会检查参数的一致性,因此如果没有打开特定的编译器选项,代码将在运行时失败,或者更糟,将给出错误的结果。此外,现在隐式键入很少有用,大多数时候都是自找麻烦。始终使用
    隐式无

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


  • 错误:在调用方的规范区域中声明函数的名称和类型 这就像声明变量一样,如下所示:

    program main
    implicit none
    real*8 :: x, y, power
    
    顺便说一句,属性
    external
    可以应用于像您这样的外部过程。它不仅为过程提供了一些属性(可以作为实际参数传递,从内在过程中消除歧义),还可以使标识符的来源更加清晰

    program main
    implicit none
    real*8 :: x, y, power
    external :: power
    
    你为什么不应该那样做?编译器也没有检查参数。这严重限制了您与外部函数通信的选项:参数不能被假定为形状、假定秩、多态、参数化、协同排列,也不能在被调用方声明为
    可分配
    可选
    指针
    目标
    异步
    易失性
    ;返回类型不能是数组、指针或可分配的;函数不能作为参数传递,不能是
    基本的
    ,如果是
    纯的
    ,则不能在此类上下文中使用。所有这一切的原因是缺乏明确的接口


  • 可接受:为调用者中的外部函数指定一个
    接口
  • 像这样:

    program main
    implicit none
    interface
      real*8 function power(y)
        real*8 :: y
      end function
    end interface
    
    这样,编译器就能够知道声明的所有细节,而我提到的所有限制都不适用。完全自由和代码清晰

    你为什么不应该那样做?因为有更好的方法,那就是使用
    模块
    !好吧,在你不能使用模块的情况下这样做是完全可以的,例如,在处理已经存在的大型旧代码时。缺点是,您在两个不同的地方有几乎相同的代码,并且它们必须始终匹配


    奖励:更好:使用模块

    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
    

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


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

    在我的主要著作中明确声明
    power
    real*8
    。由于您正在学习Fortran,我建议您远离“功能”,如
    COMMON
    、隐式增量和
    real*8
    。注意real*8不是标准Fortran的一部分,从来不是标准Fortran的一部分,如果你是这样想的话,我担心剩下的教学质量——特别是如果它也涵盖了普通和隐式的打字,那些不应该出现的东西