Variables, Constants, and Assignments


When you write a number in the form "10." does that mean "10.0"?

Yes. Fortran just needs the decimal to interpret the number as a REAL type. "10.", "10.0", "1.e1", "1.0e1", "1.0e01", "1.0e+1", and "1.0e+01" all are the same number to Fortran.

Must you use Real and Integer in homeworks?

For any homework assignment, you must use REAL and INTEGER statements to declare the appropriate type of all variables that you use. At one time or another, you will be forced to use both of these data types. You will also eventually be using LOGICAL and CHARACTER types and DOUBLE PRECISION (REAL(8)).

What is KIND? What exactly does it do?

A given computer may have several kinds of INTEGER representation (and possibly corresponding arithmetic units). It could store some integers in a single byte, some in 2 bytes, and others in 4 bytes. That machine has 3 KINDs of INTEGER variables. You force the selection of the KIND appropriate to your needs with a statement like:

      integer (kind=2) i,j,k
or as an abbreviation

      integer (2) i,j,k
Most machines (but not all) do not use sequential numbers (1,2,3) to label their kinds of integers (or reals), they tend to use numbers for kinds that match the number of bytes used in storing the variable.

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.

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.

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.

dens(i)=rho(t(i),p(i))

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.

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,9
					         100	C(I)=A(I)+B(I)

C(1:9:2)=A(1:9:2)+B(1:9:2)  is the same as	DO 100 I=1,9,2
					        100  	C(I)=A(I)+B(I)
Write a few 5-10 line programs until you are comfortable with how this behaves.

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.

What is an ASCII character?

A standards group got together and decided what "characters" would be useful for computers. These include the obvious upper and lower case alphabet, characters to represent numbers (0,1,2,3,4,5,6,7,8,9), and other odds and ends that you see on the keyboard (: , . " ' + = - / \ ~ and others). They also included "control characters". This is what results when you hold the control key and hit a letter key. These characters were all written down, and each given a unique number from 0 to 127. The resulting standard relation between numbers and letters helps computers communicate character information back and forth without converting the underlying binary numbers to nonsense.

Are the characters "A" and "a" associated with different integers?

Yes. Take a look at my new full list of the ASCII definitions for how each integer from 0 to 127 is to be translated to a "character".

When you are shifting characters, won't this also shift and number's you read?

Yes and no. At the stage you see me shifting characters, the "numbers" are still really just strings of characters that have no meaning to the computer as numbers. I can move them around as much as I want as long as I don't disrupt the sequence of characters giving digits, decimal point and exponent. It is only through the process of a READ with a * format or ,"i", "f", "e" or related edit descriptor, that the characters we typed into the file are converted to internal representation of a number (integer or real). In particular, the default format '*' is smart enough to scan a character string, and based on the variable type that you request in the READ, obtain a value if your characters make sense, regardless of leading or trailing blanks.

Can you explain parsing and concatination again?

Parsing is the act of deviding a "sentence" into individual "words". Normally the separation that indicates "word" boundaries is a space or a comma. In computer applications the list of separators may be expanded, or completely different. However, the separators are always defined within the program and used for isolating "words". Once this separation is complete, individual words (or proper sequences) can be tested by the computer for "meaning" that cause the program to assign values to specific variables, or take other more complex actions. You are looking at the very beginning of language recognition.

Concatination is the inverse operation. You will frequently find that you have isolated words or phrases containing information that you want to tack together to form a coherent "sentence". At your stage, you are tempted to say "so what, I can already do that with formats". Usually true, but more awkward. When you get into passing arguments to graphics routines, the direct manipulation of characters becomes even more useful. Suppose I want the flexibilty to display results in either metric or English units. I need to give a plotting subroutine labels for the axes as character strings. I might have a CHARACTER array "varname" containing names for all variables of interest:

varname(1)='Temperature'

varname(2)='Pressure'

etc.

Actually, I probably would set these values in a DATA statement. For the units, I would actually use a doubly dimensioned array, but we aren't there yet, so I'll use two more regular arrays:

metric(1)='K'

metric(2)='Pa'

english(1)='F'

english(2)='psia'

etc.

Depending on user choice in the variable to output, and units to use, I will construct a full label with a concatination line like:

label = varname(i)//'('//metric(i)//')'

As we saw in the example this could result in contents of label with too many spaces:

Pressure (Pa )

(i=2 in this example). The solution to this is to use the "trim" intrinsic function

label = trim(varname(i))//' ('//trim(metric(i))//')'

giving a value in "label" of

Pressure (Pa)

Your plot is properly and cleanly labeled, ready for formal presentation. Why didn't I include the parentheses in the contents of the "metric" and "english" arrays? To use those same arrays for printing numbers like "200 psia".

Where are all or your CHARACTER variables coming from in your examples?

In the charvar.f and charvr90.f examples the vast majority of the CHARACTER strings are coming from the input file. The important thing that you must remember for any computer language is that the contents of a file created with an editor like "vi" or "emacs" are just characters. You can use FORMAT edit descriptors, or "*" default formats to convert the characters to numbers in many cases. However, you have the most flexibility by bringing the contents of the file into your program as characters, and then using appropriate tests within the program to sort out what the characters all mean. You are programming the computer to do the sorts of things you do when you read on this page "Temperature = 300.2".

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:

      	PARAMETER (NDIM=5,NR1=NDIM/2,NR2=NDIM-NR1)
     	      DIMENSION C(NDIM),A(NDIM)
      	DATA A/NR1*1.,NR2*2./
     	      DO 100 I=1,NDIM
     100	C(I)=A(I)**2+5+I
      	PRINT *, C
      	STOP
    	          END
	      DIMENSION C(5),A(5)
      	DATA A/2*1.0, 3*2.0./
     	      DO 100 I=1,5
     100	C(I)=A(I)**2+5+I
      	PRINT *, C
      	STOP
    	      END
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.

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)
	x=x * y**2 * sin(x)
	x = x          *       y  **  2      *     sin ( x )
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,1000000
  100	   y(i)=addon(x(i))			   100	   y(i)=addon(x(i))
	stop						       stop
	end						       end	
	function addon(x)	                   	 function addon(x)
	data b/1.12345/					 b=1.12345
	addon=x+b					       addon=x+b
	return						 return	
	end						       end
