Numerical Recipes的ran_init
子程序包含以下行:
INTEGER(K4B) :: new,j,hgt
...
hgt=hg
...
if (hgt+1 /= hgng) call nrerror('ran_init: arith assump 3 fails')
K4B
,hgng
和hg
在模块中通过以下方式全局声明:
INTEGER, PARAMETER :: K4B=selected_int_kind(9)
INTEGER(K4B), PARAMETER :: hg=huge(1_K4B), hgm=-hg, hgng=hgm-1
问题在于,在一台特定的计算机中(但在其他计算机中),我收到错误ran_init: arith assump 3 fails
。我从有关此错误的文档中获得的唯一内容是:
这里有一点脏衣服!我们正在测试是否最积极 当1为1时,整数hg回绕到最负的整数hgng 添加到它。我们不能只写hg + 1,因为有些编译器会 在编译时评估它并返回溢出错误消息。 如果你的编译器看到临时的游戏 变量hgt,你必须找到另一种方法来欺骗它!
我怎么欺骗它?
答案 0 :(得分:0)
优化步骤中的编译器可以很容易地证明hgt+1
和hgng
不能相同,因此它将整个条件优化为.false.
。添加两个正整数不能创建负整数。
您可以通过使用较小级别的优化或通过一些伪装临时变量来避免它,这将比他们在旧的数字食谱中所做的更精细。您可以尝试使用-fwrapv
或-fno-strict-overflow
标记。但它非常不确定,只能在具有某些编译器标志的给定版本的编译器中工作。
最简单的"修复"可能只是删除违规检查。它很可能在许多情况下工作(并且在其他情况下非常失败)。
什么数字食谱对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中的有符号整数溢出,如果你这样做将终止你的程序。
这就是为什么我试图展示一个更复杂的解决方案,但不是围绕标准工作,而是遵循它。