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



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

为了补充Vladimir F的回答,我将提到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()不能直接用于初始化:对它的引用不是常量表达式。对于这种使用,采用这种方法来获取位模式,并应用另一个答案的方法。

您还需要确保对每种数据类型的特性都有支持。

这是可能的。首先必须找出哪个位模式代表一个可能的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尾数的第一位为0。但正如https://faculty.cc.gatech.edu/~hyesoon/spr09/ieee754.pdf所指出的那样:"SNaNs",主要是出于政治原因而存在,很少使用。上面引用的转换器没有显示这种差异。

例如:

  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

返回
 qnan: T
 snan: T

在Intel Fortran中的默认设置。在GCC (gfortran)中,默认情况下禁用信令nan。并且可以通过-fsignaling-nans启用,但它似乎无论如何都没有帮助。

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


许多编译器都有一个选项来为所有真实的变量做这个。正如francescalus所示,在fortran中它是-finit-real=nan。手动操作可以更好地控制。

免责声明:切换到不同的平台时要小心。Endianness和其他问题可能会起作用,尽管我认为这实际上是可以的。我假设一个符合IEEE的CPU。


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

如果你的GFortran没有固有的IEEE,但有固有的iso_c_binding(就像在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的检查。

正如francescalus在本次讨论中指出的,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

最新更新