Fortran 90中的NaN问题

5

我意识到,如果你编写

    Real (Kind(0.d0))::x,y
    x = sqrt(-1.d0)
    y = sqrt(-1.d0)
    if (x == y) then
       write(*,*)'yep, they are equals', x
    endif

使用ifort编译没有问题。但是,什么都没有被写入,条件始终为false,你有注意到吗?为什么会这样呢?


-1的平方根是非法的。你必须使用复数。另外,如果你得到了NaN(不是数字),NaN永远不会等于NaN。大多数程序将在第一个NaN上崩溃。 - cup
你可以在这个问题的答案中了解更多。NaN不是数字,与它们的比较几乎总是返回false。(从sqrt(-1.d0)得到的NaN和从0./0.得到的NaN相同吗?它们应该相等吗?为什么?) - Jonathan Dursi
我的问题是因为我需要在函数返回值为NaN时执行一些操作,例如if(outcome == sqrt(-1.d0) then etc.。好的@Jonathan Dursi,我大致阅读了您给我的链接(因为答案很长)。结论是要注意Fortran中的这些问题。对于Fortran来说,1.d0/0.d0等于2.d0/0.d0。我将在Matlab中测试它并查看其行为。 - JoeCoolman
1
@JoeCoolman:这是一个IEEE问题,而不是Fortran问题,因此Matlab可能会产生与Fortran代码相同的结果。 - Kyle Kanos
@KyleKanos - 没错。Inf 等于 Inf,但NaN 不等于 NaN(对于quiet NaNs有例外),而这样做有非常好的理由。这些都是IEE754所固有的,并且几乎任何语言或软件包都是相同的。在Matlab中,您可以使用isnan来检查NaN(正是因为上述原因)。在现代Fortran中,您可以use ieee_arithmetic并使用内置的IEEE_IS_NAN(X)来检查NaN。 - Jonathan Dursi
1个回答

16

NaN代表非数字,由于可能有很多不同的原因导致计算结果为NaN,它们通常不会与自己相等。如果您想进行NaN测试,支持f2003标准(大多数编译器的最新版本)的Fortran编译器在ieee_arithmetic模块中有ieee_is_nan函数:

program testnan
    use ieee_arithmetic

    real (kind=kind(0.d0)) :: x,y,z

    x = sqrt(-1.d0)
    y = sqrt(-1.d0)
    z = 1.d0

    if ( ieee_is_nan(x) ) then
       write(*,*) 'X is NaN'
    endif
    if ( ieee_is_nan(y) ) then
       write(*,*) 'Y is NaN'
    endif
    if ( ieee_is_nan(x) .and. ieee_is_nan(y) ) then
       write(*,*) 'X and Y are NaN'
    endif

    if ( ieee_is_nan(z) ) then
       write(*,*) 'Z is NaN, too'
    else
       write(*,*) 'Z is a number'
    endif

end program testnan

编译并运行该程序的结果为:

ifort -o nan nan.f90

 X is NaN
 Y is NaN
 X and Y are NaN
 Z is a number

很遗憾,截至撰写时间,gfortran仍未实现ieee_arithmetic,因此您必须使用非标准的isnan


你们都做出了出色的贡献,我今天学到了新的东西。 - JoeCoolman
2
我通常使用 if(x /= x) 作为我的 NaN 检查,而不是调用 isnanieee_is_nan 方法。这只是另一种选择。 - Kyle Kanos

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