Fortran 90/95中有一种标准方法来检查无穷大和NaN吗?

29

我一直在尝试寻找一种符合标准的方法来检查Fortran 90/95中的无穷大和NaN值,但比我想象的难得多。

  • 我尝试手动创建使用IEEE 754中描述的二进制表示法的Inf和NaN变量,但未找到此类功能。
  • 我知道Fortran 2003中有内置的ieee_arithmetic模块,其中包含ieee_is_nan()ieee_is_finite()内部函数。但是,并非所有编译器都支持它(特别是gfortran 版本4.9及以下版本)。

pinf = 1./0 nan = 0./0 之类的定义无限大和NaN的方法让我觉得不太正规,在我的意见中可能会引发一些构建问题 - 例如,如果某些编译器在编译时检查这一点,则需要提供特殊标志。

有没有一种我可以在标准Fortran 90/95中实现的方法?

function isinf(x)
! Returns .true. if x is infinity, .false. otherwise
...
end function isinf

什么是isnan()函数?


4
GNU Fortran 4.10 fixes this. (翻译:GNU Fortran 4.10已经修复了这个问题。) - Susanne Oberhauser
1
GCC 5及更高版本支持IEEE_ARITHMETIC,但旧版本的支持仍然是一个问题,并且在很长一段时间内将继续存在。 - Vladimir F Героям слава
8个回答

36

不使用 ieee_arithmatic 的简单方法是:

无穷大:定义一个变量 infinity = HUGE(dbl_prec_var)(或者如果您有,一个四倍精度的变量)。然后您可以通过 if(my_var > infinity) 来简单地检查您的变量是否是无穷大。

NAN:这更加简单。根据定义,NAN 不等于任何东西,甚至不等于它本身。只需将变量与自身进行比较:if(my_var /= my_var)


1
@amaurea:您可以通过使用“INTERFACE”并将您可能使用的几个类型链接到同一个“MODULE PROCEDURE”中来处理数据类型问题。 - Kyle Kanos
@IanH:你可以合理地设置 infinity = 1.e100_dp,仍然可以获得良好的无穷大检查器。即使在使用小比例(如1e-24 m)描述大比例(如1e+24 m)时,你仍然远远没有达到1e100。 - Kyle Kanos
3
如果请求快速数学运算或类似操作,优化器确实会优化结帐流程。然而,它甚至会优化掉gfortran的isnan内置函数。当程序员请求不安全的快速数学运算但仍想检测NaN时,程序员有责任注意这一点。 - Vladimir F Героям слава
2
HUGE是最大的非无穷数;条件myvar > HUGE(myvar)只有在myvar为无穷大时才应该成立,但将HUGE(myvar)称为无穷大是具有误导性的。 - ShadSterling
2
@jvriesem 使用 iee_arithmetic 是正确的做法(所以如果你的编译器不支持它,请换一个更好的编译器)。这是符合标准的一种方法,而且相当实用:当变量大于某个对代码来说没有意义的值时,它们就是“无限”的(当小于某个特定值时则为无穷小);虽然一些编译器可以优化 NaN 检查。 - Kyle Kanos
显示剩余4条评论

4

我声望不够,无法评论,因此我将“回答”Rick Thompson关于测试无限大建议的问题。

if (A-1 .eq. A) 

如果A是一个非常大的浮点数,而且“1”低于A的精度,则这也是正确的。
一个简单的测试:
subroutine test_inf_1(A)
    real, intent(in) :: A
    print*, "Test (A-1 == A)"
    if (A-1 .eq. A) then
        print*, "    INFINITY!!!"
    else
        print*, "    NOT infinite"
    endif
end subroutine

subroutine test_inf_2(A)
    real, intent(in) :: A
    print*, "Test (A > HUGE(A))"
    if (A > HUGE(A)) then
        print*, "    INFINITY!!!"
    else
        print*, "    NOT infinite"
    endif
end subroutine


program test
    real :: A,B

    A=10
    print*, "A = ",A
    call test_inf_1(A)
    call test_inf_2(A)
    print*, ""

    A=1e20
    print*, "A = ",A
    call test_inf_1(A)
    call test_inf_2(A)
    print*, ""

    B=0.0 ! B is necessary to trick gfortran into compiling this
    A=1/B
    print*, "A = ",A
    call test_inf_1(A)
    call test_inf_2(A)
    print*, ""

