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

How best to handle integer overflow situations ?

WSinc
New Contributor I
2,493 Views

Hello -

as far as I know, in the current version of Fortran, we dont have a way to AUTOMATICALLY detect an integer overflow.

Of course, one can try to mitigate that by making integers 8 bytes long.

when I have short integers, I have to test to see (for example) if the sum of two positive integers is still positive.

But I don't think the CPU can detect a situation where the answer is wrong.

I was wondering if anything is being done to improve this situation.

In the olden days, we always got a trap if the result was too big to fit into the result.

On an IBM 7094, if you added two one byte integers whose result is >127, you would get an overflow trap.

Or if you multiplied 17*17, you would get a TRAP as well.

Of course, you might WANT the result to be possibly bigger:

integer(1) X/17/,y/17/

integer(2) XY

XY=X*Y

 

The compiler will NOT give you the right answer here, of course, since it will do an integer(1) multiply.

0 Kudos
17 Replies
Martyn_C_Intel
Employee
2,493 Views

No, there isn't currently a way to trap integer overflow, and I'm afraid there are no plans to create one. The processor doesn't flag integer overflows the way it does floating-point overflows.

0 Kudos
WSinc
New Contributor I
2,493 Views

 

I was pretty sure the CPU cant do that, but do we have a way to tell the compiler to check the results?

That would be better than the PROGRAMMER having to put a bunch of numerical tests, cluttering up the code.

For example, we now have to say something like:

integer(1)  a/17/, b/17/, C

C=a*b

if(c<0)print *,"Integer multiply is wrong ! !"

Don't you think that is kinda STUPID ?

 

 

 

 

 

 

 

0 Kudos
FortranFan
Honored Contributor II
2,493 Views

billsincl wrote:

.. Don't you think that is kinda STUPID ? ..

@Bill,

You have been bringing this issue up for a long time now and you should now accept that the best option for you will be to do the handling of such situations by yourself.  If you really wish to do run-time handling, I strongly suggest you look into using assert type of functionality, especially when you are debugging your code.  Shown below is an example, can you please, please take a close look and see how you can use something like this?

Example of a custom assertion in Fortran:

module assert_m

   use, intrinsic :: iso_fortran_env, only : output_unit, I1 => int8, I2 => int16, I4 => int32, I8 => int64

   implicit none

   private

   public :: assert

contains

   subroutine assert( assertion, description, val, oun )

      !.. Argument list
      logical, intent(in)           :: assertion
      character(len=*), intent(in)  :: description
      class(*), intent(in)          :: val
      integer, intent(in), optional :: oun

      ! Output format
      character(len=*), parameter :: fmt_gen = "(*(g0))"

      ! Local variables
      integer, parameter :: MAX_LENGTH = 2048 ! suitable string size
      character(len=MAX_LENGTH) :: sval
      integer :: lun

      ! Output unit
      if ( present(oun) ) then
         lun = oun
      else
         lun = output_unit
      end if

      ! Process value
      select type ( val )
         type is ( real )
            blkr: block
               real :: rval
               rval = val
               write( sval, fmt=fmt_gen ) rval
            end block blkr
         type is ( integer(I1) )
            blki1: block
               integer(I1) :: ival
               ival = val
               write( sval, fmt=fmt_gen ) ival
            end block blki1
         type is ( integer(I2) )
            blki2: block
               integer(I2) :: ival
               ival = val
               write( sval, fmt=fmt_gen ) ival
            end block blki2
         type is ( integer(I4) )
            blki4: block
               integer(I4) :: ival
               ival = val
               write( sval, fmt=fmt_gen ) ival
            end block blki4
         type is ( integer(I8) )
            blki8: block
               integer(I8) :: ival
               ival = val
               write( sval, fmt=fmt_gen ) ival
            end block blki8
         type is ( character(len=*) )
              sval = val
         class default
            sval = "val is of unknown type"
      end select

      ! Error stop if assertion is true
      if ( assertion ) then
         write( lun, fmt=fmt_gen ) "Assertion: " // trim(description) // ", value = " // trim(sval)
         error stop 1
      end if

      return

   end subroutine assert

end module assert_m

Modification of your program to make use the custom assertion: note it requires /DCODE_ASSERT compiler option:

program p

   use, intrinsic :: iso_fortran_env, only : I1 => int8, DP => real64
!dec$ if defined (CODE_ASSERT)
   use assert_m, only : assert
!dec$ endif

   implicit none

   integer(I1) :: a, b, c

   a = 12
   b = 12
   c = a*b
