Search code examples
fortrangfortran

How to make two large enough integer(kind=4) add together to be stored as integer(kind=8)?


I'm trying to add two integers(kind=4) that can be large enough to produce an integer(kind=8). I'm not sure if this is possible, but I made a few tests trying to make it work:

!gfortran, gcc version 5.4.0 20160609

program hello
    use iso_fortran_env

    integer, parameter :: i64 = int64
    integer(kind=4) :: a1, a2
    integer(kind=8) :: b1, b2
    integer(kind=8) :: c
    integer(kind=8) :: ugly

    a1 = 2023123123 !kind(4)
    a2 = a1         !kind(4)
    b1 = 2023123123 !kind(8)
    b2 = b1         !kind(8)

    ! sum integers of kind=8 that surely will lead to a kind=8
    c = b1+b2
    print*,c

    ! sum integers of kind=4 and kind=8 that will lead to a kind=8
    c = a1+b1
    print*,c

    ! sum integers of kind=4 which may lead to a kind=8
    c = a1+a2
    print*,c

    ! try to tell gfortran to make a kind(4) behave as a kind(8)
    ! for that operation   
    c = a1+a2_i64
    print*,c

    ! ugly workaround fail - 64 bit 0 on last position
    ugly = 0
    c = a2+a1+ugly
    print*,c

    ! ugly workaround ok - 64 bit 0 on first position
    ugly = 0
    c = ugly+a2+a1
    print*,c

    ! ugly workaround ok - divide in two operations
    c = a1+ugly
    c = c+a2
    print*,c
end program hello

The output of the script is

4046246246  ! kind(8) + kind(8) = kind(8) -> ok
4046246246  ! kind(4) + kind(8) = kind(8) -> ok, but not sure if it always work
-248721050  ! kind(4) + kind(4) = kind(8) -> NOT OK
2023123072  ! kind(4) + kind(4)_i64 = kind(8) -> NOT OK
-248721050  ! ugly workaround summing 0(kind=8) -> FAIL
4046246246  ! ugly workaround summing 0(kind=8) -> OK
4046246246  ! another ugly work around -> OK

Does anyone know how if its possible to add two integer(kind=4) resulting in a integer(kind=8) WITHOUT that very ugly workaround?


Solution

  • If a and b are integers then the expression a+b is an integer. If a and b of the same kind, then the expression is of that kind. If they are of different kind but one has greater decimal exponent range then the expression is of that kind.

    If a and b are of the same kind as a+b then there is no conversion. An operand (a or b) which is of different kind from a+b is treated as though it is converted to that kind.

    So, if you want the result to be of kind 8 (assuming that is the one of greater range) then you need one (or both) of the operands to be of kind 8. In the case of the question:

    • b1+b2 both are kind 8;
    • a1+b1, b1 of kind 8, a1 converted to that kind;
    • a1+a2, both are kind 4, not converted, result of kind 4.

    In the case of a+b+c then the expression is treated as (a+b)+c:

    • a2+a1+ugly, a2+a1 has no conversion in its evaluation, but is converted for sum (a2+a1)+ugly
    • ugly+a2+a1, ugly+a2 has a2 converted to kind 8, then a1 converted to kind 8 to give result of kind 8.

    So, if a2+a1 is not valid for range then a2+a1+ugly has that same problem, but ugly+a2+a1 has both a1 and a2 treated as kind 8.

    Finally:

    • int(a1,8)+int(a2,8) has explicit conversion to kind 8;
    • int(a1,8)+a2, a1+int(a2,8) has the expected behaviour.

    All of these are ugly: don't use kinds like 4 and 8, especially if you have int64 available and behaving as you want.