Recently, a customer wrote that he wanted to write out the values of an array, all in one line, where the number of elements was not known at compile time.  His first attempt at this was:

write (30,'(2x,f8.2)') array

and he was dismayed to find each element written on a new line.  He had then tried this variant:

write (30,'(2x,f8.2)',advance='no') array

reasoning that the “advance=’no'” would prevent the new lines.  No good – still one element per line.

Another user suggested this:

write (30,'(2x,(f8.2))') array

but this was worse in that the first line had two spaces before the number, but subsequent lines did not.

What’s happening here?  What is the best way to write a format that will give a specific format to a variable number of values?

Bouncing off a hard, rubber wall

The odd behavior is due to a little-understood Fortran language feature called “format reversion”.  This dates back to at least Fortran 77, if not Fortran IV, and specifies what happens when you get to the end of the format but still have items to process.  A new record is started, (technically, it behaves as if a ‘/’ edit descriptor was processed), and “format control then reverts back to the beginning of the format item terminated by the last preceding right parenthesis… If there is no such preceding right parenthesis, format control reverts to the first left parenthesis of the format specification.”

If we take the original format above,

'(2x,f8.2)'

and assume that the array has three elements, this ends up being equivalent to:

'(2x,f8.2/2x,f8.2/2x,f8.2)'

because there is no preceding right parenthesis, so control reverts back to the beginning of the format and three records will be created.  Even if advance='no' is added, we’ll get three records because “non-advancing I/O” is about what happens at the end of the I/O statement, not the middle of it.

Now let’s look at the proposed fix:

'(2x,(f8.2))'

This ends up being equivalent to:

'(2x,f8.2/f8.2/f8.2)'

as the end of the (f8.2) group is the preceding right parenthesis, so we revert to the beginning of that group. By the way, if there is a repeat count on that group, then the repeat count gets reused.

What to do, what to do

In Fortran 2003, there’s no really good way of generally attacking this problem.  If you have an idea of an upper limit for the number of elements, you could specify a large repeat count on the group, such as:

'(1000(2x,f8.2))'

This assumes there won’t be more than 1000 elements, but is somewhat ugly.  And no, you can’t put a PARAMETER constant in for the 1000.  Some people will suggest that you construct a format at run-time by writing into a character variable and using a run-time format, like so:

character(80) :: fmt
...
write (fmt,'(A,I0,A)') '(',n,'(2x,f8.2))'
write (30,fmt) array

where “n” is the number of elements in the array.

Another option is to use an extension called Variable Format Expressions (VFEs).  This was created by DEC in the 1970s and it earned the enmity of Fortran compiler writers everywhere who were pestered by their customers to support it as well.  With VFEs, you can enclose an integer expression in angle brackets and the value of the expression will be used in the format.  For example:

write (30, '(<n>(2x,f8.2)') array

Intel Fortran, of course, given its DEC heritage, supports VFEs, but I don’t recommend their use if you have other reasonable options.

Look to the future

Fortran 2008 solves this problem with a feature called the “unlimited format item” where a * can be used as a group repeat count.  Its effect is “as if its enclosed list were preceded by a very large repeat count”.  For example:

'(*(2x,f8.2))'

This lets you avoid having to write a specific large number as the repeat count and makes it more obvious what is going on.  [Edit – Intel Fortran added support for the unlimited format item in Intel [Visual] Fortran Composer XE 2011 (compiler version 12.0).]

The Doctor’s advice, should you find yourself in this situation, is to use the unlimited format item.  You may find some cases where VFEs simplify your coding, so feel free to use them if there is no reasonable standard-conforming alternative.

Got suggestions for future Doctor Fortran columns?  Let me know!

(Originally posted at Intel Developer Zone, copied with permission)

Write Your Comments

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe to Doctor Fortran

Subscribe to Doctor Fortran

Loading