Anyone who’s moved beyond a Hello World! on the command line is likely to discover formatted printing. It’s used in almost every modern language I can think of, from Python to Lisp. Here’s an example of a formatted print in C:
printf("I have exactly %i %s", 20, "apples");
Which outputs I have exactly 20 apples.
FORTRAN is in a bit of a stranger spot though. Generally, the most convenient way to print out variables is with list-directed formatting. We perform this with the PRINT*, and WRITE(*, *) statements1:
This can have undesirable consequences. Notice in the above example that there’s a space in front of I. That can be irritating if you’re exacting in your whitespacing standards. In fact, by convention, all of FORTRAN’s list-directed output requires a “blank character” at the beginning of each new line. If you want greater control of whitespacing, you’ll need to use a format specifier:
Like with C’s printf arguments, in FORTRAN we have edit descriptors. In this case, I2 represents an integer, where 2 is the “width” or number of characters that an integer takes up when printing. For instance, when we have two apples, we now have a space in place of a digit:
FORTRAN has a lot of oddities and peculiarities when it comes to dealing with I/O; the more that you experiment, the more you’ll notice weird overlaps and seemingly useless features. So what gives with all of these odd ways to handle printing?
Background
FORTRAN (FORmula TRANslating system, as described in The FORTRAN programmer’s reference manual2), was released by IBM in 1956. It’s ancient by computer science standards3. The fact that it is still relevant nearly 70 years after its creation is a testament, at least in part, to FORTRAN’s runtime performance. Widespread adoption amongst computational mathematicians doesn’t hurt either.
I tell you all this merely for context: FORTRAN has many ways to format I/O, and is unintuitive compared to other languages simply because it is so old. The FORMAT statement dates to the first iteration of the language2. The statement FORMAT(I2 /(E12.4, F10.4)) must work on punch cards just as well as (if not better than) any modern compiler.
Which is why you’ll run into 5 different ways to handle PRINT, or WRITE, or FORMAT statements online. Improvements are present in every iteration of the language, with a million pieces of computing history wedged underneath. This makes compatibility easier, at the cost of being quite confusing for new learners.
My hope is that this post, and the associated web tool, will de-mystify part of your FORMATting options.
The Web Tool
A huge amount of thanks goes to Dr. George W Stagg, whose post on LLVM’s Flang runtime library running in WebAssembly was instrumental to getting the web tool to work. Flang-RT is really the only modern solution we have available for running FORTRAN components on the web.
You can view the tool online. The source code for this tool is available on GitHub.
Disclaimer
This post will not attempt to distinguish between what is or isn’t supported between different FORTRAN standards, since Flang-RT doesn’t make this distinction either. For instance, the following code will compile for most FORTRAN compilers (although most will throw a warning if you set the FORTRAN standard):
Program Main
WRITE(*, "(5HHello)")
End Program
This is despite Hollerith Constants having been deprecated from the FORTRAN standard since FORTRAN 77 (pg. A-2), and removed in FORTRAN 95.
With all that said, let’s talk about the core of FORTRAN’s formatted I/O.
Edit Descriptors
Recall 10 FORMAT("I have", I2, "apples"). I2 is an edit descriptor specifying an Integer Edit of width 2: when we READ(*, 10), we expect an integer represented by at most two characters; when we WRITE(*, 10), we print out an integer of at most two characters.
Simply, edit descriptors describe “edits” that modify how we will either read from or write to different files. A good understanding of how FORMAT works involves understanding a good deal of what edit descriptors are available to us.
Data Edit Descriptors
These are descriptors that describe how to read into or write from variables.
Integers
Iw
w represents the width of the integer in the resulting print:
If the integer exceeds the width, the text will be replaced with *:
Iw.m
m represents the minimum number of characters to be displayed.
Real Numbers
Decimal - Fw.d
As with integers, Fw.d is an edit descriptor:
Frepresents a floating point numberwthe width of the decimal in characters, including the decimal pointdthe number of digits expected after the decimal point
Exponential Form - Ew.d
Note that w always represents a width in characters of the displayed output. So even though 0.3E+01 is comprised of only two digits, it makes up 7 characters in total. So if we were to shrink the number of characters:
Exponential Form - Dw.d
The D functions the same as the E edit descriptor, with one minor cosmetic change:
Scientific Form - ESw.d
Engineering Form - ENw.d
The only difference between engineering notation and scientific notation is that engineering notation uses multiples of three. Contrast with ES of the same number:
Hexadecimal Significand - EX
Digits in Exponent - Ee
Ee can be appended to any real-number edit descriptor that has an exponential component, where E is an edit descriptor of exponential form, and e the number of digits to be shown in the exponent. For instance, in specifying the exponent of Ew.d, we create the edit descriptor Ew.dEe:
Generalized Edit Descriptor - Gw.d
Automatically selects an appropriate underlying data descriptor:
Characters
Character - Aw
You can set a maximum number of characters with Aw. These truncate if width is exceeded:
Hollerith Constants - wH
Before the A format descriptor (introduced in FORTRAN 66), there were Hollerith Constants, which have existed since the first FORTRAN manual2:
Logicals - L
Booleans in FORTRAN:
Logical Width - Lw
You can also set the width of logicals for whatever reason:
Representations
Binary Bw.m
Represents any variable in its binary form:
Octal Ow.m
Represents any variable in its octal form:
Hexadecimal Zw.m
Represents any variable in its hexadecimal form:
Conclusion
FORTRAN’s edit descriptors offer something of a strange deviation to the string formatting that you might be familiar with in other languages. This post should hopefully offer a limited example of how you might be able to begin to work with different data types, at least when it comes to interfacing with variables directly.
For brevity, this post does not cover “Control Edit Descriptors”, which do not directly display variable outputs or inputs. Nor does it cover edit descriptors in a context of recieving input.
If I’ve made a mistake with any of the above, or if you’re interested in seeing more FORTRAN I/O coverage in this space, please reach out! My email is ambiguousname (at) ambiguous.name.
In the meantime, feel free to use the FORTRAN WASM demo for any purposes you might have. Thank you for your time!
Sources
All of the following were utilized heavily when referencing the behavior of FORTRAN functions:
For describing the intended behaviors of FORTRAN, both The Computer History museum’s archive of the first FORTRAN manual and fortran90.org/wg5-fortran.org’s archive of FORTRAN standards were extremely useful.
Since it is also the basis of the web demo, I’ve used the LLVM FORTRAN Runtime docs/source code extensively.
The FORTRAN logo is from FORTRAN’s own GitHub page.
-
PRINT F,andWRITE(*, F)are the same statement. For consistency, we’ll be usingWRITE(*, F)for the rest of this post. ↩ -
The FORTRAN Automatic Coding System for the IBM 704 EDPM: Programmer’s Reference Manual, October 15th, 1956. ↩ ↩2 ↩3
-
My father worked with FORTRAN IV in college. My grandfather has floppy disks of code he commissioned for his ship salvage work in the 1960s. Apparently they would digitize punchcards onto magnetic tape to run on Boeing’s timeshare. Those would be later digitized into the floppy disks we have now. ↩