在 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
是否可以使用 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
常量,但很有用。
补充
而不是
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
的检查。
正如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