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

Q about integer multiply

WSinc
New Contributor I
1,197 Views

Suppose we have:

      integer(4) x,y

      integer (8) z

      z=x*y

In almost all cases, the result will be wrong, in fact, more than 99.9999 percent of the

time, since the compiler does not understand that the result can be more than 4 bytes.

This wrong result is not detected, since apparently the compiler does not check for an overflow.

At least, not on the INTEL compiler, although in my work at Aerospace and JPL IBM 7094

main frames, it did tell you about the overflow.

so to avoid this, I have to say:

       z=int(x,8)*y

Is there a simpler way around this? Maybe the compiler people should take a serious look at this.

You see the same problem with INTEGER(1) AND INTEGER(2) variables, BTW...........

 

As well as with the ADD operator. seems like an overflow check would be a simple answer,

though it does slow down things. So maybe as an option ?

0 Kudos
12 Replies
Arjen_Markus
Honored Contributor I
1,193 Views

I think your mental model is slightly wrong: it is not a matter of the compiler not understanding that the result can be larger than expressable in 4 bytes, but the Fortran standard prescribes that the right-hand side is evaluated independently of the left-hand side.

Furthermore, if I understand these matters correctly, detecting overflows and other such unusual conditions is a task for the computer's processor, not for the compiler/runtime and most hardware checks only for floating-point exceptions. I think I read somewhere that this behaviour (silent overlfows) is even expected in a lot of software.

The simplest solution is: do not mix different types of integers. I would use the kind mechanism to define integers of the desired range, just as is commonly done for reals. 

0 Kudos
TimP
Honored Contributor III
1,194 Views

In fact, Fortran on the 7094 didn't support mixed data types, nor even any precursor of KIND.  Later f66 compilers support mixed data types only outside the standard, with vendor-specific treatments.

0 Kudos
WSinc
New Contributor I
1,194 Views

The problem with having ALL INTEGERS OF THE SAME TYPE, is that you can use up a lot of unwanted storage that way.

Suppose, for example, you want to get the largest product from two arrays of integer(1):

integer (1)  x(1000000), y (1000000)

integer(2) xymax/0/,xy

do i=1,1000000

do j=1,1000000

xy=int(x(i),2)*y(j)

xymax=max(xymax,xy)

enddo

enddo

Print *,xymax=",xymax

It would be really stupid to make those integer arrays integer(2) and waste 2000000 bytes of storage,

don't you agree?

Of, course in this case, one should take the MAX of each array, then multiply that, but this is

just to illustrate the point.

If you know that no integer is > 127, then integer(1) is the largest you would make them.

Unfortunately, FORTEAN 95 does not give us a convenient way to multiply two INTEGER(8) quantities,

or even INTEGER(8) by a smaller size integer, with getting an overflow and detecting it. One can, however, simulate it by using

REAL(16), though, as follows:

real(16)  xy

integer(8) x,y

xy=real(x,16)*real(y,16) ! they dont both HAVE to be real

You still would have trouble storing the result, however.

Also REAL(16) cannot handle a 126 bit result, since some of the 16 bytes (12 bits ?) is used by the exponent.

The largest product you can have is (2**63-1)*(2**63-1) which would be 126 bits long.

0 Kudos
andrew_4619
Honored Contributor III
1,194 Views

so to avoid this, I have to say:

       z=int(x,8)*y

Is there a simpler way around this?

It isn't exactly a complicated solution is it? Or would an 'easier' solution be to redfnne how the Fortran language works?

 

0 Kudos
WSinc
New Contributor I
1,193 Views

Well, the really bad part is you can get completely wrong answers without KNOWING it.

That can be really critical if you have a really serious application.

 

For example : Medical, space travel, missile launching, etc.

0 Kudos
WSinc
New Contributor I
1,193 Views

arjenmarkus said "the right side is evaluated independently."

But it does NOT evaluate the RHS properly if the result is too big to fit.

Unless one forces it by using int(*,8), int (*,4), etc.

 

Storing it, well, that's a different matter.

0 Kudos
dboggs
New Contributor I
1,194 Views

Bill,

Pardon my French but as someone who has apparently been working with Fortran (and FORTRAN) for quite a few years, you should know about the risk of overflow in situations exactly like this. It has been this way for about 60 years. The way to protect against it is as others have already indicated.

0 Kudos
JVanB
Valued Contributor II
1,194 Views

It does seem a great pity that Haswell has such nice primitives as MULX, ADCX, and ADOX for performing bignum arithmetic, but Intel's Fortran compiler does not give us access to them.

 

0 Kudos
WSinc
New Contributor I
1,194 Views

I have been using Fortran myself for over 50 years, since the Fortran II days.

But Intel is the only company that ignores integer overflow situations, that I have ever seen.

Even if we take all these precautions, it can still bite you in the tush.

How do I get the Haswell product ?