!dec$ if defined (CODE_ASSERT)
   blka1: block
      logical :: assertion
      real(DP) :: c_dp
      c_dp = real(a,dp)*real(b,dp)
      assertion = abs(c_dp-real(c,dp)) > epsilon(1.0_dp)
      call assert( assertion, "Integer overflow while computing c at line 14 in p.f90", c )
   end block blka1
!dec$ endif
   print *, "c = ", c

   stop

end program p

Upon execution using Intel Fortran, the above program gives:

Assertion: Integer overflow while computing c at line 14 in p.f90, value = -112
1

Now change the value of a to 10 and b to 11 and recompile and rerun the program and you should get:

 c =  110
Press any key to continue . . .

And can this be the last time you bring this up?  That you will use some option like the one above or look to other ways to refactor your code so the integer overflows do NOT occur in the first place.  Thanks,

0 Kudos
Steve_Lionel
Honored Contributor III
2,493 Views

FWIW, it is possible to detect integer overflows on x86 - CVF did it. Unlike with floating overflow, it requires generating explicit code to check the processor flags after an integer instruction. However, the Intel code generator team has not considered it important enough to support, despite many customer requests. The code to do this is, by necessity, slower than without checking, though my view was that this was a perfectly appropriate debugging aid that users could enable or disable as they wished.

0 Kudos
FortranFan
Honored Contributor II
2,493 Views

billsincl wrote:

.. if(c<0)print *,"Integer multiply is wrong ! !" ..

@Bill,

Oh, I forgot to mention earlier: a check for negative result is NOT sufficient because the integer result can be based on (multiple) "wrap around" the range of the integer kind being used and the erroneous answer may be either positive or negative.  So you need better checks; you can use some bitwise arithmetic to 'assert' the result more cleanly, but in my example I used floating-point to simply illustrate the custom handling option.

If you're concerned about 'code clutter', well your only recourse may be a code design review of your needs so that the issue can be avoided in the first place.  That is unless someone else steps forward and gives you better ideas.  But I see you have oft been asking about this and here you are again, so that should tell you something.  Note though, with what I show with compiler directives, it is only 'clutter' in the source file; your 'production' code (e.g., Release versions of programs ) can be buiilt without such instructions by excluding the /D definition and thereby you can avoid any adverse impact with optimizations.

0 Kudos
Arjen_Markus
Honored Contributor I
2,493 Views

FortranFan wrote:

If you're concerned about 'code clutter', well your only recourse may be a code design review of your needs so that the issue can be avoided in the first place.  That is unless someone else steps forward and gives you better ideas.  But I see you have oft been asking about this and here you are again, so that should tell you something.  Note though, with what I show with compiler directives, it is only 'clutter' in the source file; your 'production' code (e.g., Release versions of programs ) can be buiilt without such instructions by excluding the /D definition and thereby you can avoid any adverse impact with optimizations.

With the possible exception of a genuine integer overflow during production runs :).

 

0 Kudos
FortranFan
Honored Contributor II
2,493 Views

Arjen Markus wrote:

Quote:

FortranFan wrote:

 

If you're concerned about 'code clutter', well your only recourse may be a code design review of your needs so that the issue can be avoided in the first place.  That is unless someone else steps forward and gives you better ideas.  But I see you have oft been asking about this and here you are again, so that should tell you something.  Note though, with what I show with compiler directives, it is only 'clutter' in the source file; your 'production' code (e.g., Release versions of programs ) can be buiilt without such instructions by excluding the /D definition and thereby you can avoid any adverse impact with optimizations.

 

 

With the possible exception of a genuine integer overflow during production runs :).

Indeed!  The expectation is that such situations have all been resolved during debugging and that with production, some form of 'assertion' exists at key points during the computations that then lead to appropriate treatment of the problem!

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,493 Views

Using C interoperability, you can call the C function __readeflags() to read the eflags, then test the O flag (bit 11). You can also write a C function, that returns a Fortran LOGICAL as a result of the assembly instruction SETO (eax/rax receives 1 if O flag set, 0 if not). Give the function a name not likely to be of use elsewhere (e.g. C_EFLAGS_O). You can also add similar functions for C (carry), B (borrow), ...

then use as

    c = a*b
    if(C_EFLAGS_O()) STOP "Integer Overflow"

The above is not going to be vector friendly. For vector instructions you can use conditional movs, though some work will have to be done with the intrinsic calls.

