0

ran_initNumerical Recipes的子程序包含以下几行:

INTEGER(K4B) :: new,j,hgt
...                                                                                            
hgt=hg 
...
if (hgt+1 /= hgng)    call nrerror('ran_init: arith assump 3 fails')

其中K4B,hgnghg通过以下方式在模块中全局声明:

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。我从有关此错误的文档中得到的唯一信息是:

这里有点脏衣服!我们正在测试最正整数 hg 在加 1 时是否回绕到最负整数 hgng。我们不能只写 hg+1 ,因为一些编译器会在编译时评估它并返回溢出错误消息。如果您的编译器看穿了临时变量 hgt 的字谜,您将不得不找到另一种方法来欺骗它!

我该如何欺骗它?

4

1 回答 1

0

坠机的直接原因:

优化步骤期间的编译器可以很容易地证明hgt+1hgng不能相同,因此它将整个条件优化为.false.。两个正整数相加不能产生负整数。

您可以通过使用较小级别的优化或使用临时变量进行一些伪装来避免它,这些临时变量会比他们在旧的数字食谱中所做的更复杂。您可以尝试使用-fwrapvor-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 中的有符号整数溢出,如果您这样做,将终止您的程序。

这就是为什么我试图展示一个更复杂的解决方案,但不是围绕标准工作,而是遵循它。

于 2016-09-13T09:35:12.847 回答