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

Problem with Do loops

WSinc
New Contributor I
1,840 Views

This problem occurs when the DO INDEX 8 bytes.

I admit that we should not see this very often, but

apparently the lower and upper limits cant be more than 32 bits.

for example this Do Loop does not execute properly:

 

Integer (8)  ix

do    ix=1,2**36

enddo

 

It does not do ONE pass thru the loop, as if the upper limit is negative.

If the upper limit is 31 bits (the largest possible 4 byte number) then it executes properly.

 

Is this documented anywhere ?

0 Kudos
24 Replies
mecej4
Honored Contributor III
1,672 Views

The integer expression 2**36 is evaluated using the default integer size, which is 4-bytes. The value of the expression, as a result, is 0.

If you want the expression to be evaluated as an 8-byte integer, you may write "2_8**36" or another expression with the same effect.

The following test program may be illustrative of the issues:

program tst
   integer*8 :: i
!
   i=2**36
   print *,4,i
   i=2_8**36
   print *,8,i
   print *,int(2**36,kind=4),int(2_8**36,kind=8)
end

 

0 Kudos
WSinc
New Contributor I
1,672 Views

I dont understand -

The example you gave does not address DO LOOPS.

I was talking about DO LOOPS.

Namely, is there an upper limit on the SIZE of an integer that can be used

as the start or end values ?

Example:

integer(8) ix2/10000000000/

integer(8) ix1/1/

integer(8) ix

do ix=ix1,ix2,100000000

print *,"ix=",ix

enddo

 

In this example, ix2 is longer than 32 bits.

The largest value that can be put in a 4 byte integer is 2**31-1, since it has to hold negative integers as well.

0 Kudos
Steve_Lionel
Honored Contributor III
1,672 Views

Bill, what he is saying is that it is not sufficient to use an INTEGER(8) for the loop index variable, you must also use INTEGER(8) values for the start and end values (and ideally the increment). The standard specifies how the number of iterations is determined and it isn't based on the index variable. The count overflows default integer, but the compiler won't tell you that.

0 Kudos
WSinc
New Contributor I
1,672 Views

O I C  !!

Thanks for clarifying that !

Apparently it calculated the number of iterations BEFORE doing the loop,

rather than an IF test to determine whther the upper limit is satisfied.

 

l will remember you in my will..........

0 Kudos
gib
New Contributor II
1,672 Views

The answer to your question is implicit in what mecej4 wrote.  Try this:

integer(8) :: i,i1,i2

i1 = 1
i2 = 2_8**36
do i = i1,i2
	if (mod(i,1000000000) == 0) write(*,*) i
enddo
end

Alternatively, you could have declared n2 as integer(8), and set i2 = n2**36.

0 Kudos
WSinc
New Contributor I
1,672 Views

I will run GIB's example too, just for fun.

Whooopppppeeeee   !

0 Kudos
FortranFan
Honored Contributor II
1,672 Views

billsincl wrote:

I dont understand -

The example you gave does not address DO LOOPS.

I was talking about DO LOOPS.

..

@billsincl,

A tacit message here is a suggestion to you to make use of modern facilities in Fortran language which can help you with your coding needs with integer values that are well above those supported by the default integer type in Intel Fortran which is 32 bits e.g.,

  • use defined kinds of your data types consistently, 
  • isolate literal values of your data such as 2 and 36 to single instructions via named constants because otherwise the instructions can easily fall into mixed-mode arithmetic expressions with unexpected results for which the compiler can fail to warn you.

and so forth.  If you do so, you can have 'clean' looking code which also works as you would expect it too.

