You... and the PPP

Patrick Melvin

2/28/06

Entry Essay For The PPP

Timmy. I am your father. My shibbness is directly responsible for your shibbiness, and therefore, I feel, as your father, I should be entitled to entry into the PPP.
 
im gunna agree with evan...hate me all u want but....evan is right

he isnt being negative in my mind....what does make them so much better.....
 
you know why you have to remember them?

Because they are all stocked in the PPP. Thats right, we stole them all.
 
it's true. you know the chocolate ones with the vanilla icing and rainbow spinkles? i claimed those ones..
 
we could cut your flow off anyday since we run both those too...but we won't...just yet...
 
was it, i was supposed to read it for a summer reading thing, but i only made it to like page 105. still managed to get a 90 on the test though.

goldfish are good, so are poptarts, and fruit roll ups. i never really ate gushers, but i think they must have been pretty good. i think its all about Animal Crackers and those Shark snacks(fruit flavored but so good). i want animal crackers now
 
My essay:

Professional Programmer's Guide to Fortran77 [Note: © Clive G. Page, 1988, 1995]

Clive G. Page, University of Leicester, UK

22nd February 1995

This file contains the full text of Professional Programmer's Guide to Fortran77 published by Pitman in 1988. Since the book went out of print in 1994, it seemed reasonable to make the text available free of charge over the Internet. Fortran77 is, of course, technically obsolete, since the ISO Standard for Fortran90 has replaced it. Many programmers are likely to continue using Fortran77, however, until Fortran90 compilers become much more widely available.

I am retaining all rights to this text, except that it may be copied and reproduced without fee, provided that the attribution to the author is preserved.

This file is written in LaTeX and is called prof77.tex: it is substantially the same as the published version but the opportunity has been taken to correct a few mistakes and make some minor updates.

In order to keep the price down, the book was deliberately kept rather shorter than the average Fortran textbook, but it still covered the entire Fortran77 language as defined in the ANSI and ISO Standards. I also managed to include several topics which are often omitted from much larger textbooks because they are deemed to be too "advanced".

I also wanted to encourage the writing of clear, reliable, portable, robust, and well structured code, so short sections appear throughout the book offering guidance on the practical use of Fortran. Various obsolete or superfluous features of the language, mainly those which have been retained for compatibility with earlier versions of Fortran, are omitted from the main text but are covered in the section 13. This is provided solely for the assistance of those who inherit elderly software.

\tableofcontents

--------------------------------------------------------------------------------

What Is Fortran?

Fortran is the most widely used programming language in the world for numerical applications. It has achieved this position partly by being on the scene earlier than any of the other major languages and partly because it seems gradually to have evolved the features which its users, especially scientists and engineers, found most useful. In order to retain compatibility with old programs, Fortran has advanced mainly by adding new features rather than by removing old ones. The net result is, of course, that some parts of the language are, by present standards, rather archaic: some of these can be avoided easily, others can still be a nuisance.

This section gives a brief history of the language, outlines its future prospects, and summarises its strengths and weaknesses.

Early Development

Fortran was invented by a team of programmers working for IBM in the early nineteen-fifties. This group, led by John Backus, produced the first compiler, for an IBM 704 computer, in 1957. They used the name Fortran because one of their principal aims was "formula translation". But Fortran was in fact one of the very first high-level language: it came complete with control structures and facilities for input/output. Fortran became popular quite rapidly and compilers were soon produced for other IBM machines. Before long other manufacturers were forced to design Fortran compilers for their own hardware. By 1963 all the major manufacturers had joined in and there were dozens of different Fortran compilers in existence, many of them rather more powerful than the original.

All this resulted in a chaos of incompatible dialects. Some order was restored in 1966 when an American national standard was defined for Fortran. This was the first time that a standard had ever been produced for a computer programming language. Although it was very valuable, it hardly checked the growth of the language. Quite deliberately the Fortran66 standard only specified a set of language features which had to be present: it did not prevent other features being added. As time went on these extensions proliferated and the need for a further standardization exercise became apparent. This eventually resulted in the current version of the language: Fortran77.

Standardization

One of the most important features of Fortran programs is their portability, that is the ease with which they can be moved from one computer system to another. Now that each generation of hardware succeeds the previous one every few years, while good software often lasts for much longer, more and more programs need to be portable. The growth in computer networks is also encouraging the development of portable programs.

The first step in achieving portability is to ensure that a standard form of programming language is acceptable everywhere. This need is now widely recognised and has resulted in the development of standards for all the major programming languages. In practice, however, many of the new standards have been ignored and standard-conforming systems for languages like Basic and Pascal are still very rare.

Fortunately Fortran is in much better shape: almost all current Fortran systems are designed to conform to the standard usually called Fortran77. This was produced in 1977 by a committee of the American National Standards Institute (ANSI) and was subsequently adopted by the International Standards Organisation (ISO). The definition was published as ANSI X3.9-1978 and ISO 1539-1980. The term "Standard Fortran" will be used in the rest of this book to refer to mean Fortran77 according to this definition.

Fortran is now one of the most widely used computer languages in the world with compilers available for almost every type of computer on the market. Since Fortran77 is quite good at handling character strings as well as numbers and also has powerful file-handling and input/output facilities, it is suitable for a much wider range of applications than before.

Full and Subset Fortran

The ANSI Standard actually defines two different levels for Fortran77. The simpler form, subset Fortran, was intended for use on computers which were too small to handle the full language. Now that even personal computers are powerful enough to handle full Fortran77, subset Fortran is practically obsolete. This book, therefore, only describes full Fortran77.

Fortran90 Fortran90

The ISO Standard for Fortran90 has, officially, replaced that for Fortran77. It introduces a wealth of new features many of them already in use in other high-level languages, which will make programming easier, and facilitate the construction of portable and robust programs. The whole of the Fortran77 Standard is included as a proper subset, so existing (standard-conforming) Fortran programs will automatically conform also to the new Standard. Until well-tested compilers for Fortran90 are widespread, however, most programmers are still using Fortran77, with perhaps a few minor extensions.

Strengths and Weaknesses

Fortran has become popular and widespread because of its unique combination of properties. Its numerical and input/output facilities are almost unrivalled while those for logic and character handling are as good as most other languages. Fortran is simple enough that you do not need to be a computer specialist to become familiar with it fairly quickly, yet it has features, such as the independent compilation of program units, which allow it to be used on very large applications. Programs written in Fortran are also more portable than those in other major languages. The efficiency of compiled code also tends to be quite high because the language is straight-forward to compile and techniques for handling Fortran have reached a considerable degree of refinement. Finally, the ease with which existing procedures can be incorporated into new software makes it especially easy to develop new programs out of old ones.

It cannot be denied, however, that Fortran has more than its fair share of weaknesses and drawbacks. Many of these have existed in Fortran since it was first invented and ought to have been eliminated long ago: examples include the 6-character limit on symbolic names, the fixed statement layout, and the need to use statement labels.

Fortran also has rather liberal rules and an extensive system of default values: while this reduces programming effort it also makes it harder for the system to detect the programmer's mistakes. In many other programming languages, for example, the data type of every variable has to be declared in advance. Fortran does not insist on this but, in consequence, if you make a spelling mistake in a variable name the compiler is likely to use two variables when you only intended to use one. Such errors can be serious but are not always easy to detect.

Fortran also lacks various control and data structures which simplify programming languages with a more modern design. These limitations, and others, are all eliminated with the advent of Fortran90.

Precautions

Extensions Extensions and Portability

Computer manufacturers have a natural tendency to compete with each other by providing Fortran systems which are "better" than before, usually by providing extensions to the language. This does not conflict with the Fortran Standard, provided that standard-conforming programs are still processed correctly. Indeed in the long term languages advance by the absorbtion of such extensions. In the short term, however, their use is more problematical, since they necessarily makes programs less portable.

When the latest Fortran Standard was issued in 1977 there was fairly widespread disappointment that it did not go just a little further in eliminating some of the tiresome restrictions that had persisted since the early days. The US Department of Defense issued a short list of extensions which manufacturers were encouraged to add to their Fortran77 systems. The most important of these were the following:

the END DO statement END DO statement

the DO WHILE loop DO WHILE loops

the INCLUDE statement INCLUDE statement

the IMPLICIT NONE facility IMPLICIT NONE statement

intrinsic functions for bit-wise operations on integers.

Many Fortran systems, especially those produced in the United States, now support these extensions but they are by no means universal and should not be used in portable programs.

One of the most irksome restrictions of Fortran77 is that symbolic names cannot be more than six characters long. This forces programmers to devise all manner of contractions, abbreviations, and acronyms in place of meaningful symbolic names. It is very tempting to take advantage of systems which relax this rule but this can have serious repercussions. Consider a program which makes use of variables called TEMPERATURE and TEMPERED. Many compilers will be quite happy with these, though a few will reject both names on grounds of length. Unfortunately there are also one or two compilers in existence which will simply ignore all letters after the sixth so that both names will be taken as references to the same variable, TEMPER. Such behaviour, while deplorable, is quite in accordance with the Standard which only requires systems to compile programs correctly if they conform to its rules.

The only way to be certain of avoiding problems like this is to ignore such temptations entirely and just use Standard Fortran. Many compilers provide a switch or option which can be set to cause all non-standard syntax to be flagged. Everything covered in this book is part of Standard Fortran unless clearly marked to the contrary.

Guidelines

Computer programming always requires a very high standard of care and accuracy if it is to be successful. This is even more vital when using Fortran than with some other languages, because, as explained above, the liberal rules of Fortran make it harder for the system to detect mistakes. To program successfully it is not enough just to conform to the rules of the language, it is also important to defend yourself against known pitfalls.

There is a useful lesson to be learned from the failure of one of the earliest planetary probes launched by NASA. The cause of the failure was eventually traced to a statement in its control software similar to this:

+ DO 15 I = 1.100+

when what should have been written was:

+ DO 15 I = 1,100+

but somehow a dot had replaced the comma. Because Fortran ignores spaces, this was seen by the compiler as:

+ DO15I = 1.100+

which is a perfectly valid assignment to a variable called DO15I and not at all what was intended.

Fortran77 permits an additional comma to be inserted after the label in a DO statement, so it could now be written as:

+ DO 15,I = 1,100+

which has the great advantage that it is no longer as vulnerable to a single-point failure.

There are many hazards of this sort in Fortran, but the risk of falling victim to them can be minimised by adopting the programming practices of more experienced users. To help you, various recommendations and guidelines are given throughout this book. Some of the most outdated and unsatisfactory features of Fortran are not described in the main part of the book at all but have been relegated to section 13.

There is not room in a book of this size to go further into the techniques of program design and software engineering. As far as possible everything recommended here is consistent with the methods of modular design and structured programming, but you should study these topics in more detail before embarking on any large-scale programming projects.

--------------------------------------------------------------------------------

Basic Fortran Concepts

This section presents some of the basic ideas of Fortran by showing some complete examples. In the interests of simplicity, the problems which these solve are hardly beyond the range of a good pocket calculator, and the programs shown here do not include various refinements that would usually be present in professional software. They are, however, complete working programs which you can try out for yourself if you have access to a Fortran system. If not, it is still worth reading through them to see how the basic elements of Fortran can be put together into complete programs.

