Is it possible to set a parameter variable with NaN? and have that in a particular module. I want to use it for initialization of some other variables. Therefore, I'll be faced with a run-time error, if they are not updated, rather than simulations running with some random numbers.
I am using GFortran.
Not a Number (NaN) is represented by the largest value that the exponent can assume (all ones), and a nonzero fraction. Normalized REAL and DOUBLE PRECISION numbers have an implicit leading bit that provides one more bit of precision than is stored in memory.
To add to Vladimir F's answer I'll mention that gfortran 5.0 (but not earlier) supports the IEEE intrinsic modules.
Instead of
real x
x=0
x=0/x
one can use
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)
This gives you a little flexibility over which of the NaN values you get. You also needn't worry about any signalling invalid flag. [But note that asking for ieee_signaling_nan
may not really give you that.]
Note that ieee_value()
can't be used directly in initialization: a reference to it isn't a constant expression. For such use, take this approach to get the bit pattern and apply the method of the other answer.
You'll also need to ensure that there is the support for the features for each datatype.
It is possible. You first have to find out which bit pattern represents one of the possible NaN values. You can store the bit pattern in an integer:
use, intrinsic :: iso_fortran_env
real(real64) x
integer(int64) i
x = 0
x = 0/x
print *, x
print *, transfer(x, i)
end
It gives: -2251799813685248
Then you can initialize your variables using
real(real64), parameter :: nan64 = transfer(-2251799813685248_int64, 1._real64)
Similarly for 32 bit variables you get the integer -4194304, so that you can do
real(real32), parameter :: nan32 = transfer(-4194304_int32, 1._real32)
If you are using IEEE-754 compatible floating point representation (almost certain, when you care about NaNs), you can also use the definitions from that standard. There are many possible bit patterns that mean a not-a-number. They have all bits in the exponent equal to 1 and some bit in the mantissa equal to 1. One can use convertors such as https://www.h-schmidt.net/FloatConverter/IEEE754.html
If you need to distinguish signaling and quiet NaNs, the quiet NaNs have the first bit (most significant) in the mantissa equal to one and the signaling NaNs have the first bit equal to zero. But as https://faculty.cc.gatech.edu/~hyesoon/spr09/ieee754.pdf notes: "SNaNs, which exist mainly for political reasons and are rarely used". The convertor referenced above does not show this difference.
For example:
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
in Intel Fortran in default settings. In GCC (gfortran) signaling NaNs are disabled by default. and can be enabled by -fsignaling-nans
, but it does not seem to help anyway.
Other bits, including the first sign bit, are usually ignored.
Many compilers have an option to do that for you for all real variables. As francescalus shows, in gfortran it is -finit-real=nan
. Doing that manually gives you a finer control.
Disclaimer: Be careful when switching to a different platform. Endianness and other issues could play a role, even though I think it could be actually OK. I assumed an IEEE conforming CPU.
See, francescalus's answer for an alternative which uses a standard function. Unfortunately, it is not applicable for parameter
constants, but is useful.
If you are stuck with a GFortran that does not have the intrinsic IEEE but does have the intrinsic iso_c_binding (like the one needed to build R on Windows), the following works and is equivalent to the C and R NaN (passes is.nan
on R):
real(kind = c_double), parameter :: ONE = 1_c_double
real(kind = c_double), parameter :: NAN = TRANSFER(z'7FF0000000000001', ONE)
Interestingly, real(kind = c_double), parameter :: NAN = TRANSFER(z'7FF0000000000001', 1_c_double)
fails my check for is.nan
.
As pointed out by francescalus and in this discussion, the result of ieee_value
cannot be assigned to a parameter
. Another approach is using a protected
module variable instead of a parameter
. However, then one has to call a model initialization function at the start of the program.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With