end program test

输出:

A =    10.0000000    
Test (A-1 == A)
    NOT infinite
Test (A > HUGE(A))
    NOT infinite

A =    1.00000002E+20
Test (A-1 == A)
    INFINITY!!!
Test (A > HUGE(A))
    NOT infinite

A =          Infinity
Test (A-1 == A)
    INFINITY!!!
Test (A > HUGE(A))
    INFINITY!!!

2

我使用过:

  PROGRAM MYTEST
  USE, INTRINSIC :: IEEE_ARITHMETIC, ONLY: IEEE_IS_FINITE      
  DOUBLE PRECISION :: number, test
  number = 'the expression to test'
  test = number/number
  IF (IEEE_IS_FINITE(test)) THEN
     WRITE(*,*) 'We are OK'
  ELSE
     WRITE(*,*) 'Got a problem'
  END IF         
     WRITE(*,*) number, test
  END PROGRAM MYTEST

这将打印“Got a problem”,当且仅当number = 0.0D0,1.0D0/0.0D0,0.0D0/0.0D0,SQRT(-2.0D0)时,以及对于溢出和下溢如number = EXP(1.0D800)或number = EXP(-1.0D800)。请注意,通常像number = EXP(1.0D-800)这样的情况只会在编译时设置number = 1.0并产生警告,但程序将打印“We are OK”,这我认为是可以接受的。OL.

1
这个问题要求判断是否为非数字,但你的示例测试有限性。它也会将+Inf和-Inf误判为false positive。此外,请注意该问题实际上主要关注当IEEE_ARITHMETIC不可用时应该怎么做,并询问如何使用Fortran 90/95来解决这个问题。 - Vladimir F Героям слава
除了Vladimir F的担忧外,即使使用ieee_arithmetic也不能保证ieee_support_datatype(test)为真。如果不是这样,就不允许考虑ieee_is_finite(test) - francescalus
我可能有点过于苛刻了,这个问题也要求一个等价的 IEEE_IS_FINITE()。但关键是 OP 知道它的存在,但还是在寻找替代方案。 - Vladimir F Героям слава

2

不需要。

IEEE_ARITHMETIC中生成/检查NaN的关键部分对于为特定架构编写gfortran很容易。


关于你的第二个陈述,它们似乎不一致 http://gcc.gnu.org/ml/gcc-bugs/2012-10/msg00580.html 或者我漏掉了什么? - astrojuanlu
顺便提一下,我添加了一个链接到相关的gfortran错误报告(该错误报告始于2006年,状态为NEW)。 - astrojuanlu
3
为了解决这个错误所涉及的问题,要为gfortran支持的所有体系结构编写IEEE_ARITHMETIC的全部内容非常困难!但编写特定体系结构(例如x64)上生成/检查NaN的选择位相对容易。可以参考Tim Prince提供的一个例子,该例子依赖于任何两个NaN之间的不等式。另一种替代方法是使用Fortran位内置函数来生成/测试指示NaN的特定模式。(有关更多讨论,请参见“原始”gfortran错误29383。) - IanH

1

用于测试 NaN ,没有任何一件事情有效,例如,如果测试实数 s2p 是否为 NaN,则

if(isnan(s2p)) 

在gfortran中也没有工作,

if(s2p.ne.s2p). 

唯一有效的事情是:
if(.not.s2p<1.and..not.s2p>1)  

尽管为确保真实性,您可能需要添加该内容。
if(.not.s2p<1.and..not.s2p>1.and..not.s2p==1)    

0

不行。

Fortran 90/95 没有符合标准的方法来检查无穷大或NaN,也不能有符合标准的方法。在 Fortran 90/95 中,没有定义这些准数字中的任何一个的符合标准的方法。


好的,在IEEE浮点标准的范围内,也就是这个意思。 - astrojuanlu

0

对于Fortran来说,1/无穷大=0,因此将您的变量除以零,即:

program test
implicit none
real :: res
integer :: i

do i=1,1000000
    res=-log((1.+(10**(-real(i))))-1.)
    print*,i,res
    if ((1./res)==0.) then
        exit
    end if
end do

end program

这里是你的无限检查。不需要任何复杂操作。


-2

对于 Inf,似乎满足如果 (A-1 .eq. A) 为真,则 A 为 Inf


5
也许你可能想再详细解释一下。 - CinCout

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接