我已经"继承了"一个简单的同余伪随机数生成器,如下所示:
subroutine ribm(rndm,ial)
implicit none
integer :: ial
real :: rndm, al
ial=ial*65539
if(ial.lt.0) then
ial=ial+2147483647+1
endif
al=ial
rndm=al*0.4656613e-9
end subroutine ribm
ial
变量包含RNG种子,并在生成器外部初始化一次。像往常一样,生成器应该产生0到1之间的伪随机数。因此,ial
必须始终保持非负面。代码尝试使用if
语句强制执行此条件。
我使用以下程序测试发生器:
program testribm
implicit none
real :: r
integer :: i
data i /12345/
call ribm(r,i)
call ribm(r,i)
call ribm(r,i)
call ribm(r,i)
call ribm(r,i)
call ribm(r,i)
print *,r,i
contains
subroutine ribm(rndm,ial)
! [...]
end subroutine ribm
end program testribm
如果我使用优化gfortran
或更低的-O1
v4.8.2进行编译,我会得到:
0.580895185 1247462939
OTOH,如果我使用-O2
我得到:
-0.709615409 -1523887535
请注意负的种子值。优化会影响整数算术以及重复调用ribm
的最终结果。
我知道如何解决/解决这个问题。我的问题是:这是一个gfortran
错误吗?或者这种行为是由于代码是非标准的?但如果是这种情况,不应该gfortran
产生一些警告信息吗?
答案 0 :(得分:1)
优化可以改变实际完成的计算。如果结果更改为非标准计算(例如涉及整数溢出的计算),请不要感到惊讶。我建议在Fortran中使用无符号整数的算法是使用下一个更大的整数类型。例如,如果算法应使用无符号32位整数完成,请使用Fortran的带符号64位整数。
答案 1 :(得分:1)
我想补充一下M.S.B。答案,只有当编译器一次性优化对ribm
的多次调用时才会出现问题。 (我以前的建议无效。)
但原始问题的答案。 我认为它不是编译错误。 2147483647+1
只是超出范围。问题是为什么即使-pedantic -Wall -Wextra
也没有警告。事实证明,@ Arek'如果需要-Wstrict-overflow
,则仅在-Wstrict-overflow=1
中包含-Wall
。
如果可能,我建议使用函数iand
,ior
,ieor
,ishft
等类似于便携式结果,而不是算术。
答案 2 :(得分:0)
两个答案都很好,至于解决方案,我认为使用括号是防止编译器优化的正确方法吗?
subroutine ribm(rndm,ial)
implicit none
integer :: ial
real :: rndm, al
ial=ial*65539
if(ial.lt.0) then
ial=(ial+1)+2147483647
endif
al=ial
rndm=al*0.4656613e-9
end subroutine ribm