1. Preface
The goal of this Fortran tutorial is to give a quick introduction to the most common features of the Fortran 77 programming language. It is not a complete reference! Many details have been omitted. The presentation focuses on scientific computations, mainly linear algebra. The outline of this tutorial was inspired by the book “Handbook for Matrix Computations” by T. F. Coleman and C. Van Loan, published by SIAM.
2. What is Fortran?
Fortran is a general purpose programming language, mainly intended for mathematical computations in e.g. engineering. Fortran is an acronym for FORmula TRANslation, and was originally capitalized as FORTRAN. However, following the current trend to only capitalize the first letter in acronyms, we will call it Fortran. Fortran was the first ever high-level programming language. The work on Fortran started in the 1950’s at IBM and there have been many versions since. By convention, a Fortran version is denoted by the last two digits of the year the standard was proposed. Thus we have
-
Fortran 66
-
Fortran 77
-
Fortran 90 (95)
-
Fortran 2003
-
Fortran 2008
As of the mid-nineties, the most common Fortran version was still Fortran 77, although Fortran 90 was growing in popularity. Fortran 95 is a revised version of Fortran 90. There are also several versions of Fortran aimed at parallel computers. The most important one is High Performance Fortran (HPF), which is a de-facto standard.
Users should be aware that most Fortran 77 compilers allow a superset of Fortran 77, i.e. they allow non-standard extensions. In this tutorial we will emphasize standard ANSI Fortran 77.
2.1. Why learn Fortran?
Fortran is the dominant programming language used in engineering applications. It is therefore important for engineering graduates to be able to read and modify Fortran code. From time to time, so-called experts predict that Fortran will rapidly fade in popularity and soon become extinct. These predictions have always failed. Fortran is the most enduring computer programming language in history. One of the main reasons Fortran has survived and will survive is software inertia. Once a company has spent many man-years and perhaps millions of dollars on a software product, it is unlikely to try to translate the software to a different language. Reliable software translation is a very difficult task.
2.2. Portability
A major advantage Fortran has is that it is standardized by ANSI (American National Standards Institute) and ISO (International Standards Organization). Consequently, if your program is written in ANSI Fortran 77, using nothing outside the standard, then it will run on any computer that has a Fortran 77 compiler. Thus, Fortran programs are portable across machine platforms (for more details, check the Fortran Standards Documents).
3. Fortran 77 Basics
A Fortran program is just a sequence of lines of text. The text has to follow a certain structure to be a valid Fortran program. We start by looking at a simple example:
program circle
real r, area
! This program reads a real number r and prints
! the area of a circle with radius r.
write (*, *) 'Give radius r:'
read (*, *) r
area = 3.14159 * r * r
write (*, *) 'Area = ', area
stop
end
The lines that begin with a !
are comments and have no purpose other than to make the program more readable for humans. Originally, all Fortran programs had to be written in all upper-case letters. Most people now write lower-case since this is more legible, and so will we. You may wish to mix case, but Fortran is not case-sensitive, so X
and x
are the same variable.
3.1. Program organization
A Fortran program generally consists of a main program (or driver) and possibly several subprograms (procedures or subroutines). For now we will place all the statements in the main program; subprograms will be treated later. The structure of a main program is:
program name
! Declarations
! Statements
stop
end
The stop
statement is optional and may seem superfluous since the program will stop when it reaches the end anyway, but it is recommended to always terminate a program with the stop
statement to emphasize that the execution flow stops there.
You should note that you cannot have a variable with the same name as the program.
3.2. Column position rules
Fortran 77 is not a free-format language, but has a very strict set of rules for how the source code should be formatted. The most important rules are the column position rules:
Columns | Field description |
---|---|
1 |
Blank, or a |
1 … 5 |
Blanks, or statement label |
6 |
Blank, or continuation of previous line |
7 … 72 |
Statement |
73 … ∞ |
Ignored by the compiler |
Most lines in a Fortran 77 program start with 6 blanks and end before column 73, i.e. only the statement field is used.
3.3. Comments
A line that begins with the exclamation mark (!
) in the first column is a comment. Comments may appear anywhere in the program. Well-written comments are crucial to program readability. Commercial Fortran codes often contain about 50% comments.
3.4. Continuation
Sometimes, a statement does not fit into the 66 available columns of a single line. One can then break the statement into two or more lines, and use the continuation mark in position 6. Example:
!23456789 (This demonstrates column position!)
! The next statement goes over two physical lines
area = 3.14159265358979
+ * r * r
The continuation character can be the plus sign (+
) or an ampersand (&
).
3.5. Blank spaces
In the statement field, blank spaces are significant only as delimiters between identifiers and reserved words.
4. How to use Fortran on Ubuntu
4.1. Installing Fortran
To install Fortran 77 on a computer running Ubuntu or any other Debian-based Linux distribution, type at the command line terminal:
sudo apt-get update
sudo apt-get install fort77
4.2. Source code, object code, compiling, and linking
A Fortran program consists of plain text that follows certain rules (syntax). This is called the source code. You need to use an editor to write (edit) the source code. The most common editors in Unix/Linux are emacs
and vi
, but these can be a bit tricky for novice users. You may want to use a simpler GUI editor, like gedit
or pluma
.
When you have written a Fortran program, you should save it in a file that has the .f
extension. Before you can execute the program, you have to translate the program into machine readable form. This is done by a special program called a compiler. The Fortran 77 compiler is called f77
. The output from the compilation is given the somewhat cryptic name a.out
by default, but you can choose another name if you wish. To run the resulting program, simply type the name of the executable file prepending it with a dot slash (./
), for example: ./a.out
. (This explanation is a bit oversimplified. Really the compiler translates source code into object code and the linker/loader makes this into an executable.)
4.3. Examples
You can compile and run the circle.f
source file by following these steps:
f77 circle.f
./a.out
Note that there are several dots (periods) there which can be easy to miss! If you need to have several executables at the same time, it is a good idea to give the executables descriptive names. This can be accomplished using the -o
option. For example,
f77 circle.f -o circle.out
will compile the file circle.f
and save the executable in the file circle.out
. Please note that object codes and executables take a lot of disk space, so you should delete them when you are not using them. (The remove command in Unix/Linux is rm
.)
In the previous examples, we have not distinguished between compiling and linking. These are two different processes but the Fortran compiler performs them both, so the user usually does not need to know about it.
5. Variables, types, and declarations
5.1. Variable names
Variable names in Fortran consist of one ore more characters chosen from the letters a
… z
and the digits 0
… 9
. The first character must be a letter. Fortran 77 does not distinguish between upper and lower case.
The words which make up the Fortran language are called reserved words and cannot be used as variable names. Some of the reserved words which we have seen so far are: program
, real
, stop
and end
.
5.2. Types and declarations
Every variable should be defined in a declaration. This establishes the type of the variable. The valid declarations are:
|
|
|
|
The list of variables should consist of variable names separated by commas. Each variable should be declared exactly once. It’s an error to use an undeclared variable.
5.3. Integers and floating point variables
Fortran 77 has one type for integer variables. Integers are stored as 32 bits (4 bytes) variables. Therefore, all integer variables should take on values in the range [-m, m] where m is approximately 2×109.
Fortran 77 has one type for floating point variables, called real
. This type uses 4 bytes.
5.4. The parameter
statement
Some constants appear many times in a program. It is then often desirable to define them only once, in the beginning of the program. This is what the parameter
statement is for. It also makes programs more readable. For example, the circle area program should rather have been written like this:
circle2.f
program circle2
real r, area, pi
parameter (pi = 3.14159)
! This program reads a real number r and prints
! the area of a circle with radius r.
write (*, *) 'Give radius r:'
read (*, *) r
area = pi * r * r
write (*, *) 'Area = ', area
stop
end
The syntax of the parameter statement is:
parameter (name = constant)
The rules for the parameter statement are:
-
The name defined in the
parameter
statement is not a variable but rather a constant. (You cannot change its value at a later point in the program.) -
A name can appear in at most one parameter statement.
-
The parameter statement(s) must come before the first executable statement.
Some good reasons to use the parameter statement are:
-
It helps reduce the number of typos.
-
It makes it easier to change a constant that appears many times in a program.
-
It increases the readability of your program.
6. Expressions and assignment
6.1. Constants
The simplest form of an expression is a constant. There are four types of constants, corresponding to the four data types. Here are some integer constants:
1
0
-100
32767
15
Then we have real constants:
1.0
-0.25
2.0E6
3.333E-1
The E-notation means that you should multiply the constant by 10 raised to the power following the E
. Hence, 2.0E6
is two million, while 3.333E-1
is approximately one third.
The next type is logical constants. These can only have one of two values:
.true.
.false.
Note that the dots enclosing the letters are required.
The last type is character constants. These are most often used as an array of characters, called a string. These consist of an arbitrary sequence of characters enclosed in apostrophes (single quotes):
'ABC'
'Anything goes!'
'It is a nice day'
Strings and character constants are case sensitive. A problem arises if you want to have an apostrophe in the string itself. In this case, you should double the apostrophe:
'It''s a nice day'
6.2. Expressions
The simplest non-constant expressions are of the form:
operand operator operand
and an example is:
x + y
The result of an expression is itself an operand, hence we can nest expressions together like
x + 2 * y
This raises the question of precedence: Does the last expression mean x + (2 * y)
or (x + 2) * y
? The precedence of arithmetic operators in Fortran 77 are (from highest to lowest):
Operator | Description |
---|---|
|
Exponentiation |
|
Multiplication, division |
|
Addition, subtraction |
All these operators are calculated left-to-right, except the exponentiation operator **
, which has right-to-left precedence. If you want to change the default evaluation order, you can use parentheses.
The above operators are all binary operators. There is also the unary operator -
for negation, which takes precedence over the others. Hence an expression like -x+y
means what you would expect.
Extreme caution must be taken when using the division operator, which has a quite different meaning for integers and reals. If the operands are both integers, an integer division is performed, otherwise a real arithmetic division is performed. For example, 3/2
equals 1
, while 3.0/2.0
equals 1.5
(note the decimal points).
6.3. Assignment
The assignment has the form:
variableName = expression
The interpretation is as follows: Evaluate the right hand side and assign the resulting value to the variable on the left. The expression on the right may contain other variables, but these never change value! For example,
area = pi * r ** 2
does not change the value of pi
or r
, only area
.
6.4. Type conversion
When different data types occur in the same expression, type conversion has to take place, either explicitly or implicitly. Fortran will do some type conversion implicitly. For example:
real x
x = x + 1
will convert the integer one to the real number one, and has the desired effect of incrementing x by one. However, in more complicated expressions, it is good programming practice to force the necessary type conversions explicitly. For numbers, the following functions are available:
int()
real()
ichar()
char()
The first two have the obvious meaning. ichar
takes a character and converts it to an integer, while char
does exactly the opposite.
7. Logical expressions
Logical expressions can only have the value .true.
or .false.
. A logical expression can be formed by comparing arithmetic expressions using the following relational operators:
Operator | Description |
---|---|
|
Less than |
|
Less or equal |
|
Greater than |
|
Greater or equal |
|
Equal |
|
Not equal |
Logical expressions can be combined by the logical operators .and.
, .or.
, .not.
which have the obvious meaning.
7.1. Logical variables and assignment
Truth values can be stored in logical variables. The assignment is analogous to the arithmetic assignment. Example:
logical a, b
a = .true.
b = a .and. 3 .lt. 5/2
The order of precedence is important, as the last example shows. The rule is that arithmetic expressions are evaluated first, then relational operators, and finally logical operators. Hence b
will be assigned .false.
in the example above. Among the logical operators the precedence (in the absence of parenthesis) is that .not.
is done first, then .and.
, then .or.
is done last.
Logical variables are seldom used in Fortran. But logical expressions are frequently used in conditional statements like the if
statement.
8. The if
statement
An important part of any programming language are the conditional statements. The most common such statement in Fortran is the if
statement, which actually has several forms. The simplest one is the logical if
statement:
if (logicalExpression) statement
This has to be written on one line. This example finds the absolute value of x
:
if (x .lt. 0) x = -x
If more than one statement should be executed inside the if
, then the following syntax should be used:
if (logicalExpression) then
! Statements
endif
The most general form of the if
statement has the following form:
if (logicalExpression) then
! Statements
else if (logicalExpression) then
! Statements
! More "else if" clauses
! ...
else
! Statements
endif
The execution flow is from top to bottom. The conditional expressions are evaluated in sequence until one is found to be true. Then the associated statements are executed and the control resumes after the endif
.
8.1. Nested if
statements
if
statements can be nested in several levels. To ensure readability, it is important to use proper indentation. Here is an example:
if (x .gt. 0) then
if (x .ge. y) then
write(*, *) 'x is positive and x >= y'
else
write(*, *) 'x is positive but x < y'
endif
endif
You should avoid nesting many levels of if
statements since things get hard to follow.
9. Loops
For repeated execution of similar things, loops are used. If you are familiar with other programming languages you have probably heard about for-loops, while-loops, and until-loops. Fortran 77 has only one loop construct, called the do-loop. The do-loop corresponds to what is known as a for-loop in other languages. Other loop constructs have to be built using the if
and goto
statements.
9.1. do-loops
The do-loop is used for simple counting. Here is a simple example that prints the cumulative sums of the integers from 1 through n
(assume n
has been assigned a value elsewhere):
integer i, n, s
s = 0
do 10 i = 1, n
s = s + i
write(*, *) 'i =', i
write(*, *) 's =', s
10 continue
The number 10
is a statement label. Typically, there will be many loops and other statements in a single program that require a statement label. The programmer is responsible for assigning a unique number to each label in the main program and in each subprogram (function or subroutine). Recall that column positions 1-5 are reserved for statement labels. The numerical value of statement labels have no significance, so any integers (from 0
to 99999
) can be used, in any order. Typically, most programmers use consecutive multiples of 10.
The variable defined in the do-statement is incremented by 1 by default. However, you can define the step to be any number but zero. This program segment prints the even numbers between 1 and 10 in decreasing order:
integer i
do 20 i = 10, 1, -2
write(*, *) 'i =', i
20 continue
The general form of the do-loop is as follows:
do label var = expr1, expr2, expr3
! Statements
label continue
var is the loop variable (often called the loop index) which must be integer. expr1 specifies the initial value of var, expr2 is the terminating bound, and expr3 is the increment (step).
The do-loop variable must never be changed by other statements within the loop! This will cause great confusion. |
9.2. while-loops
In the C-family languages a while-loop looks like this:
while (logicalExpression) {
statements
}
The program will alternate testing the condition and executing the statements in the body as long as the condition in the while
statement is true. In Fortran 77 you must use if
and goto
to get the same behaviour:
label if (logicalExpression) then
! Statements
goto label
endif
Here is an example that calculates and prints all the powers of two that are less than or equal to 100:
integer n
n = 1
10 if (n .le. 100) then
write (*, *) n
n = 2*n
goto 10
endif
9.3. do-while-loops
If the termination criterion is at the end instead of the beginning, it is often called an do-while-loop. In the C-family languages it looks like this:
do {
statements
} while (logicalExpression);
Again, this should be implemented in Fortran 77 by using if
and goto
:
label continue
! Statements
if (logicalExpression) goto label
10. Arrays
Many scientific computations use vectors and matrices. The data type Fortran uses for representing such objects is the array. A one-dimensional array corresponds to a vector, while a two-dimensional array corresponds to a matrix.
10.1. One-dimensional arrays
The simplest array is the one-dimensional array, which is just a sequence of elements stored consecutively in memory. For example, the declaration
real a(20)
declares a
as a real array of length 20. That is, a
consists of 20 real numbers stored contiguously in memory. The size of the array has to be specified as a positive integer constant or a name defined using a parameter
statement. Fortran arrays are indexed from 1 and up. Thus the first number in the array is denoted by a(1)
and the last by a(20)
.
The type of an array element can be any of the basic data types. Examples:
integer i(10)
logical aa(2)
character x(100)
Each element of an array can be thought of as a separate variable. You reference the ith element of array a
by a(i)
. Here is a code segment that stores the 10 first square numbers in the array sq
:
integer i, sq(10)
do 100 i = 1, 10
sq(i) = i ** 2
100 continue
A common bug in Fortran is that the program tries to access array elements that are out of bounds or undefined. This is the responsibility of the programmer, and the Fortran compiler will not detect any such bugs!
10.2. Two-dimensional arrays
Matrices are very important in linear algebra. Matrices are usually represented by two-dimensional arrays. For example, the declaration
real A(3, 5)
defines a two-dimensional array of 3 * 5 = 15 real numbers. It is useful to think of the first index as the row index, and the second as the column index. Hence we get the graphical picture:
(1, 1) |
(1, 2) |
(1, 3) |
(1, 4) |
(1, 5) |
(2, 1) |
(2, 2) |
(2, 3) |
(2, 4) |
(2, 5) |
(3, 1) |
(3, 2) |
(3, 3) |
(3, 4) |
(3, 5) |
It is quite common in Fortran to declare arrays that are larger than the matrix we want to store. (This is because Fortran does not have dynamic storage allocation.) This is perfectly legal. Example:
real a(3, 5)
integer i, j
!
! We will only use the upper 3 by 3 part of this array.
!
do 20 j = 1, 3
do 10 i = 1, 3
a(i, j) = real(i) / real(j)
10 continue
20 continue
You may assume that the unused portion of the matrix will be initialized with zeros.
11. Subprograms
When a program is more than a few hundred lines long, it gets hard to follow. Fortran codes that solve real engineering problems often have tens of thousands of lines. The only way to handle such big codes, is to use a modular approach and split the program into many separate smaller units called subprograms.
A subprogram is a (small) piece of code that solves a well defined subproblem. In a large program, one often has to solve the same subproblems with many different data. Instead of replicating code, these tasks should be solved by subprograms. The same subprogram can be invoked many times with different input data.
Fortran has two different types of subprograms, called functions and subroutines.
11.1. Functions
Fortran functions are quite similar to mathematical functions: They both take a set of input arguments (parameters) and return a value of some type. In the preceding discussion we talked about user defined subprograms. Fortran 77 also has some intrinsic (built-in) functions.
A simple example illustrates how to use a function:
x = cos(pi / 3.0)
Here cos
is the cosine function, so x
will be assigned the value 0.5 (if pi
has been correctly defined; Fortran 77 has no built-in constants). These are the Fortran 77 intrinsic functions:
Function | Description |
---|---|
|
Absolute value |
|
Minimum value |
|
Maximum value |
|
Square root |
|
Sine |
|
Cosine |
|
Tangent |
|
Arctangent |
|
Exponential (natural) |
|
Logarithm (natural) |
All these function take a real
parameter and return a real
result.
Now we turn to the user-written functions. Consider the following problem: A meteorologist has studied the precipitation levels in the Bay Area and has come up with a model r(m, t) where r is the amount of rain, m is the month, and t is a scalar parameter that depends on the location. Given the formula for r and the value of t, compute the annual rainfall.
The obvious way to solve the problem is to write a loop that runs over all the months and sums up the values of r. Since computing the value of r is an independent subproblem, it is convenient to implement it as a function. The following main program can be used:
program rain
real r, t, sum
integer m
read (*, *) t
sum = 0.0
do 10 m = 1, 12
sum = sum + r(m, t)
10 continue
write (*,*) 'Annual rainfall is', sum, 'inches'
stop
end
Note that we have declared r
to be real
just as we would a variable. In addition, the function r
has to be defined as a Fortran function that corresponds to what the meteorologist came up with:
real function r(m,t)
integer m
real t
r = 0.1 * t * (m ** 2 + 14 * m + 46)
if (r .lt. 0) r = 0.0
return
end
We see that the structure of a function closely resembles that of the main program. The main differences are:
-
Functions have a type. This type must also be declared in the calling program.
-
The return value should be stored in a variable with the same name as the function.
-
Functions are terminated by the
return
statement instead ofstop
.
To sum up, the general syntax of a Fortran 77 function is:
returnType function name(listOfParameters)
! Declarations
! Statements
return
end
The function has to be declared with the correct type in the calling program unit, it’s an error not to do so. The function is called by simply using the function name and listing the arguments in parenthesis.
Functions can be recursive.
11.2. Subroutines
A Fortran function can essentially only return one value. Often we want to return two or more values (or sometimes none!). For this purpose we use the subroutine
construct. The syntax is as follows:
subroutine name(listOfParameters)
! Declarations
! Statements
return
end
Note that subroutines have no type and consequently should not (cannot) be declared in the calling program unit. They are also invoked differently than functions, using the word call
before their names and arguments.
Subroutines can also be recursive.
We give an example of a very simple subroutine. The purpose of the subroutine is to swap two integers.
subroutine iswap(a, b)
integer a, b
! Local variables
integer tmp
tmp = a
a = b
b = tmp
return
end
Note that there are two blocks of variable declarations here. First, we declare the input/output parameters, i.e. the variables that are common to both the caller and the callee. Afterwards, we declare the local variables, i.e. the variables that can only be used within this subprogram. We can use the same variable names in different subprograms and the compiler will know that they are different variables that just happen to have the same names.
11.3. Call-by-reference
Fortran 77 uses the so-called call-by-reference paradigm. This means that instead of just passing the values of the function/subroutine arguments (call-by-value), the memory address of the arguments (pointers) are passed instead. A small example should show the difference:
program callex
integer m, n
m = 1
n = 2
call iswap(m, n)
write(*, *) m, n
stop
end
The output from this program is "2 1", just as one would expect. However, if Fortran 77 had been using call-by-value then the output would have been "1 2", i.e. the variables m
and n
were unchanged! The reason for this is that only the values of m
and n
had been copied to the subroutine iswap
, and even if a
and b
were swapped inside the subroutine the new values would not have been passed back to the main program.
In the above example, call-by-reference was exactly what we wanted. But you have to be careful about this when writing Fortran code, because it is easy to introduce undesired side effects. For example, sometimes it is tempting to use an input parameter in a subprogram as a local variable and change its value. Since the new value will then propagate back to the calling program with an unexpected value, you should never do this unless (like our iswap
subroutine) the change is part of the purpose of the subroutine.
We will come back to this issue in a later section on passing arrays as arguments.
11.4. Arrays in subprograms
Fortran subprogram calls are based on call-by-reference. This means that the calling parameters are not copied to the called subprogram, but rather that the addresses of the parameters are passed. This saves a lot of memory space when dealing with arrays. No extra storage is needed as the subroutine operates on the same memory locations as the calling (sub-)program. However, you as a programmer have to know about this and take it into account.
It is possible to declare local arrays in Fortran subprograms, but this feature is rarely used. Typically, all arrays are declared in the main program and then passed on to the subprograms as needed.
For example, a basic vector operation is the saxpy operation (single-precision alpha times x plus y). This calculates the expression:
y := alpha * x + y
where alpha is a scalar but x and y are vectors. Here is a simple subroutine for this:
subroutine saxpy(alpha, x, y)
real alpha, x(5), y(5)
! Saxpy: Compute y := alpha * x + y,
! where x and y are vectors of length 5.
! Local variables
integer i
do 10 i = 1, 5
y(i) = alpha * x(i) + y(i)
10 continue
return
end
12. Common blocks
Fortran 77 has no global variables, i.e. variables that are shared among several program units (subroutines). The only way to pass information between subroutines we have seen so far is to use the subroutine parameter list. Sometimes this is inconvenient, e.g., when many subroutines share a large set of parameters. In such cases one can use a common block. This is a way to specify that certain variables should be shared among certain subroutines. But in general, the use of common blocks should be minimized.
12.1. Example
Suppose you have two parameters alpha
and beta
that many of your subroutines need. The following example shows how it can be done using common blocks.
program main
! Some declarations
real alpha, beta
common /coeff/ alpha, beta
! Statements
stop
end
subroutine sub1(someParameters)
! Declarations of someParameters
real alpha, beta
common /coeff/ alpha, beta
! Statements
return
end
subroutine sub2(someParameters)
! Declarations of someParameters
real alpha, beta
common /coeff/ alpha, beta
! Statements
return
end
Here we define a common block with the name coeff
. The contents of the common block are the two variables alpha
and beta
. A common block can contain as many variables as you like. They do not need to all have the same type. Every subroutine that wants to use any of the variables in the common block has to declare the whole block.
Note that in this example we could easily have avoided common blocks by passing alpha
and beta
as arguments. A good rule is to try to avoid common blocks if possible. However, there are a few cases where there is no other solution.
12.2. Syntax
common /name/ listOfVariables
You should know that
-
The
common
statement should appear together with the variable declarations, before the executable statements. -
Different common blocks must have different names (just like variables).
-
A variable cannot belong to more than one common block.
-
The variables in a common block do not need to have the same names each place they occur (although it is a good idea to do so), but they must be listed in the same order and have the same type and size.
To illustrate this, look at the following continuation of our example:
subroutine sub3(someParameters)
! Declarations of someParameters
real a, b
common /coeff/ a, b
! Statements
return
end
This declaration is equivalent to the previous version that used alpha
and beta
. It is recommended that you always use the same variable names for the same common block to avoid confusion. Here is a dreadful example:
subroutine sub4(someParameters)
! Declarations of someParameters
real alpha, beta
common /coeff/ beta, alpha
! Statements
return
end
Now alpha
is the beta
from the main
program and vice versa. If you see something like this, it is probably a mistake. Such bugs are very hard to find.
12.3. Arrays in common blocks
Common blocks can include arrays, too. But again, this is not recommended. The major reason is flexibility. An example shows why this is such a bad idea. Suppose we have the following declarations in the main
program:
program main
integer nmax
parameter (nmax=20)
integer n
real A(nmax, nmax)
common /matrix/ A, n
This common block contains first all the elements of A
, then the integer n
. Now assume you want to use the matrix A
in some subroutines. Then you have to include the same declarations in all these subroutines, e.g.
subroutine sub1()
integer nmax
parameter (nmax=20)
integer n
real A(nmax, nmax)
common /matrix/ A, n
The value of nmax
has to be exactly the same as in the main
program. Recall that the size of a matrix has to be known at compile time, hence nmax
has to be defined in a parameter
statement.
This example shows there is usually nothing to gain by putting arrays in common blocks. Hence the preferred method in Fortran 77 is to pass arrays as arguments to subroutines (along with its size).
13. Data statements
The data
statement is another way to input data that are known at the time when the program is written. It is similar to the assignment statement and it must be placed after any variable declarations. The syntax is:
data listOfVariables/listOfValues/, ...
where the three dots means that this pattern can be repeated. Here is an example:
data m/10/, n/20/, x/2.5/, y/2.5/
We could also have written this:
data m, n/10, 20/, x, y/2 * 2.5/
We could have accomplished the same thing by the assignments:
m = 10
n = 20
x = 2.5
y = 2.5
The data
statement is more compact and therefore often more convenient. Notice especially the shorthand notation (using the *
operator) for assigning identical values repeatedly.
The data
statement is performed only once, right before the execution of the program starts. For this reason, the data
statement is mainly used in the main program and not in subroutines.
The data
statement can also be used to initialize arrays (vectors and matrices). Remember that arrays are initialized by default with zeros. This example shows how to initialize a matrix with all ones when the program starts:
real A(10,20)
data A/200 * 1.0/
You may even initialize individual elements:
data A(1, 1)/12.5/, A(2, 1)/-33.3/, A(2, 2)/1.0/
Or you can list all the elements for small arrays like this:
integer v(5)
real B(3, 2)
data v/10, 20, 30, 40, 50/, B/1.0, -3.7, 4.3, 0.0, 5.1, -2.9/
The values for two-dimensional arrays will be assigned in column-first order. This means that the contents of array B
declared above would be:
1.0 |
0.0 |
-3.7 |
5.1 |
4.3 |
-2.9 |
The data
statement can be used for variables contained in a common block, but only in one (sub-)program, otherwise you get an overlapping initialization error.
14. Simple I/O
An important part of any computer program is to handle input and output. In our examples so far, we have already used the two most common Fortran constructs for this: read
and write
. Fortran I/O can be quite complicated, so we will only describe some simpler cases in this tutorial.
14.1. read
and write
The read
statement is used for input, while the write
statement is used for output. To simplify these statements we use asterisks (*) for the arguments, like we have done in most of our examples so far. This is sometimes called list directed read/write.
read(*, *) listOfVariables
write(*, *) listOfVariables
The first statement will read values from the standard input and assign the values to the variables in the variable list, while the second one writes to the standard output.
14.2. Examples
Here is a code segment from a Fortran program:
integer m, n
real x, y, z(10)
read(*, *) m, n
read(*, *) x, y
read(*, *) z
We give the input through standard input (possibly through a data file redirected to the standard input). A data file consists of records according to traditional Fortran terminology. In our example, each record contains a number (either integer or real). Records are separated by blanks. Hence a legal input to the program above would be:
-1 100 -1.0 1e+2 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
Note that Fortran 77 input is line sensitive, so it is important not to have extra input elements (fields) on a line (record). For example, if we gave the first four inputs all on one line as:
-1 100 -1.0 1e+2 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
this produces a runtime error.
If there are too few inputs on a line then the next line will be read. For example:
-1 100 -1.0 1e+2 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
This would produce the same results as the first two examples.
Just like with the data
statement, the values for two-dimensional arrays will be read in column-first order. So if we have the following code that declares and reads an array with two rows and four columns:
integer A(2, 4)
read(*, *) A
stop
end
and the input is:
1 2 3 4 5 6 7 8
then, the contents of the array A
would be:
1 |
3 |
5 |
7 |
2 |
4 |
6 |
8 |
In a similar vein, column-first order is also used when printing the contents of two-dimensional arrays using the write
statement. So, following the previous example, this statement:
write(*, *) A
displays this in the standard output:
1 2 3 4 5 6 7 8
Notice the consistency between how the input is read and how the output is written.
15. Fortran programming style
There are many different programming styles, but here are some general guidelines that are fairly non-controversial.
15.1. Portability
To ensure portability, use only standard Fortran 77. The only exception we have allowed in this tutorial is to use lower case letters.
15.2. Program structure
The overall program structure should be modular. Each subprogram should solve a well-defined task.
15.3. Comments
Write legible code, but also add comments in the source explaining what is happening! It is especially important to have a good header for each subprogram that explains each input/output argument and what the subprogram does.
15.4. Indentation
Always use proper indentation for loops and if
blocks as demonstrated in this tutorial.
15.5. Subprograms
Never let functions have “side effects”, i.e. do not change the value of the input parameters. Use subroutines in such cases.
In the declarations, separate parameters, common blocks, and local variables.
Minimize the use of common blocks.
15.6. Goto
Minimize the use of goto. Unfortunately it is necessary to use goto in some types of loops.
15.7. Arrays
In many cases it is best to declare all large arrays in the main program and then pass them as arguments to the various subroutines. This way all the space allocation is done in one place. Remember to pass the size of the array.
15.8. Efficiency concerns
When you have if
-then
-elseif
statements with multiple conditions, try to place the most likely conditions first.
16. Credits
This tutorial was designed by Erik Boman to be used in the course SCCM-001-F: Introduction to Fortran taught at Stanford University, Winter quarter 1996. It has been modified by Sarah T. Whitlock and Paul H. Hargrove for use in the Fortran courses which have been offered under different course numbers each subsequent year. The original source of the material is here: http://www.stanford.edu/class/me200c/tutorial_77/ Stanford university has re-released the material under a creative commons 3.0 attribution license.
The tutorial was transferred to AsciiDoctor format by Ariel Ortiz with some significant content modifications so that it could be used more conveniently in his Compiler Design course at the Tecnologico de Monterrey.