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

时间:2018-11-22 04:14:04

标签: 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
    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 power中写main也不起作用?

2 个答案:

答案 0 :(得分:3)

如注释中所述,仅在函数本身的范围内声明该函数,然后在调用该函数的范围内声明即可解决您的问题。但是,我也想劝阻您不要使用通用的隐式类型和完全非标准的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$ 

答案 1 :(得分:2)

当您试图在代码中调用的过程(函数或子例程)位于program主体的 之外,并且也不是任何module的一部分时,它被称为外部函数(或子例程)。

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


  1. 最坏的:依赖于隐式键入规则,该规则恰好将外部函数的返回类型与调用者中与其标识符关联的类型进行匹配(就像您在示例中所做的那样)。 / li>

为什么不应该这样做? 因为它是癌症。它使代码的含义晦涩难懂,您不知道此名称所指的是什么。在某些情况下,它甚至看起来像是数组变量,而不是函数。另外,在这种情况下,编译器不会检查参数的符合性,因此,如果您未打开特定的编译器选项,则代码将在运行时失败,或更糟糕的是,将给出错误的结果。而且,隐式键入这些天很少使用,在大多数情况下,这是自找麻烦。始终使用implicit none

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


  1. 不良:声明函数的名称并在调用者的规范区域中键入。

您将执行此操作,就像声明变量一样,

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

顺便说一句,属性external可以应用于像您这样的外部过程。除了为过程赋予一些属性(可以作为实际参数传递,与内在过程进行歧义消除)外,还可以使标识符的来源更加清晰。

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

为什么不应该这样做?编译器也没有参数检查。这严重限制了您与外部函数进行通信的选项:参数不能假定为正形,假定为秩,多态,参数化,协数组或在被调用方侧声明为allocatableoptional,{ {1}},pointertargetasynchronousvolatile;返回类型不能是数组,指针或可分配的;该函数不能作为参数传递,必须为value,如果为elemental,则不能在此类上下文中使用。造成这种情况的原因是缺少明确的界面


  1. 可接受的:在调用方中为您的外部功能指定一个pure

赞:

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中。仅当您不打算在其他程序单元中重用此功能时,才建议这样做。 @IanBush的answer涵盖了这种情况。

最后的注意:请看一下this answer,以了解为什么语法contains是非标准的,应该避免。