2

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.
Ian Bush
  • 6,996
  • 1
  • 21
  • 27
idk1415
  • 55
  • 3
  • 1
    Can you say exactly what output you would like to see? – francescalus Jan 19 '22 at 12:50
  • 1
    What do you mean by normal widening? Prepend zero bits? Those are signed integers, you cannot expect them to just change sign. There are various bit manipulation intrinsic in Fortran, though. – Vladimir F Героям слава Jan 19 '22 at 13:07
  • And welcome, I suggest taking the [tour] because this site is used differently than most other sites. – Vladimir F Героям слава Jan 19 '22 at 13:34
  • 1
    To expand slightly on Vladimir's comment note standard Fortran does not have unsigned integers, hence there is no equivalent to Integer.toUnsignedLong() – Ian Bush Jan 19 '22 at 14:00
  • @francescalus I primarily wanted to ask if the behaviour I'm seeing gfortran is standardized somewhere or is it something that's specific to gfortran. I couldn't find a source regarding this. – idk1415 Jan 19 '22 at 15:51
  • The behaviour of `int(b,kind(a))` if `a` cannot represent the value of `b` is not standardized. A program with this would be non-conforming. – francescalus Jan 19 '22 at 16:30
  • Note that in C (and likely C++) signed integer overflow is undefined behaviour as well. That is stuff like byte = 127; byte = byte +1; Your example is different, but still something that does not fit appears on the right hand side. – Vladimir F Героям слава Jan 19 '22 at 16:40
  • @francescalus, the behaviour of `int(b,kind(a))` if `a` cannot represent the value of `b` is standardized such that the code is nonconforming. See 16.9.1. _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._ – steve Jan 19 '22 at 18:28
  • @steve, I think you're agreeing with me? Inasmuch: the standard says the code is non-conforming, which means that a Fortran compiler can do what it pleases when asked to process the code. Nothing in the standard says what the result of such a "program" must be. – francescalus Jan 19 '22 at 18:42
  • I suppose it's a matter of semantics of _is not standardize_ and _is standardize to be nonconforming_. In the end, yes, we agree that the code is not valid Fortran. – steve Jan 19 '22 at 20:21
  • @IanBush Java also does not have unsigned integers either. The Integer.toUnsignedLong() function I mentioned converts a signed integer (32 bits) to a signed long (64 bits) but without sign extension and simply by prepending 0 bits. [reference](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#toUnsignedLong-int-). I was wondering if there was a Fortran analogue to this. – idk1415 Jan 20 '22 at 06:00

1 Answers1

3

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