坠机的直接原因:
优化步骤期间的编译器可以很容易地证明hgt+1
和hgng
不能相同,因此它将整个条件优化为.false.
。两个正整数相加不能产生负整数。
您可以通过使用较小级别的优化或使用临时变量进行一些伪装来避免它,这些临时变量会比他们在旧的数字食谱中所做的更复杂。您可以尝试使用-fwrapv
or-fno-strict-overflow
标志。但它非常不确定,并且只能在具有某些编译器标志的编译器的给定版本中工作。
最简单的“修复”可能只是删除有问题的检查。它很可能在许多情况下都有效(而在其他情况下则非常失败)。
解释及解决方法:
Numerical Recipes 所做的违反了 Fortran 的标准。他们假设如果你给最大的整数加 1,你会得到最小的整数。
Fortran 整数不允许这样做,C 中的有符号整数也不允许这样做。C 中只允许无符号整数使用,而 Fortran 没有它们。
所以编译器可以安全地假设两个正整数相加永远不会产生负整数。有关这些优化的详细讨论,请参阅https://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475。
一种选择是重写可能导致 C 中溢出的操作并使用转换为无符号整数。我在我使用的随机数生成器中使用它(基于http://www.cmiss.org/openCMISS/wiki/RandomNumberGenerationWithOpenMP):
#include <stdint.h>
#include <memory.h>
int32_t sum_and_overflow (int32_t a, int32_t b)
{
uint32_t au, bu, su;
int32_t s;
memcpy(&au, &a, sizeof(a));
memcpy(&bu, &b, sizeof(b));
su = au + bu;
memcpy(&s, &su, sizeof(s));
return s;
}
带 Fortran 接口
interface
function sum_and_overflow(a, b) result(res) bind(C, name="sum_and_overflow")
use, intrinsic :: iso_c_binding
integer(c_int32_t) :: res
integer(c_int32_t), value :: a, b
end function
end interface
而不是
c = a + b
我打电话
c = sum_and_overflow(a, b)
所以在你的情况下
if (sum_and_overflow(hgt,1) /= hgng) ...
但不仅如此,您还必须在生成器中使用此假设的至少一个地方。在我使用的生成器中,只有一条这样的线。
还有许多其他技巧可能会导致暂时成功,但稍后会因其他一些编译器选项而失败。例如,GCC 中的未定义行为清理不喜欢 Fortran 和 C 中的有符号整数溢出,如果您这样做,将终止您的程序。
这就是为什么我试图展示一个更复杂的解决方案,但不是围绕标准工作,而是遵循它。