Jim Dempsey

 

0 Kudos
FortranFan
Honored Contributor II
2,492 Views

jimdempseyatthecove wrote:

Using C interoperability, you can call the C function __readeflags() to read the eflags, then test the O flag (bit 11). ..

Jim,

Do you have a working example with either Fortran or even just C?  I did not think the eflags bit set with the integer arithmetic in Intel Fortran, especially with the wraparound.

Thanks,

0 Kudos
mecej4
Honored Contributor III
2,493 Views

On X86/X64 processors (and many others) the flags register sees a lot of action. Almost every instruction may change one or more flag bits.

The problem with using a high level language function call to query the flags is that the sequence of machine instructions that are executed to do just that may themselves change the flags register before it is read into a memory location - a la Heisenberg.

Furthermore, such calls will probably slow down the program considerably. I have seen programs slowed down by two orders of magnitude with Lahey/Fujitsu LF95 when their -Haesux error-checking options were used.

If there is a Fortran program that is suspected to have problems with integer overflow, it is best to use a compiler that can generate, upon request, INTO instructions after integer arithmetic instructions. A matching interrupt handler can then generate a traceback report.

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,492 Views

The following is a solution with two projects: a C static library, and a Fortran program

// Chelper.c
#include "stdafx.h"
#include <intrin.h>
int getEflags()
{
 return __readeflags();
}

and

!  eflagsTest.f90 
program eflagsTest
    implicit none
    integer :: a,b,c
    integer(4) :: eflags
    integer, parameter :: eflags_O = Z"800"
    INTERFACE 
    FUNCTION getEflags( ) bind( c, NAME='getEflags')
    import
      integer(4) :: getEflags ! INT
     END FUNCTION
    END INTERFACE
    
    a = Z'70000000'
    b = a
    c = a + 1
    if(iand(getEflags(),eflags_O) /= 0) print *,"overflow"
    c = a + b
    if(iand(getEflags(),eflags_O) /= 0) print *,"overflow"
end program eflagsTest

You can expand upon this. (e.g. add a logical function isOverflow) but be careful not to introduce statements that will alter the (e)flags.

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
2,493 Views

Look, another use for an undocumented feature! No C required:

!  eflagsTest.f90 
program eflagsTest
    implicit none
    integer, volatile :: a,b,c
    integer(4) :: eflags
    integer, parameter :: eflags_O = Z"800"
    INTERFACE 
    FUNCTION getEflags( ) bind( c, NAME='__readeflags')
    !DEC$ ATTRIBUTES KNOWN_INTRINSIC :: getEflags
    import
      integer(4) :: getEflags ! INT
     END FUNCTION
    END INTERFACE
    
    a = Z'70000000'
    b = a
    c = a + 1
    if(iand(getEflags(),eflags_O) /= 0) print *,"overflow"
    c = a + b
    if(iand(getEflags(),eflags_O) /= 0) print *,"overflow"
end program eflagsTest

I had to add the VOLATILE as otherwise the whole program was optimized away.

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,493 Views

Steve,

Ahh... will (probably not) Intel document these attributes?

BTW, why is import required for an intrinsic function (i.e. non-library function)?

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
2,493 Views

KNOWN_INTRINSIC is an internal-use hack that was created to support a couple of OpenMP functions that the code generator "knows". This isn't related to Fortran intrinsics. In Fortran, you can use this to get at some (but not all) instruction intrinsics that Intel C++ supports - the arguments and function results have to be simple types - if the intrinsic is described as taking special register type arguments, it won't work. I have successfully used this for CPUID.

I doubt it will ever be documented, but it can be handy if you figure out how to use it. Unsupported, of course!

0 Kudos
jean-vezina
Beginner
2,493 Views

Good afternoon,

Thanks a lot Steve for that hint. Is the name of the function in the BIND clause identical in 64 bit Windows or the leading underscores have to be removed?

I was quite active several years ago in the Fortran community,  and with a new artificial intelligence project, I am coming back again.

Congratulations Steve for all your work for Intel and about Fortran in particular.

Regards,

Jean

 

0 Kudos
Steve_Lionel
Honored Contributor III
2,493 Views

The name in the BIND(C) is the name of the C++ intrinsic - has no relation with procedure name decoration. It isn't a procedure at all, this was just a simple way to get the compiler "back end" to recognize a particular usage,

0 Kudos
jean-vezina
Beginner
2,493 Views

Thanks a lot for the clarification!

Regards,

Jean

0 Kudos
Reply