Here's an illustration:

   blk1: block
      use, intrinsic :: iso_fortran_env, only : IK => int32
      integer(kind=IK), parameter :: TWO = 2_ik
      integer(kind=IK), parameter :: THIRTYSIX = 36_ik
      integer(kind=IK) :: i
      logical :: OnePassThru
      i = TWO**THIRTYSIX
      print *, "In block 1: integer kind of ", IK, " toward INT32"
      print *, "Per Fortran standard specified storage size of 32 bits"
      print *, "i = 2**36 = ", i
      OnePassThru = .false.
      do i = 1, TWO**THIRTYSIX
         if (i == 1) OnePassThru = .true.
      end do
      print *, "One pass thru' of do i = 1, 2**36 loop?", OnePassThru
   end block blk1
   print *
   blk2: block
      use, intrinsic :: iso_fortran_env, only : IK => int64
      integer(kind=IK), parameter :: TWO = 2_ik
      integer(kind=IK), parameter :: THIRTYSIX = 36_ik
      integer(kind=IK) :: i
      logical :: OnePassThru
      i = TWO**THIRTYSIX
      print *, "In block 2: integer kind of ", IK, " toward INT64"
      print *, "Per Fortran standard specified storage size of 64 bits"
      print *, "i = 2**36 = ", i
      OnePassThru = .false.
      do i = 1, TWO**THIRTYSIX
         if (i == 1) OnePassThru = .true.
         if (i > 5) exit
      end do
      print *, "One pass thru' of do i = 1, 2**36 loop?", OnePassThru
   end block blk2
end

If you compile and link and run with above, you should get:

 In block 1: integer kind of  4  toward INT32
 Per Fortran standard specified storage size of 32 bits
 i = 2**36 =  0
 One pass thru' of do i = 1, 2**36 loop? F

 In block 2: integer kind of  8  toward INT64
 Per Fortran standard specified storage size of 64 bits
 i = 2**36 =  68719476736
 One pass thru' of do i = 1, 2**36 loop? T

What do you say, you think you can 'up your game' now with all this 'new-fangled stuff' with modern Fortran?!  There are lots of Fortran books and blogs that can help you along.

0 Kudos
Steve_Lionel
Honored Contributor III
1,672 Views

billsincl wrote:

Apparently it calculated the number of iterations BEFORE doing the loop,

rather than an IF test to determine whther the upper limit is satisfied.

Correct - that's what the standard says to do. Some compilers (including Intel's) will detect that you may modify the loop control variable inside the loop (not allowed by the standard. but it used to be seen in programs) and change to a test on each iteration rather than a count.

It's too bad that the team that develops the Intel code generator refuses to implement integer overflow checking. CVF had it. I am a bit more astonished that the compiler didn't gripe about 2**36. Some experimentation suggests to me that the compiler naively looked at the low 32 bits of 2**36, saw it was zero, and evaporated the loop. I think it could do better here.

0 Kudos
mecej4
Honored Contributor III
1,672 Views

Bill, it would help if, when you have a question regarding how a piece of code is not working as you expected, you provide a complete test program and state the compiler options (if any) that you used. The following program does elicit a message from the compiler:

program tst
integer(8) ix2/10000000000/
integer(8) ix1/1/
integer(8) ix
do ix=ix1,2e36
  if(mod(ix,ix2).eq.0)print *,ix
end do
end

The compiler output:

ifort bsdo.f90
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on IA-32, Version 19.0.3.203 Build 20190206
Copyright (C) 1985-2019 Intel Corporation.  All rights reserved.

bsdo.f90(5): error #6906: The value of the integer is either too great or too small, and overflow/underflow occurred.   [2E36]
do ix=ix1,2e36
----------^
compilation aborted for bsdo.f90 (code 1)

 

0 Kudos
gib
New Contributor II
1,672 Views

Not relevant to the original question, but there is something strange about the way code is being displayed.  In line 4 of the code block in post #6, and lines 3 and 20 of the block in post #8, the '_' is not displayed (on my screen, anyway).

0 Kudos
mecej4
Honored Contributor III
1,672 Views

Gib, please name the browser you are using. I see the underscores on the lines that you flagged quite well, in Chrome. Do you have any custom colors specified, especially for background color?

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,672 Views

