What are the rules surrounding conversion between different kinds of integers in Fortran?

Considering the following code snippet:

    integer(int8) :: a
    integer(int32) :: b
    integer(int64) :: c
    a = - huge(a) - 1
    b = - huge(b) - 1
    c = - huge(c) - 1
    print *, a, b, c
    a = int(b, kind=int8)
    c = int(b, kind=int64)
    print *, a, b, c

On my system it gives the following output:

 -128 -2147483648 -9223372036854775808
    0 -2147483648          -2147483648

This suggests that between conversion between integers of different kinds occurs as follows:

  1. Widening Integer Conversions [less bytes to more bytes] is done via sign-extension.
  2. Narrowing Integer Conversions [more bytes to less bytes] is done by simply lobbing off the higher-order bits.

This is similar to Java's integer conversion rules.

Is the behavior defined to be standard Fortran? Is there any way to override rule 1 and simply get a normal widening [like Java's Integer.toUnsignedLong()]?

Compiler Information:

GNU Fortran (Rev6, Built by MSYS2 project) 11.2.0
Copyright (C) 2021 Free Software Foundation, Inc.

Solution 1:

In 10.2.1.3 (8) in F2018 you will find that the intrinsic assignment the value of the expression of the right hand side is converted according a table. When the variable being assigned to is integer, the INT() intrinsic function is used with the kind of the variable.

The definition of the INT() intrinsic function says (16.9.100(5)):

Result Value.
Case (i): If A is of type integer, INT (A) = A.
...

where A is the argument.

So it just says that the value is copied. If the value does not fit, you are out of luck. The Fortran standard does not define what happens because (as pointed out by @steve):

F2018 16.9.1(2)

A program shall not invoke an intrinsic procedure under circumstances where a value to be assigned to a subroutine argument or returned as a function result is not representable by objects of the specified type and type parameters.

That makes your program non-conforming. In C they would say that the behaviour is undefined (not just implementation-defined).


If you enable -Wconversion and do the assignment without the explicit int() intrinsic, you will get a warning of a possible value change. Indeed, in a = b you can see such a change.

If you enable -fsanitization=undefined and you do the same, you may get a runtime error. I cannot reproduce it now, but I did hit such a thing before.

It must be truly runtime for the check to be triggered:

    use iso_fortran_env
    integer(int32) :: b
    read *,b
    b = 2*b
    print *, b
end


> gfortran conversion2.f90 -fsanitize=signed-integer-overflow
> ./a.out 
2000000000
conversion2.f90:4: runtime error: signed integer overflow: 2000000000 * 2 cannot be represented in type 'integer(kind=4)'
  -294967296