Statements

To start with, here is one of the simplest program that can be devised:

PROGRAM TINY

WRITE(UNIT=*, FMT=*) 'Hello, world'

END

As you can probably guess, all this program does is to send a rather trite message "Hello, world" to your terminal. Even so its layout and structure deserve some explanation.

The program consists of three lines, each containing one statement. Each Fortran statement must have a line to itself (or more than one line if necessary), but the first six character positions on each line are reserved for statement labels and continuation markers. Since the statements in this example need neither of these features, the first six columns of each line have been left blank.

The PROGRAM statement gives a name to the program unit and declares that it is a main program unit. Other types of program unit will be covered later on. The program can be called anything you like provided the name conforms to the Fortran rules; the first character of a Fortran symbolic name must be a letter but, unfortunately, they cannot be more than six characters long in total. It is generally sensible to give the same name to the program and to the file which holds the Fortran source code (the original text).

The WRITE statement produces output: the parentheses enclose a list of control items which determine where and in what form the output appears. ?UNIT=*? selects the standard output file which is normally your own terminal; ?FMT=*? selects a default output layout (technically known as list-directed format). Asterisks are used here, as in many places in Fortran, to select a default or standard option. This program could, in fact, have been made slightly shorter by using an abbreviated form of the WRITE statements:

WRITE(*,*) 'Hello, world'

Although the keywords UNIT= and FMT= are optional, they help to make the program more readable. The items in the control list, like those in all lists in Fortran, are separated by commas.

The control information in the WRITE statement is followed by a list of the data items to be output: here there is just one item, a character constant which is enclosed in a pair of apostrophe (single quote) characters.

An END statement is required at the end of every program unit. When the program is compiled (translated into machine code) it tells the compiler that the program unit is complete; when encountered at run-time the END statement stops the program running and returns control to the operating system.

The Standard Fortran character set does not contain any lower-case letters so statements generally have to be written all in upper case. But Fortran programs can process as data any characters supported by the machine; character constants (such as the message in the last example) are not subject to this constraint.

Expressions and Assignments

The next example solves a somewhat more realistic problem: it computes the repayments on a fixed-term loan (such as a home mortgage loan). The fixed payments cover the interest and repay part of the capital sum; the annual payment can be calculated by the following formula:

payment = rate.amount / (1 - (1+rate)-nyears)

In this formula, rate is the annual interest rate expressed as a fraction; since it is more conventional to quote interest rates as a percentage the program does this conversion for us.

PROGRAM LOAN

WRITE(UNIT=*, FMT=*)'Enter amount, % rate, years'

READ(UNIT=*, FMT=*) AMOUNT, PCRATE, NYEARS

RATE = PCRATE / 100.0

REPAY = RATE * AMOUNT / (1.0 - (1.0+RATE)**(-NYEARS))

WRITE(UNIT=*, FMT=*)'Annual repayments are ', REPAY

END

This example introduces two new forms of statement: the READ and assignment statements, both of which can be used to assign new values to variables.

The READ statement has a similar form to WRITE: here it reads in three numbers entered on the terminal in response to the prompt and assigns their values to the three named variables. FMT=* again selects list-directed (or free-format) input which allows the numbers to be given in any convenient form: they can be separated by spaces or commas or even given one on each line.

The fourth statement is an assignment statement which divides PCRATE by 100 and assigns the result to another variable called RATE. The next assignment statement evaluates the loan repayment formula and assigns the result to a variable called REPAY.

Several arithmetic operators are used in these expressions: as in most programming languages " /'' represents division and " *'' represents multiplication; in Fortran " **'' is used for exponentiation, i.e. raising one number to the power of another. Note that two operators cannot appear in succession as this could be ambiguous, so that instead of " **-N'' the form " **(-N)'' has to be used.

Another general point concerning program layout: spaces (blanks) are not significant in Fortran statements so they can be inserted freely to improve the legibility of the program.

When the program is run, the terminal dialogue will look something like this:

Enter amount, % rate, years

20000, 9.5, 15

Annual repayments are 2554.873

The answer given by your system may not be exactly the same as this because the number of digits provided by list-directed formatting depends on the accuracy of the arithmetic, which varies from one computer to another.

Integer and Real Data Types

The LOAN program would have been more complicated if it had not taken advantage of some implicit rules of Fortran concerning data types: this requires a little more explanation.

Computers can store numbers in several different ways: the most common numerical data types are those called integer and real. Integer variables store numbers exactly and are mainly used to count discrete objects. Real variables are useful many other circumstances as they store numbers using a floating-point representation which can handle numbers with a fractional part as well as whole numbers. The disadvantage of the real data type is that floating-point numbers are not stored exactly: typically only the first six or seven decimal digits will be correct. It is important to select the correct type for every data item in the program. In the last example, the number of years was an integer, but all of the other variables were of real type.

The data type of a constant is always evident from its form: character constants, for example, are enclosed in a pair of apostrophes. In numerical constants the presence of a decimal point indicates that they are real and not integer constants: this is why the value one was represented as " 1.0" and not just " 1".

There are several ways to specify the data type of a variable. One is to use explicit type statements at the beginning of the program. For example, the previous program could have begun like this:

PROGRAM LOAN

INTEGER NYEARS

REAL AMOUNT, PCRATE, RATE, REPAY

Although many programming languages require declarations of this sort for every symbolic name used in the program, Fortran does not. Depending on your point of view, this makes Fortran programs easier to write, or allows Fortran programmers to become lazy. The reason that these declarations can often be omitted in Fortran is that, in the absence of an explicit declaration, the data type of any item is determined by the first letter of its name. The general rule is:

initial letters I-N integer type

initial letters A-H and O-Z real type.

In the preceding program, because the period of the loan was called NYEARS (and not simply YEARS) it automatically became an integer, while all the other variables were of real type.

DO Loops

Although the annual repayments on a home loan are usually fixed, the outstanding balance does not decline linearly with time. The next program demonstrates this with the aid of a DO-loop.

PROGRAM REDUCE

WRITE(UNIT=*, FMT=*)'Enter amount, % rate, years'

READ(UNIT=*, FMT=*) AMOUNT, PCRATE, NYEARS

RATE = PCRATE / 100.0

REPAY = RATE * AMOUNT / (1.0 - (1.0+RATE)**(-NYEARS))

WRITE(UNIT=*, FMT=*)'Annual repayments are ', REPAY

WRITE(UNIT=*, FMT=*)'End of Year Balance'

DO 15,IYEAR = 1,NYEARS

AMOUNT = AMOUNT + (AMOUNT * RATE) - REPAY

WRITE(UNIT=*, FMT=*) IYEAR, AMOUNT

15 CONTINUE

END

The first part of the program is similar to the earlier one. It continues with another WRITE statement which produces headings for the two columns of output which will be produced later on.

The DO statement then defines the start of a loop: the statements in the loop are executed repeatedly with the loop-control variable IYEAR taking successive values from 1 to NYEARS. The first statement in the loop updates the value of AMOUNT by adding the annual interest to it and subtracting the actual repayment. This results in AMOUNT storing the amount of the loan still owing at the end of the year. The next statement outputs the year number and the latest value of AMOUNT. After this there is a CONTINUE statement which actually does nothing but act as a place-marker. The loop ends at the CONTINUE statement because it is attached to the label, 15, that was specified in the DO statement at the start of the loop.

The active statements in the loop have been indented a little to the right of those outside it: this is not required but is very common practice among Fortran programmers because it makes the structure of the program more conspicuous.

The program REDUCE produces a table of values which, while mathematically correct, is not very easy to read:

Enter amount, % rate, years

2000, 9.5, 5

Annual repayments are 520.8728

End of Year Balance

1 1669.127

2 1306.822

3 910.0968

4 475.6832

5 2.9800416E-04

Formatted Output

The table of values would have a better appearance if the decimal points were properly aligned and if there were only two digits after them. The last figure in the table is actually less than a thirtieth of a penny, which is effectively zero to within the accuracy of the machine. A better layout can be produced easily enough by using an explicit format specification instead of the list-directed output used up to now. To do this, the last WRITE statement in the program should be replaced with one like this:

+ WRITE(UNIT=*, FMT='(1X,I9,F11.2)') IYEAR, AMOUNT+

The amended program will then produce a neater tabulation:

Enter amount, % rate, years

2000, 9.5, 5

Annual repayments are 520.8728

End of Year Balance

1 1669.13

2 1306.82

3 910.10

4 475.68

5 .00

The format specification has to be enclosed in parentheses and, as it is actually a character constant, in a pair of apostrophes as well. The first item in the format list, 1X, is needed to cope with the carriage-control convention: it provides an additional blank at the start of each line which is later removed by the Fortran system. There is no logical explanation for this: it is there for compatibility with very early Fortran system. The remaining items specify the layout of each number: I10 specifies that the first number, an integer, should be occupy a field 10 columns wide; similarly F11.2 puts the second number, a real (floating-point) value, into a field 11 characters wide with exactly 2 digits after the decimal point. Numbers are always right-justified in each field. The field widths in this example have been chosen so that the columns of figures line up satisfactorily with the headings.

Functions

Fortran provides a useful selection of intrinsic functions to carry out various mathematical operations such as square root, maximum and minimum, sine, cosine, etc., as well as various data type conversions. You can also write your own functions. The next example, which computes the area of a triangle, shows both forms of function in action.

The formulae for the area of a triangle with sides of length a, b, and c is: s = (a + b + c)/2 area = sqrt()[s.(s-a).(s-b).(s-c)]

PROGRAM TRIANG

WRITE(UNIT=*,FMT=*)'Enter lengths of three sides:'

READ(UNIT=*,FMT=*) SIDEA, SIDEB, SIDEC

WRITE(UNIT=*,FMT=*)'Area is ', AREA3(SIDEA,SIDEB,SIDEC)

END

FUNCTION AREA3(A, B, C)

*Computes the area of a triangle from lengths of sides

S = (A + B + C)/2.0

AREA3 = SQRT(S * (S-A) * (S-B) * (S-C))

END

This program consists of two program units. The first is the main program, and it has as similar form to those seen earlier. The only novel feature is that the list of items output by the WRITE statement includes a call to a function called AREA3. This computes the area of the triangle. It is an external function which is specified by means of a separate program unit technically known as a function subprogram.

The external function starts with a FUNCTION statement which names the function and specifies its set of dummy arguments. This function has three dummy arguments called A, B, and C. The values of the actual arguments, SIDEA, SIDEB, and SIDEC, are transferred to the corresponding dummy arguments when the function is called. Variable names used in the external function have no connection with those of the main program: the actual and dummy argument values are connected only by their relative position in each list. Thus SIDEA transfers its value to A, and so on. The name of the function can be used as a variable within the subprogram unit; this variable must be assigned a value before the function returns control, as this is the value returned to the calling program.

