Хорошо, теперь, чтобы дополнить ответ @Jerry101, я написал модифицированный код. Здесь ключевая проблема заключается в том, что IMAXINV
явно не объявлено как REAL
, поэтому оно интерпретируется как INTEGER
(в результате IMAXINV = 1.0 / IMAX
всегда становится 0 в исходном коде). Кроме того, я удалил ISEED
из блока COMMON
(поскольку он передается в качестве аргумента) и поместил еще один оператор COMMON
в RANDU
для совместного использования переменных между подпрограммами. С этими модификациями программа работает корректно.
PROGRAM RANDOM
COMMON RANDOMNUMBER !<--- ISEED is deleted from here
ISEED = 123
J=1
7 CALL RANDU(ISEED)
J=J+1
WRITE(*,*) RANDOMNUMBER !<--- write to STDOUT for test
IF (J < 100) GOTO 7
END
SUBROUTINE RANDU(ISEED)
real IMAXINV !<--- this is necessary
COMMON RANDOMNUMBER !<--- this is also necessary to share variables
PARAMETER (IMAX = 2147483647, IMAXINV = 1./IMAX)
ISEED = ISEED * 65539
IF(ISEED<0) ISEED = ISEED + IMAX + 1
RANDOMNUMBER = ISEED * IMAXINV
END
Как было предложено в другом ответе, мы также могли бы использовать FUNCTION
для прямого возврата переменной. Тогда нам не нужно использовать COMMON
, поэтому код станет немного чище.
PROGRAM RANDOM
ISEED = 123
J=1
7 RANDOMNUMBER = RANDU(ISEED)
J=J+1
WRITE(*,*) RANDOMNUMBER
IF (J < 100) GOTO 7
END
FUNCTION RANDU(ISEED)
real IMAXINV
PARAMETER (IMAX = 2147483647, IMAXINV = 1./IMAX)
ISEED = ISEED * 65539
IF(ISEED<0) ISEED = ISEED + IMAX + 1
RANDU = ISEED * IMAXINV !<--- "RANDU" is the return variable
END
Но обратите внимание, что при использовании FUNCTION
тип возвращаемой переменной должен быть явно объявлен в вызывающей подпрограмме, если имя функции не соответствует неявному правилу. (В приведенном выше коде RANDU
не объявляется явно, поскольку интерпретируется как REAL
). Так или иначе, в правиле неявной типизации в Fortran77 есть много предостережений...
Дополнительные примечания:
Чтобы избежать этих ловушек, я предлагаю использовать Fortran >=90 (а не Fortran77), поскольку он предоставляет множество возможностей для предотвращения таких ошибок. Например, минимально модифицированный код может выглядеть так:
module mymodule
contains
subroutine randu ( istate, ran )
implicit none
integer, parameter :: IMAX = 2147483647
real, parameter :: IMAXINV = 1.0 / IMAX
integer, intent(inout) :: istate
real, intent(out) :: ran
istate = istate * 65539
if ( istate < 0 ) istate = istate + IMAX + 1
ran = istate * IMAXINV
end subroutine
end module
program main
use mymodule, only: randu
implicit none
integer :: j, istate
real :: randomnumber
istate = 123 !! seed for RANDU()
do j = 1, 99
call randu ( istate, randomnumber )
write(*,*) randomnumber
enddo
end program
Здесь,
implicit none
используется для явного объявления всех переменных. Это полезно, чтобы избежать неправильного ввода переменных (например, IMAXINV
в вопросе!).
- Подпрограмма
RANDU
содержится в module
, так что компилятор предоставляет явный интерфейс и множество полезных проверок (вкратце, module
— это что-то вроде пространства имен в C++). module
также можно использовать для определения глобальных переменных более безопасным способом, чем COMMON
.
- Я использовал конструкцию
do
... enddo
для перебора j
вместо того, чтобы увеличивать ее вручную и использовать goto
. Первый на самом деле проще в использовании, а также goto
часто делает код менее читаемым...
- Я назвал программный файл «test.f90» (обратите внимание на суффикс .f90), что позволяет использовать его в свободном формате. Кроме того, можно использовать строчные буквы для переменных.
- [Кроме того, поскольку
iseed
хранит информацию о текущем состоянии (псевдо) генератора случайных чисел, может быть лучше использовать какое-то другое имя переменной (например, istate и т. д.), чтобы напомнить, что ее значение необходимо сохранять во время вызовов.]
Поэтому, если вы заинтересованы, рассмотрите возможность использования более современной версии Fortran (а не Fortran77), которая позволяет нам писать более безопасные и надежные коды :)
person
roygvib
schedule
18.10.2015