Can overindexing in FORTRAN 77 modify the program itself? - fortran

Here is a little program in FORTRAN 77
dimension totlev(20)
do 100 i=1,24
totlev(i)=0.0
write(0,*) 'totlev i=',i, totlev(i)
100 continue
end
I compile it using MinGW by typing gfortran test.f and I do get a warning (not an error):
test.f:4:14:
do 100 i=1,25
2
totlev(i)=0.0
1
Warning: Array reference at (1) out of bounds (25 > 20) in loop beginning at (2)
test.f:5:40:
test.f:3:72:
do 100 i=1,25
2
test.f:5:40:
write(0,*) 'totlev i=',i, totlev(i)
1
Warning: Array reference at (1) out of bounds (25 > 20) in loop beginning at (2)
However, not always such a warning would be produced if it was a longer program. An executable is created. When I run it it behaves like an infinite loop.
And this is my problem: How is an infinite loop even possible with the DO iteration? Isn't it a logical impossibility? My only explanation is that overindexing in this case reaches to the program code itself and changes it. Is that possible?
I use Windows 7 OS if that's relevant.

It's not changing the code, it's changing the variable i. Both the array totlev(20) and the scalar i are local variables, and thus typically stored in the program's stack frame (though the standard leaves this choice to the 'processor', Fortran-speak for implementation). In this case the compiler apparently put i 4 'real's (probably 16 bytes) after the end of totlev, so assigning to totlev(24) actually changes i. Fortran basically requires that an integer and single/default-precision real variable be the same size, and while it doesn't require any particular relationship between the representations for integers and reals, most machines today use 'IEEE 754' floating-point and in that system a real 0.0 has the same representation as an integer 0.
On many though not all computer architectures it is possible to address code by indexing an array out of range, but this almost always requires indexes far out of range: millions or billions or more, not one or two. On older architectures it was often possible both to read and write code this way, but most systems since about 1980 have memory protection so that you can't write to code. In particular all Windows NT-series systems do this, which includes Windows 7.

Related

FORTRAN 77 Divide By Zero Behavior

