Can you give us a complete list of the Fortran commands and what they do?
Sorry, but there are too many pages involved, and copyright problems with the standard Fortran manuals sold by computer software vendors. You're stuck with the text and Web pages, buying something else from a bookstore, or buying a Fortran package for your PC.
Do we need to prompt the user for input on our programs?
Always! In this class, any programmed "read" from the terminal must be preceded by writing some intelligible message asking for input.
Why do you put so many lines of empty space in your programs?
I hope the lines aren't totally empty. They should contain a "c" in column one. These "blank" lines are just to make the comments stand out from Fortran code lines or to highlight key blocks of a program.
What directory is used by the compiler for compiling a Fortran Program? Where does f77 live?
For the work in this class, you should assume that everything happens in whatever directory you are in when you type the "f77". Type "pwd" if you don't know the answer to this question. The executable file called "f77" resides both in /bin and /usr/bin on these machines. This is very unsual. To locate an executable file use the "whereis" command (e.g. "whereis f77"). Unfortunately the manual pages on f77 aren't connected properly and are listed under IBM's other name for their compiler, "xlf". Try "man xlf" for more information on the compiler, but don't expect too much. IBM likes to force people to buy manuals and special CD-ROM packages.
How do you use a logical variable? What is stored there?
Most frequently, logical variables are used in association with IF statements. When you want to set a logical variable LVAR to true you use "LVAR=.TRUE.". For false use "LVAR=.FALSE." In practice the computer usually stores an integer 0 in memory for false and integer 1 for true. The normal logical variable occupies 1 byte of space.
Where can I get a Fortran Compiler for an IBM PC?
You can pick up one on the internet from the GNU project, but get a better package from MOC for about $80.00.
How do we know where various steps go in a Fortran program?
Some commands have special locations, but most are located by the needs of the specific program. The PROGRAM card is always first. Statements giving variable types (INTEGER, REAL, LOGICAL, CHARACTER, ...) should precede "executable" statements. The END card must always be at the end of the program unit.
Why doesn't Fortran have intrinsic functions for something as simple as factorial?
Two reasons. Factorial isn't all that common in heavy duty scientific and engineering applications. When it does occur, it almost always is in a context where it is more computationally efficient to generate it as you go. You need 2! first then 3!, then 4!, etc. You are basically stuck doing a factorial within the context of a do loop unless you get really good and learn to write "recursive functions", but then you are just fooling yourself and writing another form of do loop. When you are taking the factorial of a large number and don't need an exact answer you can resort to Stirling's Approximation. A Fortran statement that will load the value of this approximation into the variable nfact is:
nfact = sqrt(2*3.1415926*n)*(n/2.7182818)**n
by 20! this has a 0.4% error. Try larger numbers on the machine for a better feeling of the approximation.
What is the difference between IF, THEN and DO WHILE statements.
IF THEN combined with GO TO statements will let you do anything you want. The DO WHILE and other DO constructs allow you to loop through certain portions of code many times without ever writing GO TO statements. This makes coding slightly simpler, definitely clearer, and computer science types much happier.
What can I do if my lines wrap around to the next line?
You have to get a feel for the location of the 72nd character position on the screen, or do a lot of counting. Once you hit column 72, you must hit the RETURN key, put some character (I like & or #) in column 6 of the next line then pick up typing where you left off. I usually stop before column 72 at some natural break point between variables:
123456789012345678901234567890123456789012345678901234567890123456789012Since Fortran tends to ignore blanks, the extra ones in the above 2 lines don't cause problems.
IF ( (X.LT.2.0.AND.Y.GT.30.0).OR.(X.GT.1000.0.AND.Y.LT.-40.))
& PRINT *,' CALCULATION IS IN TROUBLE'
What is the advantage of an array over a spreadsheet format?
Both can store similar types of information in a neatly labeled and organized way. The advantage lies in where they are used. You have more control over how Fortran arrays are used than how the contents of a spreadsheet are used. In addition for any given operation on an array of numbers, once the Fortran is written, it will do the job much faster than a spreadsheet. On the other hand, when operations are not complex and computer execution time is not a problem using the spreadsheet is probably your best bet.
How can I format my output to look nicer (clear screen, double space, etc.)
The best tools within Fortran are the in the FORMAT statement. "X" to add spaces in a line, and "/" to add blank lines on the output. Fortran doesn't know directly about your terminal type, so can't issue a specific screen clear command. You can brute force with "////////////////////////" (24 "/"'s) or call the Unix system as in the following example (note the extra call to get a listing of files after the screen is cleared). When clearing screens it is also useful to learn the Fortran "PAUSE" command to pause execution until you hit the RETURN (ENTER) key. Note that 'call system' is specific to the RS6000's. Other systems often have similar Unix connections.
call system('ls -alF')
Subprograms. What do they do and how do the help program.
They do anything that the main program can do. They help you organize your total program (main program + subprograms) by grouping specific tasks in a well defined location. They can save you repeating similar program structures at several places in your code. Also, for many tasks someone has already written a subprogram to do the job and you save a lot of work by picking up their subprogram and plugging it into your work. You will see an example of this when we start solving systems of linear equations (say 10 equations and 10 unknowns). You will define the equations, then let somebody else's subprogram solve them.
Please explain Function Subprograms.
What you have so far is just meant as an introduction. I'll be talking about them several more times during the semester. In the mean time, play with my example in newton3.f . It will get you safely through the homework. As an exercise try adding a second function at the end of the file called FUNCTION G(x), that sets G(x)=x4-2x2+1, and add lines in the main program to evaluate G(0), G(1), G(2) and print these values.
I still don't understand array variables. What are they for? How do you use them? ...
I'll try to cover most of this in the next three weeks.
How do I change the DO loop increment to something other than 1?
"DO 100 I=1,10,2" will do the loop for I=1,3,5,7,9.
What does the function REAL(x) do?
Not much if x is already a real variable. If x is and integer, the output of REAL is a real (floating point number with the same value. REAL(3) is 3.00000E+00. If x is a complex number REAL returns the real part (as opposed to the imaginary part) of x.
When accessing a data file in a program can I change directories?
Yes if you have a subdirectory called "test" under the location that your program, you can open the file "my.data" in "test" for reading on unit 11 with the command: OPEN(11,file='test/my.data')
Is there a Fortran equivalent to the PASCAL case statement?
I have forgotten what little Pascal that I knew. Take a look at the Fortran CASE statement on page 524 of the text, or in class notes.
Do spaces mater in equations?
No. Spaces are generally added for clarity. Some compilers get upset if you write things like " INTEGERI,J" rather than INTEGER I,J". Simple neatness will keep you out of these problems. Remember that a space is required in column 6 if you aren't continuing from the previous line. The following are all equivalent:
x=x*y**2*sin(x)Is there a way to go back to the top of the error list after a failed compilation?
x=x * y**2 * sin(x)
x = x * y ** 2 * sin ( x )
The Unix script command will save things for you in a file. If you are running a terminal emulator with a "scroll bar" on the side of the window. Try clicking in the scroll bar to see old stuff. Most of you probably won't have that option, so try one of the following:
f77 test.f >& testout
f77 -qsource test.f
The first dumps everything that would go to the screen (& picks up error messages which are treated as special cases by Unix) and puts the output into a file called "testout". It will complain and fail if "testout" already exists. The second will create a nice listing file called "test.lst" containing your source code and error messages.
Why are go to statements bad, and what can we do to avoid them?
GO TO is bad only to the extent that it makes coding difficult to read. I think they are an important part of the language, but should be used sparingly. Use of DO loops and IF, THEN, ELSE constructs (along with good indentation habits) whenever practical will take care of most of the problem. In extreme cases where you have many nested IF statements, with many lines contained in each THEN/ELSE option set, even good indentation habits will not improve code clarity. Consider breaking code off into subroutines, or go back to GOTO constructs where you can at least trace label numbers with an editor.
I'm confused about the exact use of arrays and their purpose.
The simple answer is "pay attention to the lectures and examples for the rest of the semester." Arrays are used when you have a large number of numbers on which you want to do identical or similar analysis. Let's say I have the temperature and pressure from 10,000 measuring stations in the US, and want to calculate the air density at each of these points. I load the temperatures and pressures into arrays with a dimension of 10000, and pair by pair I march through and evaluate a function to give density in another array.
Yes, I could have read the data in a pair at a time and printed results one line at a time without using an array. However, what if this is just the beginning? What if I'm going to take all off my temperatures, pressures, densities and several other measurements from each station and use it as part of a complicated calculation to predict the weather for the next 24 hours? I have to keep all of the data in the computer. Arrays provide a convenient way of storing and retrieving all of this information within a program. Please note that while the program is executing, information contained in arrays, like other Fortran variables, is kept in the machine memory where access times are much faster than when the information is in a disk file.
When the main program is followed by a subprogram, why doesn't the compiler stop compiling when it hits the first END statement.
Fortran always reads the entire contents of any ".f" file that you give it. The END statement just tells it that what follows is a separate program entity with limited shared information.
I don't understand what a DATA statement does.
It simply puts a specific value into a Fortran variable. "DATA A/1.0/" is the same as "A=1.0" in terms of results. The only difference is that the DATA statement sets A to 1.0 before the program starts to execute. The biggest advantage occurs when you are trying to give a variable a value in a FUNCTION or SUBROUTINE that is used a large number of times. The program on the left is faster than the program on the right below.
Do 100 i=1,1000000 do 100 i=1,1000000Yes, its a stupid example, but applies to real programs.
100 y(i)=addon(x(i)) 100 y(i)=addon(x(i))
function addon(x) function addon(x)
data b/1.12345/ b=1.12345
Could you go over colons again in dealing with arrays?
Within Fortran 77 there is only one use for colons associated with arrays, and that is when establishing the size and permitted range of indices in a DIMENSION or type (REAL, INTEGER, ...) statement. For example "DIMENSION A(-4:10)" assigns 15 words of memory to the array A, and makes a note that A(-4) corresponds to the first of these sequential words. For Fortran 90 using a colon within an executable statement is just shorthand for writing a DO loop involving just one line.
C(1:9)=A(1:9)+B(1:9) is the same as: DO 100 I=1,9Write a few 5-10 line programs until you are comfortable with how this behaves.
C(1:9:2)=A(1:9:2)+B(1:9:2) is the same as DO 100 I=1,9,2
Can more than one variable be stored in a Data Statement?
Yes. For example you can set initial values for A, B, and C with either of the following statements.
What is the difference between a DATA statement and assigning a value to a variable with an =?
It is all in what happens when the machine executable code (a.out) is generated. When I assign a variable A with the statement "DATA A/1.0/" the compiler assigns an address (location) in memory, lets say 2001, that will be used to store numbers corresponding to A. It then proceeds to preload address 2001 with the computer representation for the number 1.0. Whenever you use A on the right hand side of an = the computer uses the number in address 2001 (1.0). If you use A on the left hand side of an =, the computer puts a new number into address 2001.
If you had not used a DATA statement, but set A with the statement "A=1.0", the compiler would have begun as before assigning memory address 2001 to A, but would not preload a value there. Without telling you it would also assign an address in memory , say 2005, for the real constant 1.0, and would preload the word at this address with the computer representation for the number 1.0. At every place in your Fortran that you refer to "1.0", the computer knows to go looking in address 2005 for a number to use. In particular for your statement "A=1.0", when the program (a.out) is executed, a machine instruction exists that does the job by copying the four bytes of memory beginning at address 2005 into memory beginning at 2001.
By using the DATA statement you have eliminated a machine instruction and perhaps the need for one word of memory. Big deal? Many times yes. You will often find yourself in a situation where the instruction "A=1.0" is located at the beginning of a Function or Subroutine that may be used millions to billions of times in a calculation. Savings like that add up.
I am still unclear with Subprograms. Could you give a summary.
The purpose of Subprograms (Functions and Subroutines) is to isolate certain well defined calculational (or I/O) tasks in a special location. This permits easy use of these lines of programming at any point in a large program. This has the side-effect of minimizing errors in your program. Rather than entering the same 20 lines of programming at 10 different places in a large program, and introducing typos in some of these locations, I put it all in one location limiting the area I must search if an error exists. If I later decide to do this specific task in a different way, replacement of the old coding is much simpler. In addition the total program becomes much easier to understand if there is just one point where viscosity is calculated, one point where enthalpy is calculated, etc.
Functions and Subroutines are very isolated from each other and from the main program. You start by assuming that a given subprogram knows nothing about what is going on elsewhere, that variable names do not necessarily have the same meaning as they do elsewhere. You pass information into and out from subprograms via the argument list. Say that in the main program you have the line:
and later we define the function as
When the line defining z is reached, the program makes a note of were the values that we have labeled "x" and "y" are located, and where in memory it wants the value called "funxy" tucked away. It usually puts this note in a memory location near the start of the instructions to evaluate funxy then branches to these instructions. Although funxy was written in terms of variables called "a" and "b", it checks the note from the calling program (calling program could be the main program or another subprogram) and associates "a" with the contents of the memory location named "x" in the calling program and "b" with the contents of the memory location named "y". The numbers in these locations are summed, the sum is squared, and the result is tucked away in the memory location reserved to contain the results from "funxy".
What are the relative advantages of Function Subprograms, and Statement Functions?
The Function Subprogram should be your choice when evaluation of the function takes more than one line of Fortran or the function is referenced from many places in your program, and size of your executable program is important. I am including liberal use of the line continuation option (character in column 6) in my definition of "one line of Fortran". It is not difficult to generate functions that need some IF tests to cover special cases or error processing (see viscl.f). These must be Function Subprograms. As for the space issue, you need to understand how each of these function types are implemented in machine code. The machine instructions for a Function Subprogram exist at only one location in the final executable code. When the Function Subprogram is referenced somewhere, some notes are made about the location of information and the location where program execution will continue after return from the function, then the flow of instruction execution branches to the Function Subprogram's code. All of this takes some extra computer time beyond what would be needed if the Function's programming was just imbedded in the rest of the code, but generally takes less space.
When a statement function is referenced an interesting thing happens at compilation time. The statement function basically disappears from the final executable machine code. If I write the following Fortran, with the first line as a statement function:
then what is effectively generated after the compiler is through is the machine code equivalent to:
No branches are generated to statement function coding, the statement function is substituted into the locations where it is referenced. This results in code that takes less time to execute than code using an equivalent Function Subprogram, but when used too frequently with long functions can produce longer executable files than you want.
What is the difference between DO, DO WHILE, and IF ( ) GOTO Loops.
In terms of what the computer actually does, there is generally no difference. You can structure all three so that they do the same thing. When properly indented, the DO structures tend to be easier to follow. The DO WHILE structure can produce slightly more compact coding, combining a straight DO with the option for some extra comparison logic. In a vector and/or parallel computer, DO's send strong hints to the compiler that it should be looking for ways to feed a pipeline or spread calculations over multiple processors.
Are We Actually Going to Need Character Variables? Why are they used?
You could probably get through all of the programming applications in this class without resorting to character variables. However, if you are going to create programs with legible output and/or flexible input/output you will need them. You have already seen me use them to store names of files for use in input (look at the sample program trig3.f). That is one of the most common uses for simple programs. Also, take a look at the tricks I play in plot1.f and plot2.f to generate creative output with the help of character variables. When you start driving graphics subroutines to view your data, you will find that character variables a useful and generally mandatory.
Postscript: My daughter (Senior, physics) burst out laughing when she saw this question while trying to throw me off of the family PC. Yes, she is cruel, but her experience may be closer to what you will use soon than the crazy things that I do. She is doing quite a bit of Fortran programming for Dr. Garrison in the Chem department. She says she couldn't get along without character variables to keep track of file names and manage printed and graphical output.
Could you give us sample questions on determining output of code containing recent commands?
Try this one:
DO 100 I=IMIN,IMAX,ISTEP
2000 FORMAT(' I=',I5,' C=',/, (1P,4E15.7))
What is the value of "I" printed out. What are the values of "C" printed? Also check my sample "essay" question on the back of this handout.
I still don't understand PARAMETER's. What advantages do they have over integer variables?
The PARAMETER statement creates a special variable that behaves exactly like an integer or real constant. In terms of what your final machine executable file (a.out) looks like the following two programs effectively give the same results:
DO 100 I=1,NDIM
PRINT *, C
DATA A/2*1.0, 3*2.0./
DO 100 I=1,5
PRINT *, C
The first advantage of using NDIM as a parameter would be more obvious in a longer program. If I want to change the second form of the program from operating on an array with 5 elements to one with 80 elements, I must search for all uses of 5 that are appropriate and change them to 80. In the first form, I only have to change the 5 in the parameter statement. Remember that only something declared a parameter can be used in the way that I have used NDIM, NR1, and NR2 in the DIMENSION and DATA statements. The other advantage of the PARAMETER is that when certain arithmetic operations only need to be done once, you can get them out of the way at compilation time with a PARAMETER statement. You won't really appreciate this advantage until you are setting up equations in FUNCTIONs or SUBROUTINEs. Certain constants in the equations may only require one initial evaluation, but the FUNCTION or SUBROUTINE may be used millions of times during a calculation. Please note that you can also use PARAMETER to set REAL constants.
If we write a subroutine for someone else, how can we compile it to see if it will run before giving it to them?
This question gets to the heart of good programming practices. I'll give you an introduction here, and emphasize some of these points in later lectures. The easy part of the answer was hidden in the demonstration where I compiled 2 parts of a program separately and made a program on the 2nd partial compile. If you make a subroutine in a file called "subr1.f", you can compile to just test for Fortran errors with the command "f77 -c subr1.f". The "-c" option says just go through the compilation steps but don't make an executable code for me. This will leave a file behind called "subr1.o", which is almost machine instructions, and is ready to be linked to other stuff to make a full program. You're really not done yet. You should write a very short driver main program, that feeds some numbers to subr1 (call subr1(...)) for which you know the returned answer. You then check the known answers against the subroutine's answers before declaring victory. This testing is often easier said than done. Most useful subroutines do some fairly complicated calculations and contain IF tests that can result in different options being used depending on the input conditions. Designing test cases and checking methods can be challenging, but is essential. When approached systematically, this testing process is science at its best. You are in the business of constructing and interpreting controlled experiments. When using computers, most people simply treat them as an extension to theoretical science, another way to solve some equations. By recognizing the organized experimental aspects of creating and using computer programs you can give yourself a competitive advantage in this business.
If you're eventually going to use X(1:N) with a chosen N what's the point in using X(*)?
Given what you know now, no point at all. If N is available to me in the argument list, I would generally use "REAL X(N)" rather than "REAL X(*)". Two exceptions to this. If I pass N as an argument to give the range of a DO loop associated with X, but also make a special reference to X(N+1) later in the subroutine, I would use "X(*)" to note that the array is really longer than N. Some smart compilers will force me to do this. The second exception is related to Fortran 77. As mentioned below, I can pass N to my subroutine through a COMMON block and not include it in the argument list. If that is how I do it, pure Fortran 77 won't accept "REAL X(N)", and I'm stuck with "REAL X(*)". Fortran 90 and most late model improved Fortran 77 compilers will permit "REAL X(N)" when N is in a COMMON block.
What is the difference between a Function Subprogram and a Subroutine.
Some of the comments in the textbook are misleading on this subject. There are really only two differences. The biggest difference is that a subroutine never returns a value that is associated with its name. This means that you never need to declare a subroutine name in a type statement (REAL, INTEGER, ...). All information coming back from a subroutine passes through the argument list, or something called a COMMON block (later). However, there is nothing in these communications channels that can't be used by a Function Subprogram. A secondary difference is that a Subroutine need not have an argument list. This won't make sense to you until you learn about COMMON blocks, so I've ducked the issue thus far.
I'm confused about CALL statements. Should they be within subroutines that they are calling for?
Think of a CALL as a GO TO statement. If you say "CALL INPUT1", Fortran effectively looks for the statement called "SUBROUTINE INPUT1" and goes there to execute more instructions. Just before taking the jump, the program makes some notes so that the code in the SUBROUTINE knows about location of arguments, and knows that when it hits a RETURN statement, it should GO TO the statement in your code following "CALL INPUT1". You can see when thinking of this as a GO TO operation, there are too many opportunities for an infinite loop if SUBROUTINE INPUT1 contains a line that says "CALL INPUT1". There are also some subtle data management nightmares. To avoid all of this, older Fortran standards refused to let you include "CALL INPUT1" in SUBROUTINE INPUT1. This continues to be a good idea. However, for those who like to live on the edge Fortran 90 will let you include "CALL INPUT1" in SUBROUTINE INPUT1. This process is called recursion. I never use it, and don't recommend it. You will get another story from a certified Computer Scientist. Those folks really love recursion, and to be fair can do some fairly slick things with it.
What does // do?
It sticks 2 character strings together into a single string. If char1='file1.in' and char2='file2.out' then when char3=char1(1:5)//char2(6:9) the contents of char3 are 'file1.out'
How about going over Format statements or Write statements that serve as formats.
You're asking for a Chapter or 2 in a text book, but let me cover the basics. A Format is in a sense a language within the Fortran language. It tells the computer how to convert its data into a nice set of characters for display, printout, or further character processing. As such the contents of a format statement are not converted to mysterious machine instructions until they are actually used (for example in a WRITE statement). This opens the possibility of using a WRITE statement to write things into a character variable to form a format variable. The WRITE does not directly serve as a format. It just builds a character string that works as a format somewhere else. For example in
WRITE(MYFORM,2000 ) N
the first WRITE creates a format MYFORM that is used in the second WRITE. You also learned in examples like this that if you want a single quote to appear as part of a character string, you must enter it within a quoted string as 2 sequential single quotes (''). The only other major new thing that you learned about formats since the last exam is that you can write more numbers than you have specifications for numbers within the format statement. When the last ) of a format is reached, the WRITE wraps around back to the last ( to find out what to do with the remaining numbers. When this wrap-around occurs, a new line is started on the output (like a hidden / in the format). You should still remember what I, E, F, X, and / can do and identify the result of writing things like the numbers 5, 3, 1.1, .03, 1001.1 with a format "(1X,2I5,1P,3E10.3)"
What is the difference between a Subprogram and a Subroutine.
A subroutine is one type of a subprogram. A Function is another, and a BLOCKDATA routine is a third.
What is the difference between a PRINT and a WRITE?
A print always outputs to the "default device" generally the terminal screen. A write sends things to a numbered unit that may be the screen, a disk file, or sometimes a printer (see OPEN statement for connecting unit number to a file).
Is it possible to lay out a two dimensional array with the double DO loops:
DO 10 J=1,4
DO 20 K=1,4
yes, if you have a Fortran 90 compiler.
Is there any way to use variables in a format statement? Yes, but you have to use one format statement to build a second using a write to a character string. For example if you want to include the value of "n" as the number of real numbers per line you would do the following:
DO 10 I=1,N
DO 10 J=1,N
What does "COMMON/CONTROL/ A, B, C" do to the values of A, B, C?
It doesn't do anything to the values. It just establishes a place in memory for the values to be stored and retrieved. Every subroutine and function in your program that contains the line "COMMON/CONTROL/ A, B, C" will agree that operations using variables A, B, or C will get numbers from or put numbers in the same appropriate location in memory. That is, they agree that a reference to "A" means the same in all routines with common CONTROL, etc.. To give A, B, or C a value, you have two options. You can assign values at compilation time with a BLOCK DATA routine:
BLOCK DATA ABCVAL
COMMON/CONTROL/ A, B, C
DATA A,B,C / 1.0, 2.0, 3.0/
You can also assign and use values with executable statements. Say subroutines SUB1 and SUB2 contain this common block. If SUB1 contains the lines "A=1.0", and "B=2.0", and SUB2 has the line "C=A+B", then the value of C after this line in SUB2 is executed is 3.0.
What's the use of a blank common as opposed to a common with a name?
The major use is in cutting the size of the of the executable file produced by "f77" (usually a.out). It won't seem like a big deal to you now, but in applications containing many arrays with thousands to millions of elements each, this can make a huge difference in the amount of disk space you soak up. A secondary use is that, when the program starts, the space finally allocated to blank common in memory is at the very end of the program. If you are tricky enough, you can take advantage of this to dynamically extend the size of a single array in blank common as your space requirements grow during execution.
If you use a BLOCK DATA statement to initialize a common block, do you lose the advantage of a smaller executable file?
No, and Yes. You don't lose anything because the disk space advantage was already gone when you decided to use the named common blocks rather than blank common. BLOCK DATA will only assign values to contents of named common blocks. The disk space decision takes place when you choose to place variables in named common or blank common. Please remember that you can have lots of different named common blocks, but there is only one blank common block.
When is the common command not used correctly in a Fortran Statement?
You're asking for quite a bit here. There are lots of ways to introduce errors. The most obvious is to try inserting a common in the midst of executable statements. It belongs up with DIMENSIONs, PARAMETER's, SAVE's, and the rest of the non-executables. A more subtle problem is the use of multiple COMMON statements for blank common or the same named common. Fortran will accept the following program:
print *, c, d
However, the values printed out are 1.0 and 2.0. The second common statement in the main program just tacks the variables "c" and "d" onto blank common after "b". The two commons together are equivalent to "common a,b,c,d". In the subroutine you are saying that "c" is the first element in blank common, so Fortran associates "c" in "sub1" with the same address in memory as assigned to "a" in the main program. Also recall that mixing reals and integers (common/blk1/a,b,i,c) when the reals are double precision may cause errors on some machines, and is not a good idea on any 32 bit machine.