5

If I have a c_int8_t variable in Fortran and want to interpret the underlying bits as an unsigned integer (for indexing rather than for any arithmetic) what is the most efficient way to do the conversion? I want to do something like

X( some_function( my_c_int8_t_var ) ) = 1

where X is an array and some_function should return a c_int16_t containing the unsigned value of my_c_int8_t_var. I have seen options including transfer, iadd (or I guess just a simple conditional with a cast and an addition) but I would like to know what would be the most efficient way (this indexing is taking place in an inner loop). Of course it doesn't have to be a function - if it can be done with an inline expression that would be great.

EDIT: It would be nice if the approach would also work for other integer size... ie for getting the contents of unsigned c_int16_t in a c_int32_t and getting an unsigned c_int32_t into a c_int64_t.

hippietrail
  • 15,848
  • 18
  • 99
  • 158
robince
  • 10,826
  • 3
  • 35
  • 48
  • Is it not possible to declare `X` using the full range of the signed integer, e.g. `integer, dimension(-huge(my_c_int8_t_var) : huge(my_c_int8_t_var)) :: X`? That would be much less hassle. – sigma Nov 16 '12 at 12:09
  • I see what you mean but in my case I am not using the full range - the user passes the maximum value and I allocate the X array accordingly (X(Xm) at the moment or X(0:(Xm-1)). I would like to be able to support the full range of unsigned ints though (so can get up to 256 without needing to go to 2byte data) - but never allocate more than necessary for the maximum value provided. – robince Nov 16 '12 at 12:16
  • Oh, I see; I misunderstood the intent of the question. – sigma Nov 16 '12 at 12:32

2 Answers2

4

You could use transfer() and ichar() functions. Something like

X(ichar(transfer(my_c_int8_t_var,"a")))) = 1

For example

 use iso_c_binding
 write (*,*) ichar(transfer(-1_c_int8_t,"a"))

 end

returns 255.

When you cannot find a coresponding character kind (e.g., 16bit) I would write a function that adds huge(1._my_integer_kind) to the value.

integer function indx(i)
  integer(c_int16_t),intent(in) :: i

  if (i<0) then
    indx = 2*(huge(i)+1) + i
  else
    indx = i
  end if
end function indx

or

  integer function indx2(i)
    integer(c_int8_t),intent(in) :: i

    indx2 = TRANSFER([i,0_c_int8_t],1_c_int16_t)
  end function indx2

This last case works only for little-endian platforms.

You can make a generic interface to this function.

 write (*,*) indx(-2_c_int16_t)

gives 65534

  • Thanks thats just the kind of trick I was looking for. But it seems specific to 8 bit case. I know that was my example but I was hoping to find something that would also work for getting an unsigned c_int16_t value into a c_int32_t etc. – robince Nov 16 '12 at 12:13
  • Thanks - for the huge method - doesn't there need to be a conditional? For 'positive' values in the unsigned data I don't want to add anything, the addition should only happen if i<0? (Or am I confused?) so then I was worried about the conditional slowing things down since this is an inner loop? – robince Nov 16 '12 at 12:37
  • I guess the alternative approach is to transfer to the larger type and then do a bitwise AND with an appropriate mask to zero the large bits but I was not sure how to sort that out - and also not sure if that would be slower than the addition with the conditional. – robince Nov 16 '12 at 12:39
  • But with TRANSFER apparently "If the bitwise representation of the result is longer than that of SOURCE, then the leading bits of the result correspond to those of SOURCE and any trailing bits are filled arbitrarily." I am not sure which leading and trailing are (in terms of large or small parts of the bitwise value)... – robince Nov 16 '12 at 12:43
  • With those tricks you have to be aware of differences between the little-endian and big-endian machines and resulting nonportability. – Vladimir F Героям слава Nov 16 '12 at 12:46
  • OK - but I am only ever using x86(-64) which is little-endian? So would something like IAND(TRANSFER(i,1_c_int32_t), int32(huge(i))+1) work? (I am not sure if the mask would be correct - or if it could be any faster than the one with the conditional... – robince Nov 16 '12 at 12:50
  • Last note, the equivalence approach might be faster. It depends on the optimizations the compiler is able to do. – Vladimir F Героям слава Nov 16 '12 at 13:28
0

What about

integer(c_int16_t) :: i, i2(2)
integer(c_int32_t) :: i32

equivalence (i32, i2)

i2(2) = 0

then inside the loop

i2(1) = i
X(i32) = 1

Would this work? Should I put the 16 bit data in i2(1) or i2(2) for little-endian platform?

robince
  • 10,826
  • 3
  • 35
  • 48