Within the function the dummy arguments can also be used as variables. The first assignment statement computes the sum, divides it by two, and assigns it to a local variable, S; the second assignment statement uses the intrinsic function SQRT which computes the square-root of its argument. The result is returned to the calling program by assigning it to the variable which has the same name as the function.

The END statement in a procedure does not cause the program to stop but just returns control to the calling program unit.

There is one other novelty: a comment line describing the action of the function. Any line of text can be inserted as a comment anywhere except after an END statement. Comment lines have an asterisk in the first column.

These two program units could be held on separate source files and even compiled separately. An additional stage, usually called linking, is needed to construct the complete executable program out of these separately compiled object modules. This seems an unnecessary overhead for such simple programs but, as described in the next section, it has advantages when building large programs.

In this very simple example it was not really necessary to separate the calculation from the input/output operations but in more complicated cases this is usually a sensible practice. For one thing it allows the same calculation to be executed anywhere else that it is required. For another, it reduces the complexity of the program by dividing the work up into small independent units which are easier to manage.

IF-blocks

Another important control structure in Fortran is the IF statement which allows a block of statements to be executed conditionally, or allows a choice to be made between different courses of action.

One obvious defect of the function AREA3 is that has no protection against incorrect input. Many sets of three real numbers could not possibly form the sides of a triangle, for example 1.0, 2.0, and 7.0. A little analysis shows that in all such impossible cases the argument of the square root function will be negative, which is illegal. Fortran systems should detect errors like this at run-time but will vary in their response. Even so, a message like "negative argument for square-root" may not be enough to suggest to the user what is wrong. The next version of the function is slightly more user-friendly:

REAL FUNCTION AREA3(A, B, C)

*Computes the area of a triangle from lengths of its sides.

*If arguments are invalid issues error message and returns zero.

REAL A, B, C

S = (A + B + C)/2.0

FACTOR = S * (S-A) * (S-B) * (S-C)

IF(FACTOR .LE. 0.0) THEN

WRITE(UNIT=*, FMT=*)'Impossible triangle', A, B, C

AREA3 = 0.0

ELSE

AREA3 = SQRT(FACTOR)

END IF

END

The IF statement works with the ELSE and END IF statements to enclose two blocks of code. The statements in the first block are only executed if the expression in the IF statement is true, those in the second block only if it is false. The statements in each block are indented for visibility, but this is, again, just a sensible programming practice.

With this modification, the value of FACTOR is tested and if it is negative or zero then an error message is produced; AREA3 is also set to an impossible value (zero) to flag the mistake. Note that the form " .LE.'' is used because the less-than-or-equals character, " A2 it returns (A1-A2), otherwise zero.

D = DPROD(R,R) Computes the double precision product of two real values.

R = AIMAG(X) Extracts the imaginary component of a complex number. Note that the real component can be obtained by using the REAL function.

X = CONJG(X) Computes the complex conjugate of a complex number.

The NINT and ANINT functions round upwards if the fractional part of the argument is 0.5 or more, whereas INT and AINT always round towards zero. Thus:

? INT(+3.5) = 3 NINT(+3.5) = 4 ?

? INT(-3.5) = -3 NINT(-3.5) = -4 ?

The fractional part of a floating point number, X, can easily be found either by:

? X - AINT(X)?

or

? MOD(X, 1.0)?

In either case, if X is negative the result will also be negative. The ABS function can always be used to alter the sign if required.

The MOD function has other uses. For example it can find the day of the week from an absolute day count such as Modified Julian Date (MJD):

? MOD(MJD,7)?

has a value between 0 and 6 for days from Wednesday to Tuesday. Similarly if you use the ATAN2 function but want the result to lie in the range 0 to 2 \pi (rather than -\pi to +2 \pi) then, assuming the value of TWOPI is suitably defined, the required expression is:

? MOD(ATAN2(X,Y) + TWOPI, TWOPI)?

Arithmetic Assignment Statements

Assignment statements An arithmetic assignment statement has the form:

? ? arithmetic-var = arithmetic-expression

where arithmetic-var can be an arithmetic variable or array element. For example, the following assignment statement is valid provided that N, K, and ANGLE are all defined values:

? IMAGE(N/2+1,3*K-1) = SIN(ANGLE)**2 + 1.0?

If the object on the left has a different data type from that of the expression on the right then a data type conversion is applied automatically. The type conversion function (INT, REAL, DBLE, or CMPLX) is selected to match the object on the left. Note that many type conversions lose information. If the object on the left is an array element, its subscripts can be arbitrary integer expressions, but all the operands in these expressions must be defined before the statement is executed and each must be in the range declared for the corresponding subscript of the array.

Remember with an integer item on the left and an expression of one of the floating-point types, the INT function is invoked: if the NINT function is really needed then it must be used explicitly to convert the value of the expression.

--------------------------------------------------------------------------------

Character Handling and Logic

Character handling This section describes the facilities for handling non-numerical data in Fortran. Character data are actually present in almost all programs, if only in the form of file names and error messages, but the facilities for character manipulation are now quite powerful. The logical data type is even more indispensable since a logical expression is used in every IF statement.

Character Facilities

The character data type differs from all the others in one important respect: every character item has a fixed length. This specifies the number of characters it holds.

The length of a literal character constant is just the number of characters between the enclosing apostrophes (except that two consecutive apostrophe within the string count as one). Thus:

? 'it''s' ?

is a character constant of length four. Because the length of every character variable, array, and function has to be specified in advance it is nearly always necessary to use CHARACTER statements to declare them, for example:

? CHARACTER NAME*20, ADDRSS(3)*40, ZIP*7?

The same applies to named character constants but for these a special notation sets the length to that of the attached constant, which saves the trouble of counting characters:

CHARACTER TITLE*(*)

PARAMETER (TITLE = 'Latest mailing list')

The fixed length of character objects makes it easy to output data in a fixed format as when printing a table with neatly aligned columns, but sometimes it would be more convenient to have a variable length string type as some other languages do. The rules for character assignment go some way towards this: if an expression is too short then blanks are appended to it; if it is too long then characters are removed from the right-hand end. For many purposes, therefore, it is only necessary to ensure that character variables are at least as long as the longest string you need to store in them.

When transferring character information to procedures the length of the dummy argument can be set automatically to that of the corresponding actual argument. With this passed length notation it is easy to write general-purpose character handling procedures. This is described further in section 9.5.

The most common operations carried out on character strings are splitting them up and joining them together. Any section of a character variable or array element can be extracted by using the substring notation. Strings (and substrings) can be joined end to end by using the concatenation operator in a character expression. These are described in the next two sections.

Another fairly common requirement is to search for a particular sequence of characters within a longer string: this can be done with the intrinsic function INDEX.

Other intrinsic functions ICHAR and CHAR are provided to convert a single character to an integer or vice-versa according to its position within the native character set. More complicated conversions from a numerical data type to character form and vice-versa are best carried out using the internal file READ and WRITE statements which allow the power of the format specification to applied to the task. This mechanism is described in section 10.3.

Character strings can be compared to each other using relational operators or intrinsic functions. The latter use the ASCII collating sequence irrespective of the native character code. Further details are given in section 7.6.

Character Substrings

Substrings, character The substring notation can be used to select any contiguous section of any character variable or array element. The characters in any string are numbered starting from one on the left: the lower bound cannot be altered as it can in arrays. A substring is selected simply by giving the first and last character positions of the extract. For example, with:

CHARACTER METAL*10

METAL = 'CADMIUM'

then METAL(1:3) has the value 'CAD' while METAL(8:8) has the value blank because the value is padded out with blanks to its declared length.

Substrings must be at least one character long. They can be used in general in the same ways as character variables. Continuing with the last example, the assignment statement:

? METAL(3:4) = 'ES'?

will change the value of METAL to 'CAESIUM ' (with three blanks at the end, since the total length stays at 10).

Substring Rules

The parentheses denoting a substring must contain a colon: there may be an integer expression on either side of the colon. The first expression denotes the initial character position, the second one the last character position. Both values must be within the range 1 to LEN, where LEN is the length of the parent string, and the length of the resulting substring must not be less than one.

Although the colon must always be present, the two integer expressions are optional. The default value for the first one is one, the default for the second is the position of the last character of the parent string. Thus, staying with the last example: METAL(:2) has the value 'CA' while METAL(7:) has the value 'M' with three blanks.

With array elements the substring expression follows the sub-script expression, for example:

CHARACTER PLAY(30)*80

PLAY(10) = 'AS YOU LIKE IT'

Then the substring PLAY(10)(4:11) has the value 'YOU LIKE'. Substrings can be used in expressions anywhere except in the definition of a statement function; they can also be used on the left-hand side of an assignment statement, and can be also be defined by input/output statements.

Character Expressions

Character expressions The character operator // is used to concatenate, or join, two character strings. It is, in fact, the only character operator that Fortran provides. Thus:

'CUP' // 'BOARD' \Longrightarrow \Longrightarrow 'CUPBOARD'

The length of the result is just the sum of the lengths of the operands. Parentheses may be used in character expressions but make no difference to the result. Note that any embedded or trailing blanks (spaces) will be reproduced exactly in the resulting string.

The general form of a character-expression is thus:

+ + character-operand

or+ + character-expression // character-operand

where character-operand can be any of the following:

character constant (literal or named),

character variable,

character array element,

character substring,

character function reference.

There is one special restriction on character concatenation in procedures: a passed-length dummy argument can only be an operand of the concatenation operator in an assignment statement. This seemingly arbitrary rule allows the compiler to determine how much work-space is required.

Character Assignment Statements

Assignment, character The character assignment statement has the general form:

+ + char-var = character-expression

where char-var can be a character variable, array element, or substring.

There is one important restriction on character assignment statements: none of the characters being referenced in the expression on the right may be defined in char-var on the left, that is to say there can be no overlap. Thus the assignment statement:

? STRING(1:N) = STRING(10:)?

is valid only as long as N is no higher than 9. It is, of course, easy to get around this restriction by using a temporary character variable with a suitable length.

Note when a value is assigned to a substring (as in the last example) the other characters in the parent string are not affected at all. If the string was previously undefined then the other character positions will still be undefined; otherwise they will retain their previous contents.

The expression and the character object to which its value is assigned may have different lengths: if the expression is longer then the excess characters on the right are lost; if it is shorter then blanks are appended. Care is needed to declare adequate lengths or else the results can be unexpected:

CHARACTER AUTHOR*30, SHORT*5, EXPAND*10

AUTHOR = 'SHAKESPEARE, WILLIAM'

SHORT = AUTHOR

EXPAND = SHORT

The resulting value of EXPAND will be ?'SHAKE '? where the last five characters are blanks.

Character Intrinsic Functions

Character functions The four main character intrinsic functions are described in this section. There another four functions provided to compare character strings with each other using the ASCII collating ASCII collating sequence sequence: these are described in section 7.6.

CHAR and ICHAR

These two functions perform integer to character conversion and vice-versa using the internal code of the machine. Although most computers now use the ASCII character code, it is by no means universal, so these functions can only be used in a very limited way in portable software.

CHAR(I) returns the character at position I in the code table. For example, on a machine using ASCII code, CHAR(74) = 'J', since " J" is the character number 74 in the ASCII code table.

