Почему я должен неявно указывать возвращаемое значение функции двойной точности в Фортране? - PullRequest
0 голосов
/ 22 ноября 2018

Я новичок в Фортране и пробую блок 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 голосов
/ 22 ноября 2018

Когда процедура (функция или подпрограмма), которую вы пытаетесь вызвать в своем коде, лежит на вне тела вашего program, а также не является частью какого-либо module, она называется внешняя функция (или подпрограмма).

Фортран - это язык статического типа, поэтому типы всех переменных и функций должны быть известны во время компиляции.Поэтому, если вы хотите сослаться на внешнюю функцию в вашей программе, у программы должен быть способ узнать ее тип возврата.У вас есть 3 ( плохие ) опции для этого, и я перечислю их, начиная с худшего:


  1. WORST : положитесь направило неявной типизации, которое совпадает с типом возвращаемого значения внешней функции с типом, связанным с ее идентификатором в вызывающей стороне (как вы это сделали в своем примере).

Почему вы не должныне так ли? Потому что это рак .Это делает смысл кода неясным, вы не можете знать, на что ссылается это имя.В некоторых случаях он может даже выглядеть как переменная массива, а не как функция.Кроме того, компилятор не проверяет соответствие аргументов в этом случае, поэтому, если у вас не включены конкретные параметры компилятора, код не будет работать в время выполнения или хуже, даст неправильные результаты.Более того, неявная типизация очень и очень редко используется в наши дни, в большинстве случаев это проблема.Всегда используйте 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

Почему бы вам не следуетсделать это? Компилятор также не проверяет аргументы.Это сильно ограничивает ваши возможности для связи с внешними функциями: аргументы не могут быть заданы в форме, предполагаемом ранге, полиморфными, параметризованными, грубыми или быть объявлены на стороне вызываемого как allocatable, optional, pointer, target, asynchronous, volatile или value;возвращаемый тип не может быть массивом, или указателем, или размещаемым;функция не может быть передана в качестве аргумента, быть elemental и, если pure, не может использоваться в таких контекстах.И причиной всего этого является отсутствие явного интерфейса .


ПРИНЯТО : Укажите interface для вашей внешней функции в вызывающем абоненте.

Как это:

program main
implicit none
interface
  real*8 function power(y)
    real*8 :: y
  end function
end interface

Таким образом, компиляторможет знать все детали декларации и все упомянутые ограничения не будут применяться.Полная свобода и ясность кода!

Почему вы не должны этого делать? Потому что есть лучший способ, который использует modules!Ну, это вполне нормально делать в тех случаях, когда вы не можете использовать модули, например, при работе с уже существующим большим старым кодом.Недостатком является то, что у вас есть почти одинаковый код в двух разных местах, и они должны всегда совпадать.


Бонус: BETTER : использовать модули.

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.Это рекомендуется, только если вы не планируете повторно использовать эту функцию в других программных модулях.@ 1096 * ответ IanBush охватывает этот случай.

Последнее замечание: посмотрите на этот ответ , чтобы понять, почему синтаксис real*8 нестандартен и его следует избегать.

0 голосов
/ 22 ноября 2018

Как указано в комментариях, простое объявление функции не только в своей собственной области, но и в той области, которую она вызывает, решит вашу проблему.Однако я также хочу отговорить вас от использования обычной неявной типизации и совершенно нестандартного реального * 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$ 
...