I am working on re-engineering an old FORTRAN77 program to Python for a while now. I'm running into an issue, though: when dividing by zero, it appears that the FORTRAN program just continues processing the data without issue. However, predictably it causes an issue in Python. I'm not able to find a discussion about this on any official channel for F77, and I only have an old version of the source code for the program I am translating that I can't get to compile.
TL;DR: How does F77 handle division by zero for the following cases?:
REAL division
INT division
The numerator is also zero (e.g. 0./0.)
Yes, I also have code that does nothing when a divide by zero error is encountered. Usually, it is the programmers responsibility to ensure that the results are either expected (the target variable's value is unchanged) or an error is thrown etc. In other words, you need to inspect any division operation for a possible zero divisor. Modern operating systems throw an internal exception on divide by zero (and assign NAN to the target variable if the system would pause under these circumstances), most older Fortran code is written such that divide by zero doesn't matter.

Where does this Fortran variable come from?

Can anyone tell me about the costh variable used in the following subroutine? From where is the subroutine obtaining the value of this variable? Is it some error or inbuilt function?
The complete subroutine is given below.
SUBROUTINE MOMENT(I,A,AP,MODE,EP,NCASC,ID)
COMMON/MOM/VY(99996),VZ(99996),VX(99996)
DIMENSION ERES(99996),FCT(99996)
COMMON/XQANG/SUM(300,10),MDIR,COSTH
EQUIVALENCE(ERES(1),VY(1)),(FCT(1),VZ(1))
IF(ID-2)10,30,40
C INITIALIZATION
10 N=1
NN=N+NCASC-1
DO 20 J=N,NN
VY(J)=EP*MDIR
VX(J)=0.
20 VZ(J)=EP*(1-MDIR)
GO TO 60
C
C CALCULATION OF MOMENT
C
CKM 30 RN4=RANF(0.)
30 RN4=RAN(ISEED) !KM
PHI=6.28318*RN4
SOX=2.*AP*EP/(A**2*931.5)
C BUG FIX A*(AP+A)REPLACED BY A**2 12/15/82
C IF(SOX.LT.0.)WRITE(*,943)AP,EP,A
VT=SQRT(SOX)
SOX=1.-COSTH**2
IF(SOX.LT.0.)SOX=0.
SINTH=SQRT(SOX)
VZSE=VT*COSTH
VYSE=VT*SINTH*SIN(PHI)
VXSE=VT*SINTH*COS(PHI)
VZ(I)=VZ(I)+VZSE
VY(I)=VY(I)+VYSE
VX(I)=VX(I)+VXSE
VZP=VZ(I)-VZSE*(A/AP+1.)
VYP=VY(I)-VYSE*(A/AP+1.)
VXP=VX(I)-VXSE*(A/AP+1.)
VPP2=VZP*VZP+VYP*VYP+VXP*VXP
EPART=AP*VPP2*469.
SOX=0.
IF(VPP2.GT.0.0)SOX=VZP/SQRT(VPP2)
C DO NOT USE QUICK FUNCTIONS HERE
ANG=ACOS(SOX)*180./3.1415927
CALL OUTEM(2,MODE,EPART,ANG)
GO TO 60
C
C END CALCULATION
C
40 VFTS=VX(I)**2+VY(I)**2+VZ(I)**2
ERES(I)=0.5*A*VFTS*931.5
IF(VFTS.NE.0.)GO TO 50
FCT(I)=0.0
GO TO 60
50 FCT(I)=ACOS(VZ(I)/SQRT(VFTS))*180./3.1415927
60 RETURN
END
This line
COMMON/XQANG/SUM(300,10),MDIR,COSTH
informs the subroutine about a common block called XQANG which has an element called COSTH. In the absence of other information, and in a code of that vintage, this is most likely to be a real variable.
Common blocks are an early-Fortran mechanism for sharing variables across program units, such as between a main program and a subroutine. In straightforward use the same common block declaration will be found in multiple locations, with the same list of variables. Each declaration refers to the same variables.
There is a twist though, and one to watch out for carefully. The common block is actually a shared block of memory, and there is no requirement that each instance of the declaration identify the same variables. One common use of common blocks was to declare, say, an array of 100 reals in one location, but to declare two arrays each of 50 reals elsewhere -- same memory, different variables.
Even better, they could also be used to change the types of variables (sort of). One usage of a common block might contain a real variable occupying 4 bytes, while another usage of the same block might contain a 4-byte integer variable at the same location - same bits, different interpretations.
These twists are among the reasons for common blocks being widely deprecated. Another reason for their deprecation is that they obscure the sharing of variables, these days most of us prefer to explicitly pass arguments into and out of subroutines through their argument lists.
I'd guess very few Fortran programmers under 50 are still writing new code using them. But Fortran programmers from 8 - 80 are still working on codes containing them.

gfortran Error: Integer too big for its kind at (1) [duplicate]

This question already has an answer here:
Fortran: Integer too big for its kind
(1 answer)
Closed 7 years ago.
I want to see integer kind number of gfortran
so I write this line
write(*,"(1X,I20,'correspond to kind ',I2)"),11111111111,kind(11111111111)
There will be compilation error says
precision of type test.f90:67:57: Error: Integer too big for its kind
at (1). Th is check can be disabled with the option -fno-range-check
So I tried recompile with -fno-range-check. But it gives result
-1773790777correspond to kind 4
What is wrong? On the other hand, intel fortran gives no error and correct answer
An integer literal without any kind designation is always of the default kind no matter what value you enter1. There is therefore no much sense to even inquire
kind(111111111111)
the kind of any such literal is always the default kind, provided the value is valid. So the same as kind(1).
All integer kinds have a limited range of values. The largest one you can get using
write(*,*) HUGE(1)
Here instead of 1 you can use any other constant or variable of the integer kind you examine. Most often, the default value of HUGE will be 2147483647 which corresponds to 32-bit integers.
To use larger integer literal constants, use larger non-default kinds.
It doesn't matter if you use the methods from Fortran 90:
integer, parameter :: lk = selected_int_kind(15)
write(*,*) 11111111111_lk
or from Fortran 2008
use iso_fortran_env
integer, parameter :: lk = int64
write(*,*) 11111111111_lk
Both will work. Of course kind(11111111111_lk) will return the value of lk.
1 That is in standard Fortran. Some compilers may promote a large value to a larger kind for you, but only as a non-standard extension. You may be unpleasantly surprised when moving to a compiler, which keeps the standard behaviour.
There may be a better explanation, but this is how I understand it. The short answer is the compiler defaults to a 4 byte integer.
Fortran is statically typed and you have not declared a variable type. The compiler is forced to use the default integer kind, 4 bytes in this case. The kind function simply returns the 'kind' of integer used. The compiler is letting you know that you are trying to assign a value too large for a 4 byte integer. When you apply -fno-range-check the compiler ignores this fact and the value overflows, thus the negative value returned. You can specify that the default integer kind be 8 bytes, -fdefault-integer-8. See the gfortran docs
Example foo.f90:
program foo
write(*,"(1X,I20,' correspond to kind ',I2)"),111111111111,kind(111111111111)
write(*,"(1X,I20,' correspond to kind ',I2)"),11,kind(11)
end program foo
Compiled with:
$ gfortran -fdefault-integer-8 -o foo.exe foo.f90
$ foo
Results in:
111111111111 correspond to kind 8
11 correspond to kind 8
So you can see the compiler is indifferent to the actual value you are testing.
However, I don't think this gets at the root of what you are trying to do, which I assume is to discover the minimum size of integer necessary for a specific numeric value. I don't know of a way to do this off hand with fortran. See this here and here for solutions you might be able to port from C. The second approach looks promising. In a dynamically typed language like Python the type assignment is handled for you.
>>> type(111111111111)
<type 'long'>
>>> type(11111)
<type 'int'>

Fortran : Initialize all variables to a specific default value

I am working on a ~40 years old Fortran spaghetti code with lots of variables that are implicitly declared. So there is not a simple way to even know what variables exist in the code in order to initialize their values. Now, is there a way to tell the compiler (for example Intel Fortran) to initialize all variables in the code to a specific default value (e.g., -999) other than zero or a very large number, as provided by Intel compiler?
gfortran provides some options for this. Integers can be intialized with -finit-integer=n where n is an integer. For real numbers you can use -finit-real=<zero|inf|-inf|nan|snan>. Together with -ffpe-trap=denormal this can be very helpful, to get uninitialized reals.
You probably want :
ifort -check uninit
Note per the man page this only checks scalars
Also, based on some quick testing it is a pretty weak check. It doesn't catch this simple thing for example:
program test
call f(i)
end
subroutine f(j)
write(*,*)j
end
returns 0 ..
I suppose its better than nothing though.

Importing data from file to array

I have 2 dimensional table in file, which look like this:
11, 12, 13, 14, 15
21, 22, 23, 24, 25
I want it to be imported in 2 dimensional array. I wrote this code:
INTEGER :: SMALL(10)
DO I = 1, 3
READ(UNIT=10, FMT='(5I4)') SMALL
WRITE(UNIT=*, FMT='(6X,5I4)') SMALL
ENDDO
But it imports everything in one dimensional array.
EDIT:
I've updated code:
program filet
integer :: reason
integer, dimension(2,5) :: small
open(10, file='boundary.inp', access='sequential', status='old', FORM='FORMATTED')
rewind(10)
DO
READ(UNIT=10, FMT='(5I4)', iostat=reason) SMALL
if (reason /= 0) exit
WRITE(UNIT=*, FMT='(6X,5I4)') SMALL
ENDDO
write (*,*) small(2,1)
end program
Here is output:
11 12 13 14 15
21 22 23 24 25
12
Well, you have defined SMALL to be a 1-D array, and Fortran is just trying to be helpful. You should perhaps have defined SMALL like this;
integer, dimension(2,5) :: small
What happened when the read statement was executed was that the system ran out of edit descriptor (you specified 5 integers) before either SMALL was full or the end of the file was encountered. If I remember rightly Fortran will re-use the edit descriptor until either SMALL is full or the end-of-file is encountered. But this behaviour has been changed over the years, according to Fortran standards, and various compilers have implemented various non-standard features in this part of the language, so you may need to check your compiler's documentation or do some more experiments to figure out exactly what happens.
I think your code is also a bit peculiar in that you read from SMALL 3 times. Why ?
EDIT: OK, we're getting there. You have just discovered that Fortran stores arrays in column-major order. I believe that most other programming languages store them in row-major order. In other words, the first element of your array is small(1,1), the second (in memory) is small(2,1), the third is small(1,2) and so forth. I think that your read (and write) statements are not standard but widely implemented (which is not unusual in Fortran compilers). I may be wrong, it may be standard. Either way, the read statement is being interpreted to read the elements of small in column-major order. The first number read is put in small(1,1), the second in small(2,1), the third in small(1,2) and so on.
Your write statement makes use of the same feature; you might have discovered this for yourself if you had written out the elements in loops with the indices printed too.
The idiomatic Fortran way of reading an array and controlling the order in which elements are placed into the array, is to include an implied-do loop in the read statement, like this:
READ(UNIT=10, FMT='(5I4)', iostat=reason) ((SMALL(row,col), col = 1,numCol), row=1,numRow)
You can also use this approach in write statements.
You should also study your compiler documentation carefully and determine how to switch on warnings for all non-standard features.
Adding to what High Performance Mark wrote...
If you want to use commas to separate the numbers, then you should use list-directed IO rather than formatted IO. (Sometimes this is called format-free IO, but that non-standard term is easy to confuse with binary IO). This is easier to use since you don't have to arrange the numbers precisely in columns and can separate them with spaces or commas. The read is simply "read (10, *) variables"
But sticking to formatted IO, here is some sample code:
program demo1
implicit none
integer, dimension (2,5) :: small
integer :: irow, jcol
open ( unit=10, file='boundary.txt', access='sequential', form='formatted' )
do irow=1, ubound (small, 1)
read (10, '(5I4)') (small (irow, jcol), jcol=1, ubound (small, 2))
end do
write (*, '( / "small (1,2) =", I2, " and small (2,1)=", I2 )' ) small (1,2), small (2,1)
end program demo1
Using the I4 formatted read, the data need to be in columns:
12341234123412341234
11 12 13 14 15
21 22 23 24 25
The data file shouldn't contain the first row "1234..." -- that is in the example to make the alignment required for the format 5I4 clear.
With my example program, there is an outer do loop for irow and an "implied do loop" as part of the read statement. You could also eliminate the outer do loop and use two implied do loops on the read statement, as High Performance Mark showed. In this case, if you kept the format specification (5I4), it would get reused to read the second line -- this is called format reversion. (On a more complicated format, one needs to read the rules to understand which part of the format is reused in format reversion.) This is standard, and has been so at least since FORTRAN 77 and probably FORTRAN IV. (Of course, the declarations and style of my example are Fortran 90).
I used "ubound" so that you neither have to carry around variables storing the dimensions of the array, nor use specific numeric values. The later method can cause problems if you later decide to change the dimension of the array -- then you have to hunt down all of the specific values (here 2 and 5) and change them.
There is no need for a rewind after an open statement.