Reply-to: levine@ics.uci.edu Subject: Fortran 77 Style Guide (long, REPOST) ------- Here is the f77 style guide that I have put together. Many thanks to those who provided suggestions. As mentioned in the text, I did not use them all for various reasons. Point-by-point justifications are omitted from the guide. Unless discussion of such details would be of general interest, please e-mail questions to me. David L. Levine, Dept. of ICS Internet: levine@ics.uci.edu University of California, Irvine BITNET: levine@uci Irvine, CA 92717-3425 UUCP: ...!uunet!ucivax!levine O \ o---\---- \ ---------------------------------------------------------------- Fortran 77 Coding Guidelines David L. Levine 11 Dec 1989 levine@ics.uci.edu I. Introduction The following guidelines are designed to encourage consistent coding across projects and programmers. Many arbitrary low-level decisions are made during coding. While many of these have no effect on the machine code, they do affect the appearance of the code. And, consistent practices enhance productivity and reusability. Project requirements, when applicable, take precedence. The goals of the guidelines are, in decreasing order of importance: 1) understandability -- conveys the purpose of computations to the reader 2) transportability -- between compilers on assorted modern operating systems 3) maintainability -- can be readily enhanced 4) efficiency -- execution speed II. General 1) Adhere to strict FORTRAN 77 as closely as possible, with the following exceptions. a) In addition to the standard character set (where the notation [0-9] indicates the numerals 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9) space $ ' ( ) * + , - . / : = [0-9] [A-Z] the following characters may be used: ! # & [a-z] b) Identifier names may be up to 31 characters long (the first 6 remain significant). c) INCLUDE statements may be used. d) INTEGER*4 may be used when INTEGER defaults to 2 bytes. 2) Avoid compiler directives in code, especially if an equivalent compiler option is available. If a compiler directive must be used, comment its effect and target machine, operating system, and compiler, with revision numbers. 3) Use a consistent set of compiler options, or maintain a makefile or command procedure to enable reconstruction. Suggested compiler options are listed in Appendix II. III. Project Organization 1) Use a standardized comment header at the top of every subprogram (see example in Appendix III). 2) Group related subprograms into a module. The module is assigned a unique name and is typically stored in one or more files in a single subdirectory on the file system. If used, a single object library contains the code for all the module components. 3) The first two letters of the routine and file name correspond to the module and the remaining four uniquely identify the routine. Additional characters may be used to create a more sensible name. IV. Program Units 1) Begin a main program with a PROGRAM statement. 2) Order arguments as follows: inputs, outputs, control, external names. 3) FUNCTIONs must not have side effects. 4) Reference external functions in an EXTERNAL statement. 5) Do not use alternate returns. Every subprogram should have single entry and exit points. V. Statement format 1) Use the standard source format (columns 1-5 for label, 6 for continuation, and 7-72 for statement). 2) Indicate a non-blank comment with an * in column 1. 3) Indicate a continuation with an & in column 6. 4) Do not split a name across lines. 5) Do not write more than one statement per line. VI. Statement labels 1) Assign a label only if necessary. 2) Assign labels in ascending order. 3) Assign a separate sequence of labels to FORMAT labels that are grouped at the end of a subprogram. 4) Right-adjust labels. 5) Do not use the label field of a continuation line. VII. Capitalization 1) Set keywords in all caps. 2) Set symbolic names of constants (parameters) in all lower case. 3) Set all other identifiers in initial caps with embedded words initial capped. VIII. Spacing 1) Do not use tabs. 2) Write keywords without embedded spaces, but surround keywords with spaces. 3) Do not put space between and array name and its index, Array(I). 4) Put one space between a subprogram name and its argument list, e.g., Subr (Arg1, Arg2). 5) Except in argument lists, put a space after an open parenthesis and before a close parenthesis. 6) Put one space between arguments. 7) Use spacing in equations to reveal operators and reinforce precedence. 8) Use indentation to reinforce control flow; each indent is 3 columns. 9) Use whitespace to enhance readability. IX. Identifier Selection 1) Start with letter [A-Z], follow with only letters or digits. 2) Limit to 31 characters; distinguish with first 6. 3) Choose an identifier to represent the entity being modeled. 4) Explain the significance of each variable and array in comments. 5) Do not use a keyword as an identifier. 6) Do not use a subprogram name as a COMMON block name. 7) Do not abbreviate .TRUE. or .FALSE. 8) Delimit character strings with apostrophes. X. Constants 1) Use PARAMETERs to symbolically name all compile-time constants. 2) Use only constant expressions to define PARAMETERs. XI. Typing 1) Use the following data types: CHARACTER[*n] COMPLEX DOUBLE PRECISION INTEGER*4 INTEGER LOGICAL REAL 2) Use DOUBLE PRECISION instead of REAL*8. 3) Use INTEGER for integers that are always in the range of two-byte integers [-32768,32767]; then use a compiler option to select two or four byte storage. 4) Declare all variables. Use a compiler option or IMPLICIT NONE to ensure declaration. If no such option is available, setting the implicit type of all variables to a type that is not used in the program often snags undeclared variables. An example is: IMPLICIT LOGICAL (A-Z). 5) Arrange identifiers in type declarations logically based on the entities which they describe. Subprogram arguments may be declared in order of appearance in the argument list. If there is no other obvious order, arrange alphabetically. 6) Do not compare arithmetic expressions of different types; type convert explicitly. 7) Use generic versions of functions, e.g., SQRT instead of DSQRT, unless the function return value is being passed as passed as an actual argument. XII. Operators 1) Do not use .EQ. and .NE. between floating point expressions. 2) Use .GE. or .LE., as appropriate, instead of .EQ. when checking for a threshold crossing. 3) Compare unequal length character strings with LGE, LGT, LLE, and LLT. 4) Use only the logical operators .AND., .OR., .EQV., .NEQV., and .NOT., and only use them on LOGICAL operands. 5) Avoid .NE. in an IF construct expression that has an ELSE; reverse the blocks so that the ELSE block handles the logical negation. XIII. Expressions 1) Surround low precedence operators with space. 2) Split an expression across lines after an operator. 3) Indent continuation lines. 4) Consider the types of operands and their effects on the values of subexpressions, e.g., 8 / 3 * 3.0 is 6.0, not 8.0. 5) Be careful with "exact" values of floating point expressions, e.g., assigning 30.0 / 0.1 to an integer may define it as 299! XIV. Arrays 1) Declare array dimensions in the type declaration rather than in a separate DIMENSION statement. 2) Use only INTEGER subscript expressions. 3) Preferably operate on arrays such that the first indices vary fastest and the last vary slowest. 4) Specify all subscripts in any array reference. 5) Do not exceed the bounds of declared array dimensions. XV. Control structures 1) Use GOTO carefully. See Appendix I for loop constructs. Comments at the target of a GOTO listing possible 'come froms' are very helpful. 2) Terminate or begin every loop with a distinct CONTINUE. 3) Do not jump into the middle of a loop or conditional. 4) Use STOP only for abnormal termination, and include the reason in the character string message. XVI. Arguments 1) Match the actual arguments in the caller to the formal arguments of the callee in both number and type. 2) Do not repeat an actual argument in any call. 3) All arguments to an intrinsic function must be of the same type. 4) Do not pass a constant as an actual argument unless it is to an IN formal (see Appendix III for definition). XVII. COMMON blocks 1) Only place data in COMMON blocks if necessary. 2) Place COMMON block definitions in INCLUDE files. 3) SAVE all COMMON blocks. 4) Do not mix CHARACTER and non-character types in a COMMON block. 5) Do not pass as an argument any variable referenced in a COMMON block in both the calling and called subprograms. 6) Initialize COMMON blocks only in BLOCK DATA subprograms. 7) Compile BLOCK DATA subprograms with another program unit in which it is referred to with an EXTERNAL statement. 8) Use EQUIVALENCE with care, and only to economize on storage; avoid aliasing and then only if well commented. XVIII. Input/Output 1) Use error recovery options END=, ERR=, and IOSTAT=, and handle all such conditions gracefully. 2) Position a FORMAT statement immediately following its reference. Position FORMAT statements that are used more than once at the end of the subprogram. 3) Use implied DO rather than DO loops. 4) OPEN all files with STATUS = 'UNKNOWN' unless otherwise is required. BIBLIOGRAPHY The contributions of the following individuals are gratefully acknowledged. Many laudable suggestions were unilaterally discarded solely in the interest of keeping this guide as brief as possible. A style guide by its very nature is subjective. The primary intent of this guide is draw attention to some of the intricacies of coding in the interest of encouraging consistency. Bierman, Keith, personal correspondence, <8911010025.AA06492@chiba.sun.com> (31 Oct 1989) and <8911160932.AA05443@chiba.sun.com> (16 November 1989). Caffin, R. N., "More on Fortran Coding Conventions," Fortran Forum 3:3 (ACM, December 1984). Calhoun, Myron A., personal correspondence, <8911061456.AA28516@harris.cis.ksu.edu> (6 November 1989). Cox, Robert W., personal correspondence, <8910311522.AA03009@ilmarin.c3.lanl.gov> (31 Oct 1989). Horsfall, Dan, personal correspondence, <9008222053.AA28106@dash.udev.cdc.com> (22 Aug 1990). Liesenfeld, Ulrich, personal correspondence, <1955:uli@analyt.chemie.uni-bochum.dbp.de> (16 November 1989). Metcalf, Michael, FORTRAN Optimization (New York: Academic Press, Inc., 1982). Metcalf, Michael, "FORTRAN 77 Coding Conventions," ForTec Forum 2:4 (ACM, December 1983). Miller, Geoff, "Bureau of Transport Economics Computer Users Guide, Attachment 1 - FORTRAN Programming Standards" (Canberra: Computer Centre, Australian Defence Force Academy, 8 August 1986). Montgomery, Peter, personal correspondence, <8910311752.AA20351@sonia.math.ucla.edu> (31 Oct 1989). Spector, Walter, personal correspondence, <9008022256.AA16278@sequoia.cray.com> (02 Aug 1990). Watson, Ian, personal correspondence, <8912061409.AA05518@Kodak.COM> (6 Dec 1989). APPENDIX I. Loop Constructs 1) iterative DO 10 I = 1, iterations . . . 10 CONTINUE Do not modify the loop variable. 2) while 10 CONTINUE . . . IF ( condition ) GOTO 20 . . . GOTO 10 20 CONTINUE 3) do-while or repeat-until (all statements in loop execute at least once) 10 CONTINUE . . . IF ( condition ) GOTO 10 APPENDIX II. Suggested Compiler Options 1) VAX FORTRAN: /check=(bounds,overflow)/g_float/standard/warnings=all 2) MS fl: /4I2 /4Yd /Ox (/G2 for 80286/386 instructions) 3) Sun f77: (fp is floating point hardware option, e.g., 68881 j is optimization level) Sun3 f77 version 1.2 and earlier: -ansi -ffp -u -Oj foo.f /usr/lib/fp/libm.il /usr/lib/libm.il -lm Sun3 f77 version 1.3: -ansi -fast -u -O3 foo.f -lm Sun4 f77 prior to version 1.2: -ansi -u -Oj foo.f /usr/lib/f77/libm.il -lm Sun4 f77 version 1.2: -ansi -u -Oj foo.f /usr/lib/f77/libm.il /usr/lib/f77/libm.il -lm (and use -dalign if DOUBLE PRECISION is used; may cause core dump if code is not all double word aligned) Sun4 f77 version 1.3: -ansi -fast -u -O3 foo.f -lm 4) Convex fc: -ep i -Oj (i is number of processors and j is optimization level) APPENDIX III. Example notes on example: 1) The dividing lines end in column 72, to serve as a visual aid when working with editors that do not display cursor position. 2) The 'Units' field is useful in physical applications; other parameter properties may be of interest in other applications. 3) Arguments and common block entities that are referenced in the subprogram are listed in the prologue. Each may be classified as IN, OUT, or INOUT mode, following the Ada practice as shown below. A variable is defined if its value is changed, such as by assignment. A variable is used if its value, on entry to the subprogram, is referenced. mode define use ---- ------ --- IN not allowed allowed OUT allowed not allowed INOUT allowed allowed SUBROUTINE TEUpCase (String) * * ================== Prologue ========================================== * * Purpose: * Convert a string to all upper case characters. * * History: * Version Programmer Date Description * ------- ---------- ---- ----------- * 1.0 D. Levine 03/16/89 created * 1.1 D. Levine 12/11/89 changed to INDEX into * constant string from adding * 'A' - 'a' to each char. * * IN args/commons Units Description * --------------- ----- ----------- * * OUT args/commons Units Description * ---------------- ----- ----------- * * INOUT args/commons Units Description * ------------------ ----- ----------- * String N/A string to be converted * * Processing: * For each character in String, check to see if it is lower case by * finding its INDEX in the string [a..z]. Then, replace with the * character at the same position in the string [A..Z]. * * Special requirements: * none * * ------------------ Include files ------------------------------------- * ------------------ Constant declarations ----------------------------- * ------------------ Argument declarations ----------------------------- CHARACTER*(*) String * ------------------ Global/External declarations ---------------------- * ------------------ Local declarations -------------------------------- INTEGER Pos, I CHARACTER*26 lowers, uppers DATA lowers / 'abcdefghijklmnopqrstuvwxyz' /, & uppers / 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' / * ------------------ Code ---------------------------------------------- DO 100 I = 1, LEN (String) Pos = INDEX (lowers, String(I:I)) IF ( Pos .NE. 0 ) String(I:I) = uppers(Pos:Pos) 100 CONTINUE RETURN END