How can I assign the boz-literal-constant Z'FEDCBA09'
or any other bit-pattern with the most-significant bit equal to 1 to an integer?
The standard states:
INT(A[,KIND])
: IfA
is a boz-literal-constant, the value of the result is the value whose bit sequence according to the model in 16.3 is the same as that of A as modified by padding or truncation according to 16.3.3. The interpretation of a bit sequence whose most significant bit is 1 is processor dependent.source: Fortran 2018 Standard
So the following assignments might fail (assume integer
is default 32 bit):
program boz
implicit none
integer :: x1 = int(Z'FEDCBA09')
integer :: x2 = int(Z'FFFFFFFF')
integer :: x3
data x3/Z'FFFFFFFF'/
end program
Using gfortran, this will only work when adding -fno-range-check
but this introduces extra unwanted effects:
-fno-range-check
: Disable range checking on results of simplification of constant expressions during compilation. For example, GNU Fortran will give an error at compile time when simplifyinga = 1. / 0.
With this option, no error will be given and a will be assigned the value +Infinity. If an expression evaluates to a value outside of the relevant range of[-HUGE():HUGE()]
, then the expression will be replaced by-Inf
or+Inf
as appropriate. Similarly,DATA i/Z'FFFFFFFF'/
will result in an integer overflow on most systems, but with-fno-range-check
the value will "wrap around" andi
will be initialized to-1
instead.
I attempted the following, which works fine but still not 100%
integer(kind=INT32) :: x1 = transfer(real(Z'FEDCBA09',kind=REAL32),1_INT32)
integer(kind=INT32) :: x1 = transfer(real(Z'FFFFFFFF',kind=REAL32),1_INT32)
The latter case fails with gfortran as it complains that Z'FFFFFFFF'
represents NaN.
Using IOR(0,Z'FEDCBA09')
also fails as it converts the boz-literal using INT
Question: How can you robustly assign a bit pattern using a boz-literal-constant? That is to say, independent of the used compiler (GNU, SUN, PGI, NAG, ...).
Answer: The most robust answer is currently given by Jim Rodes in this comment:
x = ior(ishft(int(Z'FEDC'),bit_size(x)/2),int(Z'BA09'))
This will work on any compiler and does not require any other data-type to be successful.
When dealing with languages that do not support unsigned integers and you need to be able to test and/or set the high bit of the largest available integer, you can split the value into 2 variables and deal with the high order and low order bits separately.
One method would be to put the upper half into one variable and the lower half into another so that:
integer :: x1 = int(Z'FEDCBA09')
becomes:
integer :: x1Hi = int(Z'FEDC')
integer :: x1Lo = int(Z'BA09')
As the OP pointed out in an edit, a shift operation could then be used to assign the full value to a single variable like this. I changed it slightly so that it would work in case x
is more than 32 bits.
x = ior(ishft(int(Z'FEDC'), 16), int(Z'BA09'))
Another possible method would be to have a separate variable for just the high bit.