Yes, its a stupid example, but applies to real programs.

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.

	DATA A/1.0/,B/2.0/,C/3.0/
	DATA A,B,C/1.0,2.0,3.0/
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.

What is the difference between a PARAMETER and DATA statement?

The difference is if I set a parameter "x1" with:

      parameter (x1=1.1534e-07)
x1 will always have that value, within the program unit containing the parameter statement. I can't change the value associated with x1 later. However, if I set x1 with:

      data x1/1.1534e-07/ 
I can later change the value if necessary:

      x1=2.1433e-08
Choice between the two depends on your needs in the program.

I'm still unclear about the different ways you can set initial values to an array

For now just worry about the data statement. Think about the way you list the array elements to receive values, and the actual values to be assigned separately. For an array "a" with a dimension of four. You can establish the same initial values with any of the following:

data a/2.,3.,4.,5./

data a(1),a(2),a(3),a(4) / 2.,3.,4.,5./

data (a(j),j=1,4)/2.,3.,4.,5./

The last two can be applied to initialize just part of the array:

      data a(2),a(3) / 3.,4./
      data (a(j),j=2,3)/3.,4./
The implied DO loop functions the same as it does in WRITE and READ statements. You'll get more practice on implied DOs as the semester rolls on.

Now on to the values. You only have two options here. Either list every value separating each by commas, or when you would have repeated the same number in the full value list, you can take a shortcut with the repeat operator. The following are the same.

      data a/3.,3.,3.,5./
      data a/3*3.,5./
What does the following line do?

real, dimension(10) :: a=(/(j,j=1,10)/), b, c

It reserves memory space for 3 real arrays each containing 10 elements. For the array "a", it also preloads values into those 10 elements (a(1)=1, a(2)=2, ... a(10)=10). However, for "b" and "c" no initial values are set. Usually the elements of "b" and "c" are all set to zero, but don't bet on it.

For the index notation of multidimensional arrays, does the first index number correspond to the number of columns and the second to the number of rows?

Other way around. The first index gives the row number and the second the column number.

Why bother with arrays?

