Search code examples
fortrangfortran

How to assign bit-pattern Z'FEDCBA09' to a 32bit integer


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]): If A 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 simplifying a = 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" and i will be initialized to -1 instead.

source: GNU Compiler Collection, gfortran manual

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.


Solution

  • 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.