ICHAR(STRING) returns the integer position in the code table of the first character of the argument STRING. For example, on a machine using ASCII code,

ICHAR('JOHN') \Longrightarrow 74

ICHAR('john') \Longrightarrow 106

INDEX

INDEX is a search function; it takes two character arguments and returns an integer result. INDEX(S1, S2) searches for the character-string S2 in another string S1, which is usually longer. If S2 is present in S1 the function returns the character position at which it finds starts. If there is no match (or S1 is shorter than S2) then it returns the value zero. For example:

CHARACTER*20 SPELL

SPELL = 'ABRACADABRA'

K = INDEX(SPELL, 'RA')

Here K will be set to 3 because this is the position of the first occurrence of the string 'RA'. To find the second occurrence it is necessary to restart the search at the next character in the main string, for example:

? L = INDEX(SPELL(K+1:), 'RA')?

This will return the value 7 because the first occurrence of 'RA' in the substring 'ACADABRA' is at position 7. To find its position in the parent string the offset, K, must be added, making 10.

The INDEX function is often useful when manipulating character information. Suppose, for example, we have an string NAME containing the a person's surname and initials, e.g.

? Mozart,W.A ?

The name can be reformatted to put the initials before the surname and omit the comma like this:

CHARACTER NAME*25, PERSON*25

*...

KCOMMA = INDEX(NAME, ',')

KSPACE = INDEX(NAME, ' ')

PERSON = NAME(KCOMMA+1:KSPACE-1) // NAME(1:KCOMMA-1)

Then PERSON will contain the string 'W.A.Mozart' (with blanks appended to the length of 25). Note that a separate variable, PERSON, was necessary because of the rule about overlapping strings in assignments.

LEN

The LEN function takes a character argument and returns its length as an integer. The argument may be a local character variable or array element but this will just return a constant. LEN is more useful in procedures where character dummy arguments (and character function names) may have their length passed over from the calling unit, so that the length may be different on each procedure call. The length returned by LEN is that declared for the item. Sometimes it is more useful to find the length excluding trailing blanks. The next function does just that, using LEN in the process.

INTEGER FUNCTION LENGTH(STRING)

*Returns length of string ignoring trailing blanks

CHARACTER*(*) STRING

DO 15, I = LEN(STRING), 1, -1

IF(STRING(I:I) .NE. ' ') GO TO 20

15 CONTINUE

20 LENGTH = I

END

Relational Expressions

Relational expressions Expressions, relational A relational expression compares the values of two arithmetic expressions or two character expressions: the result is a logical value, either true or false. Relational expressions are commonly used in IF statements, as in this example:

IF(SENSOR .GT. UPPER) THEN

CALL COOL

ELSE IF(SENSOR .LT. LOWER) THEN

CALL HEAT

END IF

The relational operators have forms such as .GT. and .LT. because the Fortran character set does not include the usual characters . and arg2 it returns (arg1 - arg2), otherwise zero.

D = DPROD(R,R) Computes the double precision product of two real values.

* = EXP(RDX) Returns the exponential, i.e. e to the power of the argument. This is the inverse of the natural logarithm.

I = ICHAR(C) Returns position of first character of the string in the local character code table.

I = INDEX(C,C) Searches first string and returns position of second string within it starting at 1, otherwise zero.

I = INT(IRDX) Converts to integer by truncation.

I = LEN(C) Returns length of the argument in characters.

L = LGE(C,C) Lexical comparison using ASCII collating sequence: returns true if arg1 >= arg2.

L = LGT(C,C) Lexical comparison using ASCII collating sequence: returns true if arg1 > arg2.

L = LLE(C,C) Lexical comparison using ASCII collating sequence: returns true if arg1 HTML by ltoh]

Russell W. Quong (quong@best.com.REMOVETHIS-SPAM-FILTER-PART)

Last modified: Feb 12 2001 (LaTeX doc modified: Nov 17 2003)
 
forgot my other half:

Arithmetic Comparisons

Comparisons, arithmetic When the two arithmetic values of differing data type are compared, a conversion is automatically applied to one of them (as in arithmetic expressions) to bring it to the type of the other. The direction of conversion is always:

integer \Longrightarrow \Longrightarrow real \Longrightarrow complex or double precision.

When comparing integer expressions, there is a considerable difference between the .LE. and .LT. operators, and similarly between .GE. and .GT., so that you should consider carefully what action is required in the limiting case before selecting the appropriate operator.

In comparisons involving the other arithmetic types you should remember that the value of a number may not be stored exactly. This means that it is unwise to rely on tests involving the .EQ. and .NE. operators except in special cases, for example if one of the values has previously been set to zero or some other small integer.

There are two restrictions on complex values: firstly they cannot be compared at all to ones of double precision type. Secondly they cannot use relational operators other than .EQ. and .NE. because there is no simple linear ordering of complex numbers.

Character comparisons

Comparisons, character A character value can only be compared to another character value; if they do not have the same length then the shorter one is padded out with blanks to the length of the other before the comparison takes place. Tests for equality (or inequality) do not depend on the character code, the two strings are just compared character by character until a difference is found. Comparisons using the other operators ( .GE.,

.GT., .LE., and .LT.) do, however, depend on the local character code. The two expressions are compared one character position at a time until a difference is found: the result then depends on the relative positions of the two characters in the local collating sequence, i.e. the order in which the characters appear in the character code table. Character collating sequence The Fortran Standard specifies that the collating sequence used by all systems must have the following basic properties:

all the upper-case letters are in order, A < B < C etc.

all digits are in order, 0 < 1 < 2 etc.

all digits precede all letters or vice-versa,

the blank (space) character precedes letters and digits.

It does not, however, specify whether letters precede digits or follow them. As a result, if strings of mixed text are sorted using relational operators the results may be machine dependent. For example, the expression

? 'APPLE' .LT. 'APRICOT'?

is always true because at the two strings first differ at the third character position, and the letter 'P' precedes 'R' in all Fortran collating sequences. However:

? 'A1' .GT. 'AONE'?

will have a value true if your system uses EBCDIC but false if it uses ASCII, because the digits follow letters in the former and precede them in the latter.

In order to allow character comparisons to be made in a truly portable way, Fortran has provided four additional intrinsic functions. These perform character comparisons using the ASCII collating sequence no matter what the native character code of the machine. These functions are:

LGE(S1, S2) greater than or equal to

LGT(S1, S2) greater than

LLE(S1, S2) less than or equal to

LLT(S1, S2) less than.

They take two character arguments (of any length) and return a logical value. Thus the expression:

? LGT('A1', 'AONE')?

will always have the value false.

Character comparisons are case-sensitive on machines which have lower-case letters in their character set. It is advisable to convert both arguments to the same case beforehand.

Guidelines

Systems which supports both upper and lower-case characters are usually case-sensitive: before testing for the presence of particular keywords or commands it is usually best to convert the an input string to a standard case, usually upper-case. Unfortunately there are no standard intrinsic functions to do this, though many systems provide them as an extension.

In character sorting operations where the strings contain mixtures of letters, digits, or other symbols, you should use the intrinsic functions to make the program portable. In other character comparisons, however, the relational operator notation is probably preferable because it has a more familiar form and may be slightly more efficient.

Logical Expressions

Logical expressions Expressions, logical Logical expressions can be used in logical assignment statements, but are most commonly encountered in IF statements where there is a compound condition, for example:

