在 Fortran 中具有 NaN 值的参数(常量)变量

Having parameter (constant) variable with NaN value in Fortran

是否可以使用 NaN 设置参数变量?并将其放在特定模块中。我想用它来初始化一些其他变量。因此,如果它们没有更新,我将面临 运行 时间错误,而不是模拟 运行 一些随机数。
我正在使用 GFortran。

这是可能的。您首先必须找出哪个位模式代表可能的 NaN 值之一。您可以将位模式存储为整数:

 use, intrinsic :: iso_fortran_env
 real(real64) x
 integer(int64) i
 x = 0
 x = 0/x
 print *, x
 print *, transfer(x, i)
end

它给出:-2251799813685248

然后您可以使用

初始化您的变量
real(real64), parameter :: nan64 =  transfer(-2251799813685248_int64, 1._real64)

类似地,对于 32 位变量,您会得到整数 -4194304,这样您就可以

real(real32), parameter :: nan32 =  transfer(-4194304_int32, 1._real32)

如果您使用 IEEE-754 兼容的浮点表示法(几乎可以肯定,当您关心 NaN 时),您也可以使用该标准中的定义。有许多可能的位模式表示非数字。它们的指数中的所有位都等于 1,尾数中的某些位等于 1。可以使用转换器,例如 https://www.h-schmidt.net/FloatConverter/IEEE754.html

如果您需要区分信号 NaN 和安静 NaN,安静 NaN 的尾数中的第一位(最高有效位)等于 1,而信号 NaN 的第一位等于零。但正如 https://faculty.cc.gatech.edu/~hyesoon/spr09/ieee754.pdf 指出的那样:“SNaN,主要出于政治原因而存在,很少使用”。上面引用的转换器没有显示这种差异。

例如:

  use, intrinsic :: iso_fortran_env
  use ieee_arithmetic
  real(real32), parameter :: qnan =  transfer(int(B'01111111110000000000000000000000',int32), 1._real32)
  real(real32), parameter :: snan =  transfer(int(B'01111111101000000000000000000000',int32), 1._real32)
 
  if  (IEEE_SUPPORT_DATATYPE(qnan)) then
     print *, "qnan:", (ieee_class(qnan)==ieee_quiet_nan)
  end if
     
  if  (IEEE_SUPPORT_DATATYPE(snan)) then
     print *, "snan:", (ieee_class(snan)==ieee_signaling_nan)
  end if
end

returns

 qnan: T
 snan: T

在 Intel Fortran 的默认设置中。在 GCC (gfortran) 中,默认情况下禁用信号 NaN。并且可以通过 -fsignaling-nans 启用,但它似乎无济于事。

其他位,包括第一个符号位,通常被忽略。


许多编译器都有一个选项可以为您对所有实变量执行此操作。正如 francescalus 所示,在 gfortran 中它是 -finit-real=nan。手动执行此操作可为您提供更好的控制。

免责声明:切换到其他平台时要小心。 Endianness 和其他问题可能会发挥作用,尽管我认为它可能实际上没问题。我假设符合 IEEE CPU.


参见 francescalus 对使用标准函数的替代方案的回答。不幸的是,它不适用于 parameter 常量,但很有用。

补充 我会提到 gfortran 5.0(但不是更早的版本)支持 IEEE 内部模块。

而不是

real x
x=0
x=0/x

一个可以用

use, intrinsic :: iso_fortran_env
use, intrinsic :: ieee_arithmetic
integer(int32) i
real(real32) x

x = ieee_value(x, ieee_quiet_nan)
i = transfer(x,i)

这使您可以更灵活地选择要获取的 NaN 值。您也不必担心任何信号无效标志。 [但请注意,要求 ieee_signaling_nan 可能不会真的给你。]

请注意 ieee_value() 不能直接用于初始化:对它的引用不是常量表达式。对于这种用途,采用这种方法获取位模式并应用其他答案的方法。

您还需要确保支持每种数据类型的功能。

如果您遇到没有固有 IEEE 但具有固有 iso_c_binding 的 GFortran(就像在 Windows 上构建 R 所需的那样),则以下工作是相当于 C 和 R NaN(在 R 上传递 is.nan):

real(kind = c_double), parameter :: ONE = 1_c_double    
real(kind = c_double), parameter :: NAN = TRANSFER(z'7FF0000000000001', ONE)

有趣的是,real(kind = c_double), parameter :: NAN = TRANSFER(z'7FF0000000000001', 1_c_double) 未能通过我对 is.nan 的检查。

正如 and in this 所指出的,ieee_value的结果不能赋值给parameter。另一种方法是使用 protected 模块变量而不是 parameter。但是,然后必须在程序开始时调用模型初始化函数。

module nan_module

  implicit none
  real, protected :: nan_real
  contains

  subroutine init_nan_module
    use, intrinsic :: ieee_arithmetic
    implicit none
    nan_real = ieee_value(nan_real, ieee_quiet_nan)
  end subroutine

end module nan_module

program test

  use nan_module, only: init_nan_module, nan_real

  implicit none
    real :: x
    call init_nan_module()
    x = nan_real
    write(*,*) x, 'isnan', isnan(x)

end program test