Having parameter (constant) variable with NaN value in Fortran
Asked Answered
J

4

14

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.

Jaynajayne answered 12/8, 2015 at 17:32 Comment(2)
For gfortran, I'd be tempted to just consider -finit-real=nan.Joachim
or -init=arrays -init=snan in ifortElongation
O
13

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.

Oscine answered 12/8, 2015 at 17:47 Comment(1)
I agree with your suggestion @Vladimir F as in this way during the run-time it is possible to change value of a variable to NaN or vice versa. But the drawback is the machine-dependent bit pattern of NaN.Jaynajayne
J
16

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.

Joachim answered 12/8, 2015 at 18:9 Comment(3)
True, I still did not go too much into this, as I must keep compatibility with gfortran 4.8. Unfortunately, ieee_value is indeed not allowed in constant expressions, so one still needs to use transfer(). Much more discussion is in groups.google.com/forum/#!msg/comp.lang.fortran/tYC3UgAyNrY/…Atencio
It seems if I want to use NaN for debugging and getting run-time error, I have to use signaling NaN rather than the quiet one ( Ref ).Jaynajayne
@Jaynajayne Yes. There is no guarantee that ieee_signaling_nan will result in such a thing (although it is recommended). I've no idea what gfortran returns - if you can test, please let me know. A quiet NaN is still valuable, in some sense: you know something's gone wrong. Not convenient for debugging.Joachim
O
13

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.

Oscine answered 12/8, 2015 at 17:47 Comment(1)
I agree with your suggestion @Vladimir F as in this way during the run-time it is possible to change value of a variable to NaN or vice versa. But the drawback is the machine-dependent bit pattern of NaN.Jaynajayne
P
1

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.

Pean answered 3/2, 2017 at 7:59 Comment(6)
Are you aware that 1_c_double is an integer? After realizing that you will not find the fail that much interesting any more. Your first line can be just real(kind = c_double), parameter :: ONE = 1 and it will do the same. See my answer for proper syntax for reals.Atencio
So in the end you can just shorten it to real(c_double), parameter :: NAN = TRANSFER(z'7FF0000000000001', 1._c_double) or real(c_double), parameter :: NAN = TRANSFER(9218868437227405313_c_int64_t, 1._c_double)Atencio
@VladimirF, Thank you.Pean
Aren't BOZ literals only allowed in data statements and some specific intrinsic for manipulating bits?Benfield
@RodrigoRodrigues See (gcc.gnu.org/onlinedocs/gfortran/BOZ-literal-constants.html). "Up to Fortran 95, BOZ literals were only allowed to initialize integer variables in DATA statements. Since Fortran 2003 BOZ literals are also allowed as argument of REAL, DBLE, INT and CMPLX; the result is the same as if the integer BOZ literal had been converted by TRANSFER to, respectively, real, double precision, integer or complex. As GNU Fortran extension the intrinsic procedures FLOAT, DFLOAT, COMPLEX and DCMPLX are treated alike."Pean
Constraint C7110 (R764) from 2015 standard: "A boz-literal-constant shall appear only as a data-stmt-constant in a DATA statement, or where explicitly allowed in subclause 16.9 as an actual argument of an intrinsic procedure." But TRANSFER intrinsic does not mention them, unlike the type conversion and bit manipulation intrinsics.Benfield
R
1

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
Renewal answered 5/5, 2022 at 8:3 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.