gfortran:为什么“-posinf”会导致算术溢出?

时间:2013-08-19 14:38:05

标签: 32bit-64bit ieee-754 gfortran

在64位系统上获得+ -inf我使用了下一个代码

double precision, parameter :: pinf           = transfer(z'7FF0000000000000',1d0) ! 64 bit
double precision, parameter :: ninf           = transfer(z'FFF0000000000000',1d0) ! 64 bit

它运作良好。

在32位上 我的ninf只有编译错误(!):

double precision, parameter :: ninf           = transfer(z'FFF0000000000000',1d0
                                                                           1     
Error: Integer too big for integer kind 8 at (1)

赋值ninf = -pinf没有帮助并导致编译算术溢出错误:

double precision, parameter :: ninf           = -pinf
                                                         1
Error: Arithmetic overflow at (1)

我知道ieee_arithmetic模块,但gcc不处理它。

是否有任何多架构方式将常量设置为正/负无穷大?

更新 Gfortran选项-fno-range-check可以抑制错误并成功编译该代码。

这不重要,但我仍然很有趣。 为什么gfortran允许+无穷大的恒定定义,但大声喊出与-Infinity完全相同的东西?

2 个答案:

答案 0 :(得分:1)

我没有使用相同的编译器(我正在使用g95为32位整数设置编译器选项-i4,以及一种解决方法(如果你坚持使用我发现transfer用于指定整数参数作为参数,如下所示:

注意:使用我的编译器,我能够直接将数字分配给参数。我不确定你的相同是否相同,但是我很确定你真的应该使用传递函数,而不是真正处理常数 - 比如你是在做什么花哨的东西浮点数和需要像对其表示的真实细节控制。

请注意变量pdirectndirect

program main
integer(8), parameter :: pinfx= z'7FF0000000000000'
integer(8), parameter :: ninfx= z'FFF0000000000000'
double precision, parameter :: pinf = transfer(pinfx, 1d0)
double precision, parameter :: ninf = transfer(ninfx, 1d0)

double precision, parameter :: pdirect = z'7FF0000000000000'
double precision, parameter :: ndirect = z'7FF0000000000000'


write (*,*) 'PINFX  ', pinfx
write (*,*) 'NINFX  ', ninfx
write (*,*) 'PINF   ', pinf
write (*,*) 'NINF   ', ninf
write (*,*) 'PDIRECT', pdirect
write (*,*) 'NDIRECT', ndirect

end program

这会产生输出:

 PINFX   9218868437227405312
 NINFX   -4503599627370496
 PINF    +Inf
 NINF    -Inf
 PDIRECT +Inf
 NDIRECT +Inf

我希望这有帮助!

答案 1 :(得分:1)

在这种情况下,gfortran在内部将十六进制(“Z”)文字表示为可用的最大无符号整数大小。由于transfer是Fortran内在函数,并且Fortran没有无符号整数,因此gfortran所做的第一件事就是将文字分配给有符号类型,这会导致负无穷大的位模式溢出。在使用BOZ文字的许多其他情况下会发生这种情况,我认为这是gfortran中的一个错误。

我认为这只出现在32位系统上,因为在64位系统上,gfortran可能有128位整数类型; 128位有符号整数不会“溢出”该位模式。

但是,您的代码也不符合Fortran标准,该标准表示十六进制文字只能出现在data语句或函数intreal中,或dble。但是,在dble中放置十六进制文字与transfer完全相同。如果gfortran没有错误,那么你的程序就可以正常工作,但技术上 是不正确的。

无论如何,以下代码在gfortran中适用于我,我相信它将以符合标准的方式解决您的问题并避免-fno-range-check

integer, parameter :: i8 = selected_int_kind(13)
integer, parameter :: r8 = selected_real_kind(12)
integer(i8), parameter :: foo = int(Z'7FF0000000000000',i8)
integer(i8), parameter :: bar = ibset(foo,bit_size(foo)-1)
real(r8), parameter :: posinf = transfer(foo,1._r8)
real(r8), parameter :: neginf = transfer(bar,1._r8)
print *, foo, bar
print *, posinf, neginf
end

输出:

  9218868437227405312    -4503599627370496
                  Infinity                 -Infinity

关键是首先创建正无穷大的模式(因为它起作用),然后通过简单地设置符号位(最后一个)来创建负无穷大的模式。 ibset内在函数仅适用于整数,因此您必须对这些整数使用transfer来设置真正的正/负无穷大。

(我使用i8 / r8只是习惯,因为我使用的是编译器,其中kind参数不等于字节数。在这种情况下,它们都等于8。)