The big reason for arrays is the relative speed of computer memory and computer disk. Yes, I could constantly cycle through the file temp.data as I need to use temperatures for various calculations. However, it is much faster (factors greater than 1000) to read the information into memory once, and use the data from memory whenever needed. The homework problem is deceptively simple. I can come up with useful calculations that would use that temperature (and probably other information like pressure) thousands to millions of times.

The array language structure is nice because one name plus an index: temp(i) covers all of the information that I might need. Without arrays I would be stuck creating variable names temp1, temp2, temp3, ... temp288 to keep things in memory, and would have to go back and add new variable names if I wanted to go to two days worth of data.

I'm sort of confused about how you use a specific value out of an array. What do you call up?

The source of your confusion is probably the similarity in syntax between a function reference and an array reference. To use a value in an array, you just include the postition index of that value. If I want to multiply the 3rd element of array "A" by the 4th element of array "B", and put the result in the 2nd element of array "C", I write:

      C(2)=A(3)*B(4)   
The number between the parentheses is just a position indicator. What's more it doesn't have to be a number as above, it can be an INTEGER variable containing a value. For example I would get the same result as above with:

      k=2
      j=3
      i=4
      C(k)=A(j)*B(i)
What is pipelining and what does it do for us?

Pipelining is feeding a steady (once per clock cycle) stream of numbers to an arithmetic unit. We don't do it directly, but make it easier for the compiler to set up, by using as many Fortran 90 array constructs and array intrinsic functions as possible ( a(1:n)= b(1:n)*c(1:n) or xsum=sum(x(1:n)) )

What does the colon mean when you define an array (e.g. A(:))?

It says that I don't know the actual range of "A". If I knew that the first element in "A" is numbered "1" and the last is numbered "10",then I might write "real A(1:10)", but I don't know the size yet, so I throw out the 1 and the 10, leaving "real A(:)".

What is the difference between DIM=1, and DIM=2?

If I define 3 arrays with "REAL A(3,5), SROW(5), SCOL(3) then the line

      SROW = SUM(A,DIM=1)
is the same as the following double loop, in which I sum the contributions of all rows within each column.

      DO 200 J=1,5
         SROW(J)=0
         DO 100 I=1,3
            SROW(J) = SROW(J) + A(I,J)
 100     CONTINUE
 200    CONTINUE
and for the other dimension

      SCOL = SUM(A,DIM=2)
is the same as summing the connects of all columns for each row, as done in the following double loop

      DO 200 I =1,3
         SCOL(I)=0
         DO 100 J=1,5
            SCOL(I) = SROW(I) + A(I,J)
 100      CONTINUE
 200    CONTINUE
When using Arrays, is it possible for the user to input the parameter for the dimension something like this:

real x,dim print *, 'Please input the number of dimension for the array' read *, x parameter (dim=x) real a(dim), b(dim), No. This violates Fortran's separation of non-executable and executable statements. The PARAMETER and REAL are non-executable and must be above the PRINT and READ. This restriction is what resulted in the creation of the ALLOCATE and associated statements.

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.

Will we need to know all of the intrinsic functions (UBOUND, LBOUND, SIZE, ...)? What kind of practical uses do they have?

Know that the capabilities exist, but don't bother memorizing the names. This will let you search your favorite Fortran manual (or class notes) for details on the Function when you need it. Know how to apply the DIM argument. Know that Fortran 90 functions can do Matrix multiplies, and Dot Products, and although we didn't get to it in class, know that they can do the transpose of a matrix.

See above for practical uses of LBOUND and UBOUND. I use MATMUL, DOT_PRODUCT, and TRANSPOSE operations as a part of more complicated procedures for the efficient solution of very large systems of linear equations, particularly ones that have special "Sparse" (lots of zeros) structure.

What is the rank of an array, again?

The rank of a Fortran array is just the number of dimensions (subscripts) that it has. For the type statement "REAL A(10), B(5,3), C(3,3,4), D(6,6,6,6)", A has rank 1, B has a rank of 2, C has a rank of 3, and D a rank of 4.

What is the maximum number of places in memory for one array.

Fortran places no bounds on this. You are only limited by the space on the computer, or the maximum size of the INTEGER variable that you are using as an array index.