0 Kudos
mecej4
Honored Contributor III
1,193 Views

billsincl wrote:

How do I get the Haswell product ?

Mainstream Haswells do not support ADCOX. Some (all? I'm not sure, need to search and read Intel processor manuals to find out) Haswell-E processors do. What this implies is that if you want ADCOX instructions on, say, a laptop, you need a Broadwell or a Skylake CPU.

Even if the CPU has some desired instructions/features in its repertoire, you need support in the programming language or through support libraries and, sometimes, in the OS and even the BIOS and motherboard.

You can find out what features are available in a specific CPU by running the following C program if you have the IPP library and the Intel C compiler installed.

#include <stdio.h>
#include <ippcore.h>
int main(){
  Ipp64u mask; IppStatus stat;

  stat=ippGetCpuFeatures(&mask,NULL);

  printf("stat(ADCOX) = %1d\n",mask & ippCPUID_ADCOX ? 1 : 0);
  printf("stat(AVX)   = %1d\n",mask & ippCPUID_AVX   ? 1 : 0);
  printf("stat(SSSE3) = %1d\n",mask & ippCPUID_SSSE3 ? 1 : 0);
}

If the first line prints a non-zero, the ADCOX feature is available but, if your software does not allow you to exploit the capability in a convenient manner, in effect it is not available.

All that said, I think that you are making much ado about little and have unreasonable expectations. Generations of programmers have survived life without hardware interrupt/trap on integer overflow. If n-byte integers were provided, invariably some users would be asking for 2.n-byte support (in this thread, n <= 4). On x86/x64 the INTO instruction is available for use with the flags register to catch overflow, but needs support in the compiler. Some Windows Fortran compilers do let you check for integer overflow (at the expense of slowing down your programs).

0 Kudos
JVanB
Valued Contributor II
1,193 Views

I guess you're right about not having ADOX/ADCX instructions. I wrote up something to check via CPUID:

; fasm mulx.asm
format MS64 coff

section '.code' code readable executable
public FTN_CPUID
FTN_CPUID:
xchg eax, edx
xchg eax, ecx
CPUID
mov [r8], eax
mov [r8+4], ecx
mov [r8+8], edx
mov [r8+12], ebx
ret
! ifort /nologo adox.f90 mulx.obj
program P
   use ISO_FORTRAN_ENV
   implicit none
   interface
      subroutine FTN_CPUID(eax,ecx,results) bind(C,name='FTN_CPUID')
         import
         implicit none
         integer(INT32), value :: eax
         integer(INT32), value :: ecx
         integer(INT32) results(4)
      end subroutine FTN_CPUID
   end interface
   integer(INT32) eax
   integer(INT32) ecx
   integer(INT32) results(4)

   eax = int(Z'01',INT32)
   ecx = int(Z'00',INT32)
   write(*,'(a)') 'Input:'
   write(*,'(a,z8.8)') 'eax = ', eax
   write(*,'(a,z8.8)') 'ecx = ', ecx
   call FTN_CPUID(eax,ecx,results)
   write(*,'(a)') 'Outut:'
   write(*,'(a,z8.8)') 'eax = ', results(1)
   write(*,'(a,z8.8)') 'ecx = ', results(2)
   write(*,'(a,z8.8)') 'edx = ', results(3)
   write(*,'(a,z8.8)') 'ebx = ', results(4)
   eax = int(Z'07',INT32)
   ecx = int(Z'00',INT32)
   write(*,'(a)') 'Input:'
   write(*,'(a,z8.8)') 'eax = ', eax
   write(*,'(a,z8.8)') 'ecx = ', ecx
   call FTN_CPUID(eax,ecx,results)
   write(*,'(a)') 'Outut:'
   write(*,'(a,z8.8)') 'eax = ', results(1)
   write(*,'(a,z8.8)') 'ecx = ', results(2)
   write(*,'(a,z8.8)') 'edx = ', results(3)
   write(*,'(a,z8.8)') 'ebx = ', results(4)
end program P
Input:
eax = 00000001
ecx = 00000000
Outut:
eax = 000306C3
ecx = 7FFAFBFF
edx = BFEBFBFF
ebx = 05100800
Input:
eax = 00000007
ecx = 00000000
Outut:
eax = 00000000
ecx = 00000000
edx = 00000000
ebx = 000027AB

The first CPUID shows

Processor Type = B'00' -- Original OEM Processor

Family ID = Z'06' = Display Family

Model ID = Z'0C'

Extended Model ID = Z'03' => Display Model = Z'3C' => Haswell (not E)

Stepping ID = Z'03'

The second CPUID shows

BMI2 = .TRUE. => MULX available

ADX = .FALSE. => ADOX/ADCX not available :(

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,193 Views

integer(8) :: x, y, z

z = x * y
if(z / y .ne. x) then
  ! overflow

Jim Dempsey

0 Kudos
Reply