FWIW Bill,

Mecej4's post #10 does not mean the compiler does not support a mathematical value of 2e36.

Rather it means (for this specific example) INTEGER(4) does not support 2e36.
The compiler did not go to the effort of determining the expression was a limit on an INTEGER(8) loop control variable and then suggest to you:
"Did you intend this to be 2_8e36?"

The Fortran compiler does not make these types of determinations (implicit promotion).

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor II
1,672 Views

mecej4 wrote:

.. you may write "2_8**36" ..:

..
   integer*8 :: i
..
   i=2_8**36
..

Note 'integer*8' is non-standard and hard-wired data type kinds as in '2_8**36' is really not a good coding practice

mecej4 wrote:

..

program tst
 .. 
 integer(8) ix
..
 do ix=ix1,2e36
 ..

Note '2e36' is not a valid literal constant of integer type per current Fortran standard; a statement such as 'do ix=ix1,2e36'  is not conformant with the Fortran standard

jimdempseyatthecove wrote:

.. it means (for this specific example) INTEGER(4) does not support 2e36.

The compiler did not go to the effort of determining the expression was a limit on an INTEGER(8) loop control variable and then suggest to you:
"Did you intend this to be 2_8e36?"..

Jim, FWIW '2_8e36' is not a valid int-literal-constant either.

Please note an underlying issue here is OP's approach to coding along the lines of that advanced by many typeless programming languages which also tend to include "user friendly' type promotions in mathematical expressions in them; and OP having expectations of the same with Intel Fortran while retaining (considerable) legacy of practices developed with older and non-standard dialects of FORTRAN.  Under the circumstances,

  1. OP needs to seriously decide why stick at all with Fortran any longer, why not start employing so many other convenient and powerful and fast advancing programming languages/paradigms that might be more suitable for the computing needs
  2. Or if there is a persuasive case to continue using Fortran, then whether to "get with the program" and really start to learn the Fortran standard and utilize what it offers consistently

Option 1 is entirely up to OP.  With option 2, readers here can help by refraining from non-standard suggestions such as 'integer*8' or integer literals such as '2e36' or inadvisable constructs as 'integer(8)' or '2_8**36' - such comments can do more harm and perpetuate the issues faced by OP rather than prove helpful.

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,672 Views

FWIW RE: 2_8

Not being on the Fortran Standards committee (and they not giving a hoot about my opinions)

use, intrinsic :: iso_fortran_env, only : IK => int64
...
    2_IK

IMHO has no syntactical difference with 2_8

I fully understand the per Fortran specifications that IK (in the above) does not necessarily have to be "integer, parameter :: IK=8".
IOW this is an implementation issue.

While IK, as above, provides you with the flexibility of changing the integer literal types throughout the application (a good thing)...
it also obfuscates just what 2_IK**36 actually means. The declaration of IK can be in some module not visible to the reader of the source code, i.e. there is no way for the reader to know what is value of 2_IK**36.

My 2 cents.

>>OP needs to seriously decide why stick at all with Fortran any longer...

IMHO the major benefits of sticking with Fortran is precisely because it has efficient multi-dimensional arrays (due to array descriptors). It permits array slices with pointers with stride other than 1, and with negative indexing. While one can write a class in C++ to emulate this, it is not native to the language and the code optimizations by the compiler may not be as efficient (in all cases).

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor II
1,672 Views

jimdempseyatthecove wrote:

FWIW RE: 2_8

Not being on the Fortran Standards committee (and they not giving a hoot about my opinions)

use, intrinsic :: iso_fortran_env, only : IK => int64
...
    2_IK

IMHO has no syntactical difference with 2_8

I fully understand the per Fortran specifications that IK (in the above) does not necessarily have to be "integer, parameter :: IK=8".
IOW this is an implementation issue.

While IK, as above, provides you with the flexibility of changing the integer literal types throughout the application (a good thing)...
it also obfuscates just what 2_IK**36 actually means. The declaration of IK can be in some module not visible to the reader of the source code, i.e. there is no way for the reader to know what is value of 2_IK**36.