How are we supposed to know how much memory we need to allocate?

That information can only come from the input of data to the problem. Your problem as a programmer is to figure the best way to either force the user to directly give you the space requirements, or to deduce the space requirements by counting how much data is provided to the program. Look at the array3a.f and array3b.f examples.

How do you enter length for memory allocation?

It is a second level effect. You specify the length between the parentheses of the array given to allocate. You never have a statement "allocate (a,b,c)". It contains no size information. You must have something like "allocate (a(n),b(1:n),c(m:n))", where m and n are variables that have already been assigned values appropriate to the bounds of the arrays.

How exactly does a program allocate space for arrays when you wish for the arrays to be ALLOCATABLE? What usually determines the sizes?

Allocation of space begins with the ALLOCATABLE declaration.

real, allocatable :: b(:)

This tells the compiler that whenever it sees the variable "b" in statements, it shouldn't insert a specific address in memory, but coding that will go to another specific location in memory to get the address of "b" (this is related to pointers in some language). When you actually ask for a specific amount of space in "b" with

allocate ( b(nspace))

the details of what happens next are probably machine dependent, but the net effect is as follows: if each element of "b" is 4 bytes long, and your original program took up "proglen" bytes, then the new amount of space that your program uses in machine memory is:

new_proglen=proglen + 4*nspace

That location in memory that was reserved to contain the address in memory for "b" now contains the address at the beginning of the new 4*nspace bytes of memory.

The only reason that you bother with allocatable memory is to respond to varying user needs for memory. This means that there is always some clue from the input to the program that will specify the size of your arrays. If you are lucky (as in the homework), one element of "b" is contained in each line of the input file. You count the number of lines in the input to get "nspace". Generally, you are not that lucky, but it's the same general idea. You make one pass through the input file, simply counting the number of elements set or requested in each array to be allocated. You allocate the arrays, then REWIND the input file, and READ again, picking up actual values for the array elements.

What are mask statements and what are they used for?

First "mask" is not a formal Fortran statement. In our applications "mask" is an optional argument to a set of intrinsic functions. Think of it as a way of activating IF tests within the function. For example a simple minded Fortran representation for what goes on ina reference to "minval(a(1:n))" is:

      aminval=1.e38
      do i =1,n
         aminval=min(aminval,a(i))
      enddo
      minval=aminval
If instead I include the "mask" argument, for example as "minval(a(1:n),mask=a.gt.0)", then the action in minval could be represented as:

      aminval=1.e38
      do i =1,n
         if(a(i).gt.0) aminval=min(aminval,a(i))
      enddo
      minval=aminval
Could you go over masking one more time?

The idea of a mask is to determine which elements of an array are elgible for an array operation. For example suppose that I have two arrays:

a = (/ 1.0, 2.0, 3.0, 3.5, 4.0, 5.0/)

b = (/ -1., 2.0, -3., 3.0, 6.0, 7.0/)

and I use the following Fortran to sum some of the elements of "a"

asum = sum ( a, mask=b.gt.0 )

The first thing that effectively happens is that the mask argument is used to create a logical array containing the results of the test for every element in b.

logical-test = (.false., .true., .false., .true., .true., .true.)

This had better have the same number of elements as "a" or we are in trouble. This array of "true" and "false" values tells us that elements 1 and 3 of "a" will not be included in the sum, but the rest will.

Masks also occur in the WHERE structure:

where (b.gt.0)

c = a/b

else where

c = 0.

end where

For the values of "a" and "b" given above, elements 1 and 3 are false and produce c(1)=0 and c(2)=0. For the rest of the elements, the division is performed (e.g. c(2)=a(2)/b(2), c(4)=a(4)/b(4), etc).

When you are defining your reals (e.g. real a(100), b(100), ...), do the "a" and "b" have to be letters or can they be numbers or even something like a variable name?

The "a" and "b" can be any legal Fortran variable name. In Fortran 90 the only practical limitations are that the first character must be a letter of the alphabet (a-z, A-Z), and you can't expect Fortran to recognize any blanks in the name ("my var" is just "myvar" to Fortran, try "my_var" if you want separation).


Up to other Questions and Answers / Home


Maintained by John Mahaffy : jhm@psu.edu