IF(AGE .GE. 60 .OR. (STATUS .EQ. 'WIDOW' .AND.

$ NCHILD .GT. 0) THEN

This combines the values of three relational expressions, two of them comparing arithmetic values, the other character values. The logical operators such as .AND. and .OR. also need decimal points at either end to distinguish them from symbolic names. The .OR. operator performs an inclusive or, the exclusive or operator is called .NEQV..

Rules

A logical expression can have any of the following forms:

logical-term

.NOT. logical-term

logical-expression logical-operator logical-term

Where: logical-term can be any of the following:

logical constant (literal or named),

logical variable,

logical array element,

logical function reference,

logical expression enclosed in parentheses,

relational expression.

and the logical operator can be any of the following: Operators, logical

.AND. logical and

.OR. logical inclusive or

.EQV. logical equivalence

.NEQV. logical non-equivalence (i.e. exclusive or).

Note that the rules of logical expressions only allow two successive operators to occur if the second of them is the unary operator .NOT. which negates the value of its operand. The effects of the four binary logical operators are shown in the table below for the four possible combinations of operands, x and y.

x y x .AND. y x .OR. y x .EQV. y x .NEQV. y

false false false false true false

true false false true false true

false true false true false true

true true true true true false

Note that a logical expression can have operands which are complete relational expressions, and these can in turn contain arithmetic expressions. The complete order of precedence of the operators in a general expression is as follows:

arithmetical operators (in the order defined in section 6.1 above).

relational operators

.NOT.

.AND.

.OR.

.EQV. and .NEQV.

If the operators .EQV. and .NEQV. are used at the same level in an expression they are evaluated from left to right.

These rules reduce the need for parentheses in logical expressions, thus:

? (X .GT. A) .OR. (Y .GT. B)?

would have exactly the same meaning if all the parentheses had been omitted.

A Fortran system is not required to evaluate every term in a logical expression completely if its value can be determined more simply. In the above example, if X had been greater than A then it would not be necessary to compare Y and B for the expression would have been true in either case. This improves efficiency but means that functions with side-effects should not be used.

Guidelines

Complicated logical and relational expressions can be hard to read especially if they extend on to several successive lines. It helps to line up similar conditions on successive lines, and to use parentheses.

Logical Assignment Statements

A logical assignment statement has the form:

logical-var = logical-expression

Where the logical-var can be a logical variable or array element. Logical variables and array elements are mainly used to store the values of relational expressions until some later point where they are used in IF statements.

--------------------------------------------------------------------------------

Control Statements

Executable statements are normally executed in sequence except as specified by control statements. The END= and ERR= keywords of input/output statements can also affect the execution sequence.

Control Structures

Branches

The best way to select alternative paths through a program is to use the block- IF structure: this may comprise a single block to be executed when a specified condition is true or several blocks to cover several eventualities. Where the IF-block would only contain one statement it is possible to use an abbreviated form called (for historical reasons) the logical-IF statement.

There is also a computed GO TO statement which can produce a multi-way branch similar to the "case" statements of other languages.

Loops

Loops Another fundamental requirement is that of repetition. If the number of cycles is known in advance then the DO statement should be used. This also controls a block of statements known as the DO-loop. A CONTINUE statement usually marks the end of a DO-loop.

Fortran has no direct equivalent of the "do while" and "repeat until" forms available in some program languages for loops of an indefinite number of iterations, but they can be constructed using simple GO TO and IF statements.

Other Control Statements

The STOP STOP statement statement can be used to terminate execution. Other statements which affect execution sequence are described in other sections: the END statement was covered in section 4.7; procedure calls including the CALL and RETURN statements are described in section 9. IF-blocks

IF-Blocks

The simplest form of IF-block looks like this:

IF(N .NE. 0) THEN

AVERAG = SUM / N

AVGSQ = SUMSQ / N

END IF

The statements in the block are only executed if the condition is true. In this example the statements in the block are not executed if N is zero in order to avoid division by zero.

The IF-block can also contain an ELSE statement to handle the alternative:

IF(B**2 .GE. 4.0 * A * C) THEN

WRITE(UNIT=*,FMT=*)'Real roots'

ELSE

WRITE(UNIT=*,FMT=*)'No real roots'

END IF

Since the IF statement contains a logical expression its value can only be true or false, thus one or other of these blocks will always be executed.

If there are several alternative conditions to be tested, they can be specified with ELSE IF statements: ELSE and ELSE IF statements

IF(OPTION .EQ. 'PRINT') THEN

CALL OUTPUT(ARRAY)

ELSE IF(OPTION .EQ. 'READ') THEN

CALL INPUT(ARRAY)

ELSE IF(OPTION .EQ. 'QUIT') THEN

CLOSE(UNIT=OUT)

STOP 'end of program'

ELSE

WRITE(UNIT=*,FMT=*)'Incorrect reply, try again...'

END IF

There can be any number of ELSE IF blocks but in each case one, and only one, will be executed each time. Without an ELSE block on the end an nothing would have happened when an invalid option was selected.

Block-IF General Rules

The general form of the block-if structure is as follows:

IF( logical-expression ) THEN

a block of statements

ELSE IF( logical-expression ) THEN

another block of statements

ELSE

a final block of statements

END IF

The IF THEN, ELSE IF, and ELSE statements each govern one block of statements. There can be any number of ELSE IF statements. The ELSE statement (together with its block) is also optional, and there can be at most one of these.

The first block of statements is executed only if the first expression is true. Each block after an ELSE IF is executed only if none of the preceding blocks have been executed and the attached ELSE IF expression is true. If there is an ELSE block it is executed only if none of the preceding blocks has been executed.

After a block has been executed control is transferred to the statement following the END IF statement at the end of the structure (unless the block ends with some statement which transfers control elsewhere).

Any block can contain a complete block-IF structure properly nested within it, or a complete DO-loop, or any other executable statements (except END).

It is illegal to transfer control into any block from outside it, but there is no restriction on transferring control out of a block.

The rules for logical expressions are covered in section 7.7.

Guidelines

The indentation scheme shown in the examples above is not mandatory but the practice of indenting each block by a few characters relative to the rest of the program is strongly recommended. It makes the structure of the block immediately apparent and reduces the risk of failing to match each IF with an END IF. An indenting scheme is especially useful when IF-blocks are nested within others. For example:

IF(POWER .GT. LIMIT) THEN

IF(.NOT. WARNED) THEN

CALL SET('WARNING')

WARNED = .TRUE.

ELSE

CALL SET('ALARM')

END IF

END IF

The limited width of the statement field can be a problem when IF-blocks are nested to a very great depth: but this tends to mean that the program unit is getting too complicated and that it will usually be beneficial to divide it into subroutines. If you accidentally omit an END IF statement the compiler will flag the error but will not know where you forgot to put it. In such cases the compiler may get confused and generate a large number of other error messages.

When an IF-block which is executed frequently contains a large number of ELSE IF statements it will be slightly more efficient to put the most-likely conditions near the top of the list as when they occur the tests lower down in the list will not need to be executed.

DO-Loops

DO-loops

The DO statement controls a block of statements which are executed repeatedly, once for each value of a variable called the loop-control variable. The number of iterations depends on the parameters of the DO statement at the heads of the loop. The first item after the keyword " DO" is the label which is attached to the last statement of the loop. For example:

*Sum the squares of the first N elements of the array X

SUM = 0.0

DO 15, I = 1,N

SUM = SUM + X(I)**2

15 CONTINUE

If we had wanted only to sum alternate elements of the array we could have used a statement like:

? DO 15,I = 1,N,2?

and then the value of I in successive loops would have been 1, 3, 5, etc. The final value would be N if N were odd, or only to N-1 if N were even. If the third parameter is omitted the step-size is one; if it is negative then the steps go downwards. For example

DO 100,I = 5,1,-1

WRITE(UNIT=*,FMT=*) I**2

100 CONTINUE

will produce 5 records containing the values 25, 16, 9, 4, and 1 respectively.

Loops can be nested to any reasonable depth. Thus the following statements will set the two dimensional array FIELD to zero.

REAL FIELD(NX, NY)

DO 50, IY = 1,NY

DO 40, IX = 1.NX

FIELD(IX,IY) = 0.0

40 CONTINUE

50 CONTINUE

General Form of DO Statement

The DO statement has two forms:

+ DO+ label , variable = start , limit, step

+ DO+ label , variable = start , limit

In the second form the step size is implicitly one.

The label marks the final statement of the loop. It must be attached to an executable statement further on in the program unit. The rules permit this statement to be any executable statement except another control statement, but it strongly recommended that you use the CONTINUE statement here. CONTINUE has no other function except to act as a dummy place-marker.

The comma after the label is optional but, as noted in section 1.4, is a useful precaution.

The variable which follows is known as the loop control variable or loop index; it must be a variable (not an array element) but may have integer, real, or double precision type.

The start, limit, and step values may be expressions of any form of integer, real, or double precision type. If the step value is present it must not be zero, of omitted it is taken as one. The number of iterations is computed before the start of the first one, using the formula:

iterations = MAX(INT(0, (limit - start + step) / step))

Note that if the limit value is less than start the iteration count is zero unless step is negative. A zero iteration count is permitted but means that the contents of the loop will not be executed at all and control is transferred to the first statement after the end of the loop. The loop control variable does not necessarily reach the limiting value, especially if the step-size is larger than one.

Statements within the loop are permitted to alter the value of the expressions used for start, limit, or step but this has no effect on the iteration count which is fixed before the first iteration starts.

The loop control variable may be used in expressions but a new value must not be assigned to it within the loop.

DO-loops may contain other DO-loops completely nested within them provided that a different loop control variable is used in each one. Although it is permissible for two different loops to terminate on the same statement, this can be very confusing. It is much better to use a separate CONTINUE statement at the end of each loop. Similarly complete IF-blocks may be nested within DO-loops, and vice-versa.

Other control statements may be used to transfer control out of the range of a DO-loop but it is illegal to try to jump into a loop from outside it. If you exit from a loop prematurely in this way the loop control variable keeps its current value and may be used outside to determine how many loops were actually executed.

After the normal termination of a DO-loop the loop control variable has the value it had on the last iteration plus one extra increment of the step value. Thus with:

DO 1000, NUMBER = 1,100,3

1000 CONTINUE

On the last iteration NUMBER would be 99, and on exit from the loop NUMBER would be 102. This provision can be useful in the event of exit from a loop because of some error:

PARAMETER (MAXVAL = 100)

REAL X(MAXVAL)

DO 15, I = 1,MAXVAL

READ(UNIT=*, FMT=*, END=90) X(I)

15 CONTINUE

90 NVALS = I - 1

The action of the statement labelled 90 is to set NVALS to the number of values actually read from the file whether there was a premature exit because the end-of-file was detected or it reached the end of the array space at MAXVAL.

Guidelines

If you a loop-control variable of any type other than integer there is a risk that rounding errors will accumulate as it is incremented repeatedly. In addition, if the expressions for the start, limit, and step values are not of integer type the number of iterations may not be what you expect because the formula uses the INT function (not NINT). None of these problems can occur if integer quantities are used throughout the DO statement.

Logical-IF Statement

Logical-IF statement

The logical-IF statement is best regarded as a special case of the IF-block when it only contains one statement. Thus:

IF(E .NE. 0.0) THEN

RECIPE = 1.0 / E

END IF

can be replaced by a single logical-IF statement:

? IF(E .NE. 0.0) RECIPE = 1.0 / E?

The general form of the logical-IF statement is:

+ IF(+ logical-expression ) statement

The statement is executed only if the logical expression has a true value. Any executable statement can follow except DO, IF, ELSE IF, ELSE, END IF, or END.

Unconditional GO TO Statement

GO TO statement

The unconditional GO TO statement simply produces a transfer of control to a labelled executable statement elsewhere in the program unit. Its general form is:

+ GO TO+ label

Note that control must not be transferred into an IF-block or a DO-loop from outside it.

Guidelines

The unconditional GO TO statement makes it possible to construct programs with a very undisciplined structure; such programs are usually hard to understand and to maintain. Good programmers use GO TO statements and labels very sparingly. Unfortunately it is not always possible to avoid them entirely in Fortran because of a lack of alternative control structures.

The next example finds the highest common factor of two integers M and N using a Euclid's algorithm. It can be expressed roughly: while (M N) subtract the smaller of M and N from the other repeat until they are equal.

PROGRAM EUCLID

WRITE(UNIT=*, FMT=*) 'Enter two integers'

READ(UNIT=*, FMT=*) M, N

10 IF(M .NE. N) THEN

IF(M .GT. N) THEN

M = M - N

ELSE

N = N - M

END IF

GO TO 10

END IF

WRITE(UNIT=*, FMT=*)'Highest common factor = ', M

END

Computed GO TO Statement

Computed GO TO statement The computed GO TO statement is an alternative to the block-IF when a large number of options are required and they can be selected by the value of an integer expression. The general form of the statement is:

+ GO TO(+ label1, label2, ... labelN ), integer-expression

The comma after the right parenthesis is optional.

The expression is evaluated; if its value is one then control is transferred to the statement attached to the first label in the list; if it is two control goes to the second label, and so on. If the value of the expression is less than one or higher than N (where there are N labels in the list) then the statement has no effect and execution continues with the next statement in sequence. The same label may be present more than once in the list.

The computed GO TO suffers from many of the same drawbacks as the unconditional GO TO, since if its branches are used without restraint they can become impenetrable thickets. The best way is to follow the computed GO TO statement with the sections of code in order, all except the last terminated with its own unconditional GO TO to transfer control to the end of the whole structure.

Any computed GO TO structure could be replaced by an IF-block with a suitable number of ELSE IF clauses. If there are a very large number of cases then this would be a little less efficient; this has to be balanced against the increased clarity of the IF structure compared to the label-ridden GO TO.

An example of the use of the computed GO TO is given here in a subroutine which computes the number of days in a month, given the month number MONTH between 1 and 12, and the four-digit year number in YEAR. Note that each section of code except the last is terminated with a GO TO statement to escape from the structure.

SUBROUTINE CALEND(YEAR, MONTH, DAYS)

INTEGER YEAR, MONTH, DAYS

GO TO(310,280,310,300,310,300,310,310,300,310,300,310)MONTH

* Jan Feb Mar Apr May Jun Jly Aug Sep Oct Nov Dec

STOP 'Impossible month number'

*February: has 29 days in leap year, 28 otherwise.

280 IF(MOD(YEAR,400) .EQ. 0 .OR. (MOD(YEAR,100) .NE. 0

$ .AND. MOD(YEAR,4) .EQ. 0)) THEN

DAYS = 29

ELSE

DAYS = 28

END IF

GO TO 1000

* Short months

300 DAYS = 30

GO TO 1000

* Long months

310 DAYS = 31

* return the value of DAYS

1000 END

STOP Statement

STOP statement

The STOP statement simply terminates the execution of the program and returns control to the operating system. Its general form is:

? STOP '? character constant '

The character constant (which must be a literal and not named constant) is optional: if present its value is "made available" to the user; usually it the message appears on your terminal. For compatibility with Fortran66 it is possible to use a string of one to five decimal digits instead of the character constant.

Ideally a program should only return control to the operating system from one point, the end of the main program, where the END statement does all that is necessary. In practice, even in the best-planned programs, situations can arise which make it pointless to continue. If these are detected in the main program there is always the option of jumping to the END statement, but within procedures there may be no choice but to use a STOP statement.

--------------------------------------------------------------------------------

Procedures

Procedures

Any set of computations can be encapsulated in a procedure. The main purpose of a procedure is to allow the same set of operations to be invoked at different points in a program. Procedures also make it possible to use the same code in several different programs. It is good practice to split a large program into sections whenever it becomes too large to be handled conveniently in one piece. The optimum size of a program unit is quite small, probably no more than 100 lines.

Four different forms of procedure can be used in Fortran programs:-

Intrinsic functions

Statement functions

External functions (also known as function subprograms)

Subroutines.

Intrinsic functions are provided automatically by the Fortran system, whereas the other three forms of procedure are user-written. Statement functions, which are defined with the statement function statement, can be only be used in the program unit in which they were defined and are subject to other special restrictions. External functions and subroutines are two alternative forms of external procedure: each is specified as a separate program unit and can be used (with only a few restrictions) anywhere else in the program.

Intrinsic Functions

Intrinsic Functions

Intrinsic functions have a number of unique properties. The data type of each intrinsic function is known to the Fortran system and is not subject to the normal rules. IMPLICIT and type statements alone have no effect on them. Some intrinsic functions have generic names: when these are used the compiler selects the appropriate specific function according to the data type of the arguments.

A few intrinsic functions such as MAX, MIN, and CMPLX, are allowed to have a variable number of arguments, but all of the arguments must have the same data type. User-written procedures cannot have optional arguments or generic type.

Although intrinsic functions can be used in any program unit, their names are not global, nor are they reserved words. It is, however, best to avoid choosing a name for a variable or array which is identical to that of an intrinsic function. It may cause confusion and in the long run it may make it more difficult to enhance the program. A name clash is more serious if it involves an external function or subroutine, for in this case the external procedure name must be specified in an EXTERNAL statement to resolve the ambiguity. By this means it is possible to substitute an external function of your own for one of the intrinsic functions.

The Fortran Standard specifies a fairly extensive set of intrinsic functions which must always be available but it does not prevent the provision of additional ones. Many systems provide additional intrinsic functions which, for example, obtain the current date and time, generate pseudo-random numbers, or evaluate Gaussian probability. The main drawback in using non-standard functions is that you may have to find a substitute if your program is moved to another system which does not have the same extensions.

The standard intrinsic functions for the arithmetic types are described in detail in section 6.2; those used with character-strings are covered in section 7.5. A complete alphabetical list is provided in the appendix.

Statement Functions

Statement functions Functions, statement

Statement functions can be defined within any executable program unit by means of statement function statements. They can only be used, however, within the same program unit. Although statement functions have limited uses, they are unjustly neglected by many programmers.

The statement function statement resembles an ordinary assignment statement. For example:

? FAHR(CELS) = 32.0 + 1.8 * CELS?

The function FAHR converts a temperature in degrees Celsius to its equivalent in Fahrenheit. Thus FAHR(20.0) would return a value 68.0 approximately.

A statement function can have any number of dummy arguments (such as CELS above) all of which must appear in the expression on the right-hand side; this expression may also include constants, variables, or array elements used elsewhere in the program. When the function is called the current values of these items will be used. For example:

REAL M1, M2, G, R

NEWTON(M1, M2, R) = G * M1 * M2 / R**2

A reference to the function in an assignment statement such as:

? FORCE = NEWTON(X, Y, DIST) ?

will return a value depending on the values of the actual arguments X, Y, and DIST, and that of the variable G at the time the function is referenced.

Definitions of statement functions can also include references to intrinsic functions, external functions, or previously defined statement functions:

PARAMETER (PI = 3.14159265, DTOR = PI/180.0)

SIND(THETA) = SIN(THETA * DTOR)

COSD(THETA) = COS(THETA * DTOR)

TAND(THETA) = SIND(THETA) / COSD(THETA)

These definitions allow trigonometry on angles specified in degrees rather than radians.

The scope of each dummy argument name (such as THETA above) is that of the statement alone; these names can be used elsewhere in the program unit as variables of the same data type with no effect whatever on the evaluation of the function.

Statement functions can have any data type; the name and arguments follow the normal type rules. They can be useful in character handling, for example:

LOGICAL MATH, DIGIT, DORM

CHARACTER C*1

DIGIT(C) = LGE(C, '0') .AND. LLE(C, '9')

MATH(C) = INDEX('+-*/', C) .NE. 0

DORM(C) = DIGIT(C) .OR. MATH(C)

These three functions each return a logical value when presented with a single character argument: DIGIT tests to see whether the character is a digit, MATH whether it is an operator symbol, and DORM will test for either condition. Note the use of the lexical comparison functions LGE and LLE in the definition of DIGIT which make it completely independent of the local character code.

Statement Function Rules

Statement function statements must appear after any the specification statements but before all executable statements in the program unit. They may be intermixed with DATA and FORMAT statements. The general form is:

+ + function ( dummy1, dummy2, ... dummyN ) = expression

The function may have any data type; the expression will normally have the same data type but if both have an arithmetic type then the normal conversion rules for arithmetic assignment statements apply.

The name of the function must be distinct from all other symbolic names in the program unit. It may appear in type statements but not in other specification statements. (There is one exception: a common block is permitted to have the same name as a statement function but since common block names always appear between slashes there is little risk of confusion). If the function has character type its length must be an integer constant expression.

The dummy arguments are simply symbolic names. A name may not appear more than once in the same list. These names may be used elsewhere in the program unit as variables of the same data type.

The expression must contain the dummy arguments as operands. The operands may also include:

literal constants, named constants, variables, and array elements; these will have their values at the time the function is executed and must then be defined.

references to intrinsic and external functions,

references to statement functions defined earlier in the same program unit,

complete expressions enclosed in parentheses.

Note that character substrings are not permitted. The variables and array elements used in the expression must be defined at the time that the function reference is executed.

Guidelines

Although statement functions have a limited role to play in programs because they can only be defined in a single statement, references to statement functions they may be executed more efficiently than references to external functions; many modern compilers expand statement function references to in-line code when it is advantageous to do so.

If the same statement function is needed in more than one program unit it would is possible to use an INCLUDE facility to provide the same definition each time, but it will usually be better to use an external function instead.

External Procedures

Procedures, external External procedures There are two forms of external procedure, both of which take the form of a complete program unit.

External functions, which are specified by a program unit starting with a FUNCTION statement. They are executed whenever the corresponding function is used as an operand in an expression.

Subroutines, which are specified by a program unit starting with a SUBROUTINE statement. They are executed in response to a CALL statement.

In either form the last statement of the program unit must be an END statement. Any other statements (except PROGRAM or BLOCK DATA statements) may be used within the program unit.

There are two statements provided especially for use in external procedures. The SAVE statement ensures that the values of local variables and arrays are preserved after the procedure returns control to the calling unit: these values will then be available if the procedure is executed subsequently. The RETURN statement may be used to terminate the execution of the procedure and cause an immediate return to the control of the calling unit. Execution of the END statement at the end of the procedure has exactly the same effect. Both of these are described in full later in the section.

Most Fortran systems also allow external procedures to be specified in languages other than Fortran: they can be called in the same way as Fortran procedures but their internal operations are, of course, beyond the scope of this book.

It is best to think of the subroutine as the more general form of procedure; the external function should be regarded as a special case for use when you only need to return a single value to the calling unit.

Here is a simple example of a procedure which converts a time of day in hours, minutes, and seconds into a count of seconds since midnight. Since only one value needs to be returned, the procedure can have the form of an external function. (In fact this is such a simple example that it would have been possible to define it as a statement function.)

*TSECS converts hours, minutes, seconds to total seconds.

REAL FUNCTION TSECS(NHOURS, MINS, SECS)

INTEGER NHOURS, MINS

REAL SECS

TIME = ((NHOURS * 60) + MINS) * 60 + SECS

END

Thus if we use a function reference like TSECS(12,30,0.0) in an expression elsewhere in the program it will convert the time to seconds since midnight (about 45000.0 seconds in this case). The items in parentheses after the function name :

? (12,30,0.0) ?

are known as the actual arguments of the function; these values are transferred to the corresponding dummy arguments

? (NHOURS, MINS, SECS)?

of the procedure before it is executed. In this example the argument list is used only to transfer information into the function from outside, the function name itself returns the required value to the calling program. In subroutines, however, there is no function name to return information but the arguments can be used for transfers in either direction, or both. The rules permit them to be used in this more general way in functions, but it is a practice best avoided.

The next example performs the inverse conversion to the TSECS function. Since it has to return three values to the calling program unit the functional form is no longer appropriate, and a subroutine will be used instead.

*Subroutine HMS converts TIME in seconds into hours, mins,secs.

SUBROUTINE HMS(TIME, NHOURS, MINS, SECS)

REAL TIME, SECS

INTEGER NHOURS, MINS

NHOURS = INT(TIME / 3600.0)

SECS = TIME - 3600.0 * NHOURS

MINS = INT(SECS / 60.0)

SECS = TIME - 60.0 * MINS

END

In this case the subroutine could be executed by using a statement such as:

CALL HMS(45000.0, NHRS, MINS, SECS)

WRITE(UNIT=*, FMT=*) NHRS, MINS, SECS

Here the first argument transfers information into the subroutine, the other three are used to return the values which it calculates. You do not have to specify whether a particular argument is to transfer information in or out (or in both directions), but there are rules about the form of actual argument that you can use in each case. These are explained in full below.

Procedure Independence

Each program unit has its own independent set of symbolic names and labels. Type statements and IMPLICIT statements may be used to specify their data types.

External procedures can themselves call any other procedures and these may call others in turn, but procedure are not allowed to call themselves either directly or indirectly; that is recursive calling is not permitted in Fortran.

Information Transfer

Information can be transferred to and from an external procedure by any of three methods.

An argument list: as shown in the two examples above. This is the preferred method of interfacing as it is the most flexible and modular. It is described in detail in the remainder of this section.

Common blocks: these are lists of variables or arrays which are stored in areas of areas of memory shared between two or more program units. They are useful in special circumstances when procedures have to be coupled closely together, but are otherwise less satisfactory. Common blocks are covered in detail in section 12.

External files: interfacing via external files is neither convenient nor efficient but it is mentioned here to point out that external files are global. Once a file has been opened in any program unit it can be accessed anywhere in the program provided that the appropriate I/O unit number is available. A unit number can be passed into a procedure as an integer argument.

Procedure Execution

It is not necessary to know how the Fortran system actually transfers information from one procedure to another to make use of the system, but the rules governing the process are somewhat complicated and it may be easier to understand them if you appreciate the basis on which they have been formulated. The rules in the Fortran Standard are based on the assumption that the address of an actual argument is transferred in each case: this may or may not be true in practice but the properties will be the same as if it is.

This means that when you reference a dummy variable or assign a new value to one you are likely to be using the memory location occupied by the actual argument. By this means even large arrays can be transferred efficiently to procedures. A slight modification of this system is needed for items of character type so that the length of the item can be transferred as well as its address.

When a function reference or CALL statement is executed any expressions in the argument list are evaluated; the addresses of the arguments are then passed to the procedure. When it returns control this automatically makes updated values available to the corresponding items in the actual argument list.

Functions with Side-effects

Functions, external The rules of Fortran allow functions to have side-effects, that is to alter their actual arguments or to change other variables within common blocks. Functions with side-effects cannot be used in expressions where any of the other operands of the expression would be affected, nor can they be used in subscript or substring references when any other expression used in the same references would be affected. This rule ensures that the value of an expression cannot depend arbitrarily on the way in which the computer chooses to evaluate it.

There are also restrictions on functions which make use of input/output statements even on internal files: these cannot be used in expressions in other I/O statements. This is to avoid the I/O system being used recursively.

By far the best course is to use the subroutine form for any procedure with side-effects.

Arguments of External Procedures

Arguments can pass information into a procedure or out from it, or in both directions. This just depends on the way that the dummy argument is used within the procedure. Although any argument order is permitted, it is common practice to put input arguments first, then those that pass information both ways, and then arguments which just return information from the procedure.

The rules for argument association are the same for both forms of external procedure. The list of dummy arguments (sometimes called formal arguments) of an external procedure is specified in its FUNCTION or SUBROUTINE statement. There can be any number of arguments, including none at all. If there are no arguments then the parentheses can be omitted in the CALL and SUBROUTINE statement but not in a FUNCTION statement or function reference.

The dummy argument list is simply a list of symbolic names which can represent any mixture of

variables

arrays

procedures.

A name cannot, of course, appear twice in the same dummy argument list.

Dummy variables, arrays, and procedures are distinguished only by the way that they are used within the procedure. The dimension bounds of a dummy arrays must be specified in a subsequent type or DIMENSION statement; dummy procedures must appear in a CALL or EXTERNAL statement or be used in a function reference; anything else is, by elimination, a dummy argument variable.

Dummy argument variables and arrays can be used in executable statements in just the same way as local items of the same form, but they cannot appear in SAVE, COMMON, DATA, or EQUIVALENCE statements.

Argument Association

Arguments of procedures The actual arguments of the function reference or CALL statement become associated with the corresponding dummy arguments of the FUNCTION or SUBROUTINE statement. The main rules are as follows:

There must be the same number of actual and dummy arguments; they are associated solely by their position in the two lists. Optional arguments are not permitted in Fortran77.

If the dummy argument is a variable, array, or procedure used as a function then the corresponding actual argument must have the same data type.

If the dummy argument is an array then its array bounds must not be larger than those of the corresponding actual argument. Alternatively the dimension bounds of a dummy array can be passed in by means of other procedure arguments to form an adjustable. This option and the assumed-size array are both described in section 9.6.

If the dummy argument is a character item then its length must not be greater than that of the corresponding actual argument. Alternatively there is a passed-length option for character arguments: see section 9.5.

Because program units are compiled independently, it is difficult for the compiler to check for mismatches in actual and dummy argument lists. Although mismatches could, in principle, be detected by the linker, this rarely seems to happen in practice. Errors, particularly mismatches of data type or array bounds, are especially easy to make but hard to detect. Sometimes the only indication is that the program produces the wrong answer. This shows how important it is to check procedure interfaces.

Duplicate Arguments

The same actual argument cannot be used more than once in a procedure call if the corresponding dummy arguments are assigned new values. For example, with:

SUBROUTINE FUNNY(X, Y)

X = 2.0

Y = 3.0

END

A call such as:

? CALL FUNNY(A, A)?

would be illegal because the system would try to assign both 2.0 and 3.0 to the variable A.

A similar restriction applies to variables which are returned via a common block and also through the procedure argument list.

Variables as Dummy Arguments

If the dummy argument of a procedure is a variable and it has a value assigned to it within the procedure, then the corresponding actual argument can be:

a variable,

an array element, or

a character substring.

If, however, the dummy variable preserves its initial value throughout the execution then the actual argument can be any of these three forms above or alternatively:

an expression of any form (including a constant).

The reason for this restrictions is easy to see by considering the ways of calling the subroutine SILLY in the next example:

SUBROUTINE SILLY(N, M)

N = N + M

END

If it is called with a statement such as:

NUMBER = 10

CALL SILLY(NUMBER, 5)

then the value of NUMBER will be updated to 15 as a result of the call. But it is illegal to call the function with a constant as the first argument, thus:

CALL SILLY(10, 7)

because on exit the subroutine will attempt to return the value of 17 to the actual argument which was specified as the constant ten. The effects of committing such an error are system-dependent. Some systems detect the attempt to over-write a constant and issue an error message; others ignore the attempt and allow the program to continue; but some systems will actually go ahead and over-write the constant with a new value, so that if you use the constant 10 in some subsequent statement in the program you may get a value of 17. Since this can have very puzzling effects and be hard to diagnose, it is important to avoid doing this inadvertently.

If you make use of procedures written by other people you may be worried about unintentional effects of this sort. In principle it should be possible to prevent a procedure altering a constant argument by turning each one into an expression, for example like this:

? CALL SILLY(+10, +5)?

or

? CALL SILLY((10), (5))?

Although either of these forms should protect the constants, it is still against the rules of Fortran for the procedure to attempt to alter the values of the corresponding dummy arguments. You will have to judge whether it is better to break the rules of the language than to risk corrupting a constant.

Expressions, Subscripts, and Substrings

If the actual argument contains expressions then these are evaluated before the procedure starts to execute; even if the procedure later modifies operands of the expression this has no effect on the value passed to the dummy argument. The same rule applies to array subscript and character substring expressions. For example, if the procedure call consists of:

? CALL SUB( ARRAY(N), N, SIN(4.0*N), TEXT(1:N) )?

and the procedure assigns a new value to the second argument, N, during its execution, it has no effect on the other arguments which all use the original value of N. The updated value of N will, of course, be passed back to the calling unit.

Passed-length Character Arguments

A character dummy argument will have its length set automatically to that of the corresponding actual argument if the special length specification of *(*) is used.

To illustrate this, here is a procedure to count the number of vowels in a character string. It uses the intrinsic function LEN to determine the length of its dummy argument, and the INDEX function to see whether each character in turn is in the set "AEIOU" or not.

INTEGER FUNCTION VOWELS(STRING)

CHARACTER*(*) STRING

VOWELS = 0

DO 25, K = 1,LEN(STRING)

IF( INDEX('AEIOU', STRING(K:K)) .NE. 0) THEN

VOWELS = VOWELS + 1

END IF

25 CONTINUE

END

Note that the function has a data type which is not the default for its initial letter so that it will usually be necessary to specify its name in a INTEGER statement in each program unit which references the function.

This passed-length mechanism is recommended not only for general-purpose software where the actual argument lengths are unknown, but in all cases unless there is a good reason to specify a dummy argument of fixed length.

There is one restriction on dummy arguments with passed length: they cannot be operands of the concatenation operator (//) except in assignment statements. Note that the same form of length specification " *(*)" can be used for named character constants but with a completely different meaning: named constants are not subject to this restriction.

Arrays as Arguments

Arrays as arguments If the dummy argument of a procedure is an array then the actual argument can be either:

an array name (without subscripts)

an array element.

The first form transfers the entire array; the second form, which just transfers a section starting at the specified element, is described in more detail further on.

The simplest, and most common, requirement is to make the entire contents of an array available in a procedure. If the actual argument arrays are always going to be the same size then the dummy arrays in the procedure can use fixed bounds. For example:

SUBROUTINE DOT(X, Y, Z)

*Computes the dot product of arrays X and Y of 100 elements

* producing array Z of the same size.

REAL X(100), Y(100), Z(100)

DO 15, I = 1,100

Z(I) = X(I) * Y(I)

15 CONTINUE

END

This procedure could be used within a program unit like this:

PROGRAM PROD

REAL A(100), B(100), C(100)

READ(UNIT=*,FMT=*)A,B

CALL DOT(A, B, C)

WRITE(UNIT=*,FMT=*)C

END

This is perfectly legitimate, if inflexible, since it will not work on arrays of any other size.

Adjustable Arrays

Arrays, adjustable Adjustable arrays A more satisfactory solution is to generalise the procedure so that it can be used on arrays of any size. This is done by using an adjustable arrays declaration. Here the operands in each dimension bound expression may include integer variables which are also arguments of the procedure (or members of a common block). The following example shows how this may be done:

SUBROUTINE DOTPRO(NPTS, X, Y, Z)

REAL X(NPTS), Y(NPTS), Z(NPTS)

DO 15, I = 1,NPTS

* etc.

In this case the calling sequence would be something like:

? CALL DOTPRO(100, A, B, C)?

An adjustable array declaration is permitted only for arrays which are dummy arguments, since the actual array space has in this case already been allocated in the calling unit or at some higher level. The method can be extended in the obvious way to cover multi-dimensional arrays and those with upper and lower bounds, for example:

SUBROUTINE MULTI(MAP, K1, L1, K2, L2, TRACE)

DOUBLE PRECISION MAP(K1:L1, K2:L2)

REAL TRACE(L1-K1+1)

The adjustable array mechanism can, of course, be used for arrays of any data type; an adjustable array can also be passed as an actual argument of a procedure with, if necessary, the array bounds passed on in parallel.

Each array bound of a dummy argument array may be an integer expression involving not only constants but also integer variables passed in to the procedure either as arguments or by means of a common block. The extent of each dimension of the array must not be less than one and must not be greater than the extent of the corresponding dimension of the actual argument array.

If any integer variable (or named constant) used in an array-bound expression has a name which does not imply integer type then the INTEGER statement which specifies its type must precede its use in a dimension-bound expression.

Assumed-size Arrays

Arrays, assumed-size Assumed-size arrays There may be circumstances in which it is impracticable to use either fixed or adjustable array declarations in a procedure because the actual size of the array is unknown when the procedure starts executing. In this case an assumed-size array is a viable alternative. These are also only permitted for dummy argument arrays of procedures, but here the array is, effectively, declared to be of unknown or indefinite size. For example:

REAL FUNCTION ADDTWO(TABLE, ANGLE)

REAL TABLE(*)

N = MAX(1, NINT(SIN(ANGLE) * 500.0))

ADDTWO = TABLE(N) + TABLE(N+1)

END

Here the procedure only knows that array TABLE is one-dimensional with a lower-bound of one: that is all it needs to know to access the appropriate elements N and N+1. In executing the procedure it is our responsibility to ensure that the value of ANGLE will never result in an array subscript which is out of range. This is always a danger with assumed-size arrays. Because the compiler does not have any information about the upper-bound of an assumed-size array it cannot use any array-bound checking code even if it is normally able to do this. An assumed-size array can only have the upper-bound of its last dimension specified by an asterisk, all the other bounds (if any) must conform to the normal rules (or be adjustable using integer arguments).

An assumed size dummy argument array is specified with an asterisk as the upper bound of its last (or only) dimension. All the other dimension bounds, if any, must conform to normal rules for local arrays or adjustable arrays.

There is one important restriction on assumed size arrays: they cannot be used without subscripts in I/O statements, for example in the input list of a READ statement or the output list of a WRITE statement. This is because the compiler has no information about the total size of the array when compiling the procedure.

Array Sections

The rules of Fortran require that the extent of an array (in each dimension if it is multi-dimensional) must be at least as large in the actual argument as in the dummy argument, but they do not require actual agreement of both lower and upper bounds. For example:

PROGRAM CONFUS

REAL X(-1:50), Y(10:1000)

READ(UNIT=*,FMT=*) X, Y

CALL OUTPUT(X)

CALL OUTPUT(Y)

END

SUBROUTINE OUTPUT(ARRAY)

REAL ARRAY(50)

WRITE(UNIT=*,FMT=*) ARRAY

END

The effect of this program will be to output the elements X(-1) to X(48) since X(48) corresponds to ARRAY(50), and then output Y(10) to Y(59) also. The subroutine will work similarly on a slice through a two-dimensional array:

PROGRAM TWODIM

REAL D(100,20)

* ...

NSLICE = 15

CALL OUTPUT(D(1,NSLICE))

In this example the slice of the array from elements D(1,15) to D(50,15) will be written to the output file. In order to work out what is going to happen you need to know that Fortran arrays are stored with the first subscript most rapidly varying, and that the argument association operates as if the address of the specified element were transferred to the base address of the dummy argument array.

The use of an array element as an actual argument when the dummy argument is a complete array is a very misleading notation and the transfer of array sections should be avoided if at all possible.

Character Arrays

Arrays, character Character arrays When a dummy argument is a character array the passed-length mechanism can be used in the same way as for a character variable. Every element of the dummy array has the length that was passed in from the actual argument.

For example, a subroutine designed to sort an array of character strings into ascending order might start with specification statements like these:

SUBROUTINE SORT(NELS, NAMES)

INTEGER NELS

CHARACTER NAMES(NELS)*(*)

Alternatively the actual argument can be a character variable or substring. In such cases it usually makes more sense not to use the passed-length mechanism. For example an actual argument declared:

? CHARACTER*80 LINE?

could be passed to a subroutine which declared it as an array of four 20-character elements:

SUBROUTINE SPLIT(LINE)

CHARACTER LINE(4)*20

Although this is valid Fortran, it is not a very satisfactory programming technique to use a procedure call to alter the shape of an item so radically.

Procedures as Arguments

Procedures as arguments Fortran allows one procedure to be used as the actual argument of another procedure. This provides a powerful facility, though one that most programmers use only rarely. Procedures are normally used to carry out a given set of operations on different sets of data; but sometimes you want to carry out the same set of operations on different functional forms. Examples include: finding the gradient of a function, integrating the area under a curve, or simply plotting a graph. If the curve is specified as a set of data points then you can simply pass over an array, but if it is specified by means of some algorithm then the procedure which evaluates it can itself be an actual argument.

In the next example, the subroutine GRAPH plots a graph of a function MYFUNC between specified limits, with its argument range divided somewhat arbitrarily into 101 points. For simplicity it assumes the existence of a subroutine PLOT which moves the pen to position (X,Y). Some other subroutines would, in practice, almost certainly be required.

SUBROUTINE GRAPH(MYFUNC, XMIN, XMAX)

*Plots functional form of MYFUNC(X) with X in range XMIN:XMAX.

REAL MYFUNC, XMIN, XMAX

XDELTA = (XMAX - XMIN) / 100.0

DO 25, I = 0,100

X = XMIN + I * XDELTA

Y = MYFUNC(X)

CALL PLOT(X, Y)

25 CONTINUE

END

The procedure GRAPH can then be used to plot a function simply by providing its name them as the first argument of the call. The only other requirement is that the name of each function used as an actual argument in this way must be specified in an INTRINSIC or EXTERNAL statement, as appropriate. Thus:

PROGRAM CURVES

INTRINSIC SIN, TAN

EXTERNAL MESSY

CALL GRAPH(SIN, 0.0, 3.14159)

CALL GRAPH(TAN, 0.0, 0.5)

CALL GRAPH(MESSY, 0.1, 0.9)

END

REAL FUNCTION MESSY(X)

MESSY = COS(0.1*X) + 0.02 * SIN(SQRT(X))

END

This will first plot a graph of the sine function, then of the tangent function with a different range, and finally produce another plot of the external function called MESSY. These functions must, of course, have the same procedure interface themselves and must be called correctly in the GRAPH procedure.

It is possible to pass either a function or a subroutine as an actual argument in this way: the only difference is that a CALL statement is used instead of a function reference to execute the dummy procedure. It is possible to pass a procedure through more than one level of procedure call in the same way. Continuing the last example, another level could be introduced like this:

PROGRAM CURVE2

EXTERNAL MESSY

INTRINSIC SIN, TAN

CALL GRAPH2(PRETTY)

CALL GRAPH2(TAN)

END

SUBROUTINE GRAPH2(PROC)

EXTERNAL PROC

CALL GRAPH(PROC, 0.1, 0.7)

END

Thus the procedure GRAPH2 sets limits to each plot and passes the procedure name on to GRAPH. The symbolic name PROC must be declared in an EXTERNAL statement as it is a dummy procedure: an EXTERNAL statement is required whether the actual procedure at the top level is intrinsic or external. The syntax of the INTRINSIC and EXTERNAL statements is given in section 9.12 below.

The name of an intrinsic function used as an actual argument must be a specific name and not a generic one. This is the only circumstance in which you still have to use specific names for intrinsic functions. A full list of specific names is given in the appendix. A few of the most basic intrinsic functions which are often expanded to in-line code (those for type conversion, lexical comparison, as well as MIN and MAX) cannot be passed as actual arguments.

Subroutine and Call Statements

It is convenient to describe these two statements together as they have to be closely matched in use. The general form of the SUBROUTINE statement SUBROUTINE statement is:

+ SUBROUTINE+ name ( dummy1, dummy2, ... dummyN )

or

+ SUBROUTINE+ name
 
my essay is a program written in c++ to solve the towers of hanoi:

#include

#include "TowersOfHanoi.h"

using std::cout;

using std::cin;

using std::endl;

void TowersOfHanoi::move(int disks, char first, char last, char temp)

{

int array[disks];

for(int n = 0; n < disks; n++)

array[n] = n + 1;

if(disks > 1)

{

move(disks - 1, first, temp, last);

}

cout
 
void TowersOfHanoi::move(int disks, char first, char last, char temp)

{

int array[disks];

for(int n = 0; n < disks; n++)

array[n] = n + 1;

if(disks > 1)

{

move(disks - 1, first, temp, last);

}

cout
 
fuck that...here is one to print out the summation of two arrays!:

#include

#include

#include

#include

#include "RLN.h"

using std::cout;

using std::cin;

using std::endl;

RLN::RLN()

{

num1 = vector (size);

num2 = vector (size);

sum = vector (size + 1);

sub = vector (size);

}

void RLN::get_input()

{

ifstream inFile("input5.txt");

int j;



for(j = 0; j < size; j++)

inFile >> num1[j];



for(j = 0; j < size; j++)

inFile >> num2[j];

}

void RLN::do_add()

{

for(int i = 0; i < size; i++)

sum[i + 1] = num1 + num2;

for(int i = size; i >= 0; i--)

{

if(sum > 9)

{

sum = sum - 10;

sum[i - 1] += 1;

}

}

}

void RLN::do_sub()

{

for(int i = size - 1; i >= 0; i--)

{

if(num1 >= num2)

sub = num1 - num2;

else

{

num1[i - 1] -= 1;

num1 += 10;

sub = num1 - num2;

}

}

}

void RLN::is_greater()

{

if(num1 > num2){}

else if(num1 < num2)

{

temp = num1;

num1 = num2;

num2 = temp;

}

}

void RLN::print_sum()

{

cout
 
PPP has lost all respect to wards all members forever until the hot girl thread is reinstated, you stole that which is not yours. and now you must die
 
^Ryno, congrats on the promotion!

anyways, my entry for the cult is in a new thread cause it's pretty long, and i don't wanna take up a bunch of fucking space and make it all broken. it's called "the true identity of atlantaski"
 
Haha, I read youre story, all 3 parts. You made someone say 'LOL' in a dialoge, haha.

It was actually pretty funny, why was it removed?
 
wow, the mods are seriously on a fucking power trip. now you cant post a harmless story. bullshit.
 
well i guess it was cause slander/untrue stories about pros arent allowed. i can kind of understand that.

but just since im pissed, and its so hot right now, ill blame the mods...
 
Just a small time girl

livin in a lonely world

she took the midnite train goin anywhere

just a city boy

born and raised in south detriot

he took the midnite train goin anywhereee

cool guitar music

a singer in a smokey room

the smell of wine and cheap perfume

for the smile they can share the nite

it goes on and on and on and on and on

strangers!

waiting, up and down the boulevarrd

shadows searching through the nigh

street lights! people!

living just to pull the motion

living in the worrrrld

ripin on guitar

workin hard to get my feel

everyone wants a pill

just to roll the dice, one mo time

summer will, some lose

some were born to sing the blues

it goes on and aon

strangesrts

waiting

repeat up above
 
^haha that showed him.

Have u heard the new Dad jokes? Like "your daddy this, and your daddy that"

Killer jokes.

Your dads so fat he can play pool with the universe. OOOOOOOO BURN HAVE SOME ICE
 
What it is to be PPP, by PhattTim

Yo,

For those of you who are new and for those of you who are old, just a quick reminder.

You are here not because we are better than everyone else. We're no cooler or more excellent than all the punks on the open forum. We just hold a different intention. That is, to have decent conversation, to talk comfortably with each other about any manner of topics, trivia or personal, trash talk or controversy, wihout being flamed and hassled by everyone else. Most of us here have known each other (personally or just through e-hugs) for many years now. We've grown up together and we know more about each other than most hetrosexual friends should but that's what makes us cool, that's what makes us different.

We're a network, a spiderweb of interminglings worldwide, brought together by common interest and mutual boredom. This is a place we can post and discuss all kinds of things amongst friends, confidents and like minded people. It's special, and let's keep it that way.

Also remember that there are a hundred people on here, most of us live in completely different places. That's almost a hundred couches you can crash on when you go travelling. a hundred guys and girls who'll show you the secret spots on the ski hills and that's awesome.

I'm still waiting for our first inter-PPP marriage but it'll happen! hehe.

So cheers guys for making this what it is and don't forget what it's all about, keeping it real, representing with your crew, owning the shib and just being uber chill. That's what Salomon the penguin would want...

T
 
Back
Top