My 2 cents.

>>OP needs to seriously decide why stick at all with Fortran any longer...

IMHO the major benefits of sticking with Fortran is precisely because it has efficient multi-dimensional arrays (due to array descriptors). It permits array slices with pointers with stride other than 1, and with negative indexing. While one can write a class in C++ to emulate this, it is not native to the language and the code optimizations by the compiler may not be as efficient (in all cases).

Jim Dempsey

Please note the term 'byte' has no relevance in the Fortran standard; you will not find it in recognized 'Terms and  Definitions" and the only places you will find 'byte' mentioned in the standard is with notes toward interoperability with a C companion processor.

So if one presumes '8 bytes' has some special meaning in Fortran and/or any references to '8' in data type kinds, literals (e.g., 2_8), and statements are any revealing or informative or otherwise not obfuscating, they are assuming considerable risk, especially with code portability.

What the standard recognizes is a way via the intrinsic module 'ISO_FORTRAN_ENV' to provide implementation support for data type kinds with certain storage sizes e.g., INT32 toward integers with 32-bits of storage and INT64 with 64 bits.  To the extent a compiler makes adequate use of the storage size to support ranges of data types of interest to a coder. as in say an integer with kind of INT64 can handle a value such as 2**36, the coders can utilize these predefined named constants.  And should the named constants appear too long (as in 2_int64), the coder can rename the named constant toward the kind, as shown in Quote #8, to anything which is sufficiently mnemonic for the coder and the team(s) involved.  That is, as long as the Fortran naming rules are followed e.g., the rename can be IK or I8 or S, etc. but not '8'!!

Now, what readers will also have noted is that the support of literal constants in the current Fortran standard is not without issues.  Coders also take on risk of unexpected results when they fail to append (or prepend) the KIND to the constant.    

But what is also increasingly clear is Fortran compiler implementations are lining up behind the standard (and this is great), so if OP or anyone else wished to persist with Fortran, then making best use of the standard facilities is what can help avoid the issue described in the original post.

As to how the coder wants to 'style' the code to conform to the standard is a matter of taste and an appreciation of good coding practices.  What is shown in Quote #8 is but one suggestion given where things stand with current standard, particularly the situation with literal constants.  Coders can always adopt the axiom "To each their own" as long as they write conforming code!  But if one can't pay attention to some basic rules in the standard with data types leading to the kind of issue raised here, the question to ask is how can additional language facilities such as an array container in Fortran be utilized reliably, not to forget any number of other computing environments - .NET (C#/VB), Python, MATLAB, Excel, etc. - provide very user-friendly options for many containers, not just arrays, which OP might find more suitable for computing needs..

0 Kudos
gib
New Contributor II
1,672 Views

@mecej4: I'm using Mozilla Firefox.  The behaviour today is a bit different - I can now see all the '_' in post #8, but not that in line 3 of post #6.  On my laptop (also with Firefox)  I see all '_'.  It's odd.  No, I haven't set any colours.  Obviously it is something funny at my end.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,672 Views

FortranFan,

Thanks for the well composed response, I appreciate your efforts (old dog, new tricks).

To assure correctness in programming, one should then (absurdly):

DO IX=INT(1,KIND=IX), INT(2, KIND=IX)**INT(36, KIND=IX)

Though the 1, 2, and 36 may need to be recursively INT-KINDed forever..

This is due to potential uncertainty of the kind IX is at the point of the DO statement. It may be IK at initial writing, then sometime later something else.

In support of your post #16:

I grew up coding on systems with 12-bit, 16-bit, 18-bit and 36-bit CPU's where INTEGER could be associated with the CPU word size but CHARACTER/BYTE size entirely something else:

12-bit systems used 6-bit characters, 12-bit Hollerith (or three 8-bit characters packed into two adjacent integers)
16-bit systems used either 8-bit characters or RADIX-50 with three characters within one word
18-bit systems could have used two 9-bit characters or two 8-bit characters and two left over bits
36-bit systems used 7-bit characters.

These CPUs are not around any more except in museums or probably in some critical government institution.

For these systems, just what would an INTEGER(4) mean?
The older systems above clearly illustrate that INTEGER(8) or 2_8 is unsuitable for correctness in programming and that some abstract method is required, such as 2_IK, 2_int64, ..., though by the same argument 2_int64 should be avoided due to that word length may not always be future-proofed. What is going to happen when quantum computing arrives or some other word/byte-size CPU evolves.

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor II
1,672 Views

jimdempseyatthecove wrote:

..
To assure correctness in programming, one should then (absurdly):


DO IX=INT(1,KIND=IX), INT(2, KIND=IX)**INT(36, KIND=IX)


Though the 1, 2, and 36 may need to be recursively INT-KINDed forever..


This is due to potential uncertainty of the kind IX is at the point of the DO statement. It may be IK at initial writing, then sometime later something else. ..


Jim,


I agree with the absurdity part of coding implied with current Fortran standard with your comments above as they pertain to literal values of 1, 2, 36, .. etc.!  This is why I wrote earlier the current Fortran standard has issues when it comes to support toward literal constants.

jimdempseyatthecove wrote:

..
I grew up coding on systems with 12-bit, 16-bit, 18-bit and 36-bit CPU's where INTEGER could be associated with the CPU word size but CHARACTER/BYTE size entirely something else ..


For these systems, just what would an INTEGER(4) mean?  ..



Re: "what would an INTEGER(4) mean?, " interesting question, I suppose depending on the compiler implementation, it can mean the same, something different, or nothing!


By the way, would you have some FORTRAN code from your work with, say, a 12-bit CPU (perhaps a listing) and expected results which you can try to run on a modern computer and compare the results?  It'll be interested to see them, if you can share!


On a somewhat serious note, Fortran adheres to its characterization as an imperative programming language in a presumptive manner.  A coder is first expected to invoke a Fortran intrinsic function SELECTED_INT_KIND( r ) where r is supplied an integral value such that it represents all integer values of 'n' expected to encounter in the coder's program with -10**r < n < 10**r.  If the returned result of this function is -1, stop.  Otherwise, hold on to with one's dear life, name it something, and use this name in all the integer contexts where one might be tempted to otherwise use '4' or '8'. 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,672 Views

>>By the way, would you have some FORTRAN code from your work with, say, a 12-bit CPU (perhaps a listing) and expected results which you can try to run on a modern computer and compare the results?

Hmm, I may have some on a 60 megabyte 3M tape cartridge, for a system who's tape drive died, and which I donated to a museum.

These programs were written in FORTRAN IV for the PDP-8 series of computers.

INTEGER was 12-bits
REAL was 36-bits (12-bit exponent, 24-bit mantissa). I cannot trust my memory as to if the leading 1 bit of the mantissa was implicit or not (I am leaning towards it being present). Therefore the results data, though different, may possibly have been more precise (for non-exact representable numbers).

That era of system could:

a) do all the floating point in software
b) with a hardware option it could manipulate the 24-bit mantissa (rotates, adds, normalize, etc...)
c) with a co-processor, you had two CPU's. One for the native 12-bit CPU, and a second CPU that had an entirely different instruction set for executing Floating point oriented programs. The two CPUs shared the same memory. Hardware multi-threading back in the 70's. The Fortran compiler would generate both instruction sets, the WRITE/READ performed on the host CPU, the rest of the code either by hardware or emulation.

Jim Dempsey

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,430 Views

Oh, I forgot, if the host system was a PDP-12, the main 12-bit CPU had two instruction sets: PDP-8 and LINK-8, plus the optional floating point CPU. The host CPU could programically switch between instruction sets.

Jim Dempsey

0 Kudos
Reply