In contrast to C, no Fortran standard does provide unsigned data types (although there are some vendor extensions). This can cause problem when interfacing C functions that require unsigned arguments. For example the components of RGB-values or parts of IPv4 adresses are often expressed as unsigned 8-bit integers.
In principle it is possible to pass signed integers with the same bit pattern as the unsigned value one wants to pass (For example, both an unsigned 8-bit integer with the value 128 and a signed integer with the value -128 have the same bit pattern (binary: 1000 0000) if the Two’s complement is used as is it the case for most systems. This may, however, lead to code that is difficult to read and maintain.
I have written a module that takes the numerical values of larger integer types (i.e. types where more bits are used to store the data) and storeds them into smaller types in a way that the bit pattern corresponds to a unsigned (C-type) variable…
For example the function call int16_to_unit8(+128) stores returns 128 (The bit pattern of a +128 in an unsigned variable is equal to the bit pattern of -128 in a signed variable, 1000 0000 in binary notation).
It is also possible to store the numerical value of an integer whose bit pattern is interpreted as unsigned in a “larger” integer variable uint8_to_int16(-128_int8) .eq. +128_int16.
(The functions assume that the Two’s complement is used and the endianness of the data to be converted is the same.)
My module (along with some test programs) can be found at
.
However, the code is not very polished and it is (apart from learning) probably not useful to re-“invent” the same thing for the 1000-th time.
So I’d like to know whether there is a well-known and posibly more efficient set of functions for this task that works with standard fortran and requires no vendor extension.
use iso_fortran_env, only: int8
integer(int8) :: a, b
a = int(128,int8) ! requires -fno-range-check
! or
a = 128_int8 ! requires -fno-range-check
! or
b = int(b'10000000',int8)
print *, a, b
end
With recent gfortran versions you might need to use the flag -fno-range-check:
Disable range checking of input values during integer READ operations. For example, GNU Fortran will give an error if an input value is outside of the relevant range of [ -HUGE() : HUGE() ]. In other words, with INTEGER (kind=4) :: i , attempting to read -2147483648 will give an error unless -fno-range-check is given.
There was a BITS data type proposed for Fortran 2008 that effectively solved the problems associated with sign-independent bit patterns. It was dropped as part of the compromise that F2008 was “too big”, but the edits for it were included in J3 file 07-007r1.pdf if you want to look at the details.
Thanks a lot for your feedback and the links to additional
information concerning the topic.
I am currently trying to learn how to wrap C functions in Fortran
(Currently for fun only as I am no longer develop scientific codes
after leaving academia.) and have by no mean experience in mixed-language
programing.
When looking at some C libraries I was wondering what happens, if a C library uses
unsiged types like unsigned int or size_t.
Even if unsigned types have problems as the sources you have provided
have pointed out, they are actually used quite frequently to describe natural numbers
even in important libraries like the C standard library or the UNIX socket library.
In many situations it will be better to write some additional wrapper code in C to provide an interface that better matches the programming practices (and idiosyncrasies) of Fortran.
As an idea you could define an interoperable C routine to return a scalar or array of integer bit patterns you need. However in the case of signed and unsigned conversion, quoting,
there is no fully C-compliant way to do this because casting between signed/unsigned for values out of range is implementation-defined. But this will still work in most cases:
unsigned int x = 65529;
int y = (short) x; // If short is a 16-bit integer.
or alternatively:
unsigned int x = 65529;
int y = (int16_t) x; // This is defined in <stdint.h>
To access such a “global” C variable in your Fortran program, you can use a declaration like:
integer(c_int), bind(c,name="y") :: y
Depending on how you want to use bit patterns you could also consider using the new Fortran stdlib bitset_type or the maturer C++ std::bitset.
Actually your suggestions lead to much faster code than invoking my library functions (> 2 orders of magnitudes for gfortran 9.3) for the conversion large signed integer ==> small unsigned integer.
So I guess the funsignedwrapper module should not be used when performance is a concern.
Still the the module may be used for the conversion
small unsigned integer ==> large signed integer.
I currently don’t really work on a concrete problem (yet).
Actually, I used Fortran during my PhD (numerical astrophysics)
and postdoc time.
While I have left academia almost two years ago and numerical
programming is no longer a part of my job, I am still interested in the
programming language Fortran on a hobby basis.
I liked the language, but on the other hand have used a rather
Fortran-90 style language back then.
Now my current job we use an object-oriented programming language
and make strong use of supporting tools like debuggers etc. that in retrospect
would have made my work easier back then.
So I’d like understand the usage of modern concepts/techniques in the context of
Fortran programing, but as mentioned before it is currently just for fun.
I asked my question questions and wrote my (not-so-efficient…) module because
I really think that interopability and programers that understand the corresponding concepts
and problems are very important for the future of Fortran to prevent its niche from becoming
too small.
By the way, I really appreciate your helpful answers and your friendliness towards newbies
in the forum and your efforts to enhance the usability of Fortran for the presence and future.