Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29252 Discussions

Need help with UNSIGNED integer arithmetic

blueyedtam
Beginner
1,255 Views

First of all, does Intel Fortran have a package for doing unsigned arithmetic?

We can't unfortunately type integers as unsigned like in C++. I would like to avoid the problem of interfacing mixed languages if possible.

The reason I ask, is that I want to convert a series of bytes (integer*1) quantites into 32 bit integers, 4 at a time. They are to be treated as unsigned bytes, and packed little endian first. For example the four bytes 81 82 83 and 84 would be converted to: Z'84838281'

If I type them as integer*1, Fortran treats them as signed quantites, which would be wrong. I want a direct packing into the 32-bit words without any conversion. Of course, if they have the range z'00' to z'79' there is no problem, since those would all be positive sign anyway. But the bytes I want to convert have the full range z'00' to z'FF'

Any ideas?

Thanks; Tammy

0 Kudos
9 Replies
TimP
Honored Contributor III
1,255 Views
If I understand what you want, packing integer*1 into integer*4 could be done successfully in various ways, among them equivalencing an array of bytes to an integer, and assigning (moving) each byte individually. More efficiently, something like shifting the high order bytes by ishft() and combining them with ieor(). These intrinsics don't do any sign extension, so there is no need for an unsigned byte data type. You haven't described anything which involves actually doing unsigned arithmetic. You could even combine them with plain arithmetic, since you need only operations where the bit patterns don't care about signs:
integer*1 byte0, byte1, byte2, byte3
byte0 + 256*(byte1 + 256*(byte2 + 256*byte3)))
or any equivalent. You could check that all the operations are promoted to default integer, or show explicit promotion to integer*4 kind.
The resulting 32-bit integer would be treated as signed, with the sign being the sign of the high order byte.

0 Kudos
blueyedtam
Beginner
1,255 Views

integer*1 byte0, byte1, byte2, byte3
byte0 + 256*(byte1 + 256*(byte2 + 256*byte3)))


Unfortunately, this only works ifALL of the above are between 0 and Z'7F' If any bytes are outside this range the answer will be incorrect. That was the first thing I tried.

For example, if the bytes are FF, 00 00 and 00, the compiler will promote -1 to Z'FFFFFFFF' but what we actually want isFF000000.

And using theEQUIVALENCE statement has the same problems, because the compiler does arithmetic conversion. I can't turn it off.

However, using the ISHFT and IOR statements looks promising. Does Fortran have a Swap BYTES library call?

0 Kudos
TimP
Honored Contributor III
1,255 Views
OK,
iand(byte0,z'ff') + 256*(iand(byte1,z'ff') + 256*(iand(byte2,z'ff') + 256*iand(byte3,z'ff'))))
iand() used this way works the same as the non-standard zext() intrinsic inherited from DEC Fortran.

Elemental Intrinsic Function (Generic): Extends an argument with zeros. This function is used primarily for bit-oriented operations. It cannot be passed as an actual argument.

Syntax

result = ZEXT (x [, kind])



0 Kudos
jimdempseyatthecove
Honored Contributor III
1,255 Views

Tammy,

Why not simply tell Fortran the data are characters?

If you are not performing math operations but simply moving the bytes about you shouldn't have a problem. Also, when you write the data, either write in binary or text with hex formatting.

I find it easiest to us UNIONs for polymorphic data

type C4I4
union
map
character :: C4(4)
end map
map
integer(4) :: I4
end map
end union
end type

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
1,255 Views
As suggested by Tim, ZEXT is the right way to convert an unsigned I1 to an I4, but it does not seem to me that's what you want here - you just want to move the bytes aroumd. In that case, it does not matter how Fortran treats the value arithmetically.

It is not entirely clear to me what you're looking for. If it's to swap the byte order, you can use a series of IBITS calls or shifts and masks to construct the value. There is no "swap bytes" intrinsic. However, on I/O, there is the ability to read "big-endian" data and convert it on input to little-endian.

Tammy, can you elaborate on your application and what you want to do?
0 Kudos
blueyedtam
Beginner
1,255 Views

Hi Dr. Fortran;

We could not persuade the military guys to use IEEE storage formats, unfortunately.

I'm getting the series of bytes (4 at a time) in "little endian" order from an external file. They are data telemetry from a military gadget (Secret proprietary info). So let's say I get:

80 81 82 83 F3 F4 F5 F6 B7 B8 B9 BA FF FE FD FC.

What I want to end up with is:

83828180, F6F5F4F3, BAB9B8B7, and FCFDFEFF.

The inputs and outputs are unsigned integers, and the telemetry device is giving us a "raw" file, i.e. non-Fortran. So I have to use a DIRECT ACCESS to read it. The output probably should be integer*8 because otherwise it will treat the final result as a signed integer. Likewise, there is a pitfall with ZEXT() because the assignment operator tries to do a sign conversion. I think the ISHFT function also does a zero extension. I will experiment with those.

To bad the ranges aren't small enough to allow integer*4 conversions. I have the same problem with 2-byte integers, but probably the best approach can also be used there.

Thanks; Tammy

0 Kudos
blueyedtam
Beginner
1,255 Views

BTW, I forgot to mention:

I can't type the inputs as character*1 because the compiler has strict limitations regarding type conversions. They generate fatal error messages.

At least I haven't yet found a way to bypass this particular limitation imposed by the compiler. But possibly I have overlooked something "cute" or "clever."

I'm willing to try any new ideas....

Hmmm - - - machine code anyone? At least it would be efficient........

Thanks; Tammy

0 Kudos
Steven_L_Intel1
Employee
1,255 Views
Tammy, thi is easier than you think. Try something like this:

integer(4) mydata(4)
open (unit=1,file='mydata.dat',form='binary',convert='big_endian'),status='old')

do
read (1,end=99) mydata
write (*,'(4Z9.8)') mydata ! Shows the data is byteswaped
end do
99 continue
close (1)
end

The "convert='big_endian'" tells the I/O system to byteswap the data within each element of the array. "form='binary'" says to read the data as a bytestream. This could also be done with the F2003 feature of stream access.
0 Kudos
DavidWhite
Valued Contributor II
1,255 Views

You may be interested in this link, which is Michel Olagnon's implementation of unsigned arithmetic. He has made it available for use.

http://www.nag.co.uk/nagware/Examples/32bit.f90

0 Kudos
Reply