List-directed output

The list-directed output statement

print *, 1, int(2,kind=selected_int_kind(10)), 3., 4.d0
end

yields five different outputs from five different Fortran compilers:

           1                    2   3.00000000       4.0000000000000000
           1                     2   3.000000       4.00000000000000
            1                        2    3.000000
    4.000000000000000
 1 2   3.0000000   4.0000000000000000
 1 2 3.000000000 4.00000000000000000

In the absence of a de facto common standard, and in the belief that needless spaces and zero digits don’t contribute anything of value to the output, I’m producing this from f18:

 1 2 3. 4.

The floating-point values are in “minimal” form – the fewest digits needed to recreate the same binary value after input into the same kind of REAL.

Sound good? Any objection?

(EDIT: Subclause 13.10.4 applies, paragraphs 4-6; I’m reading it as if I can choose ‘w’, ‘d’, &c. for each value rather than using constant ‘w’, ‘d’, &c. for all values.)

1 Like

What are you asking for here? Most of the time you will have not perfectly fitting floating point numbers. It is more likely you will have numbers like 4.000000001 depending how the internal binary representation of a real number converts to decimal representation after rounding as appropriate.

In the case of gfortran list directed we choose a width such that if you write out a value and read it back in, you will get the same internal binary representation as close as possible. We chose to use a fixed width which that fortran standard states is processor dependent.
If you want finer control, use formatted output, that is what it is for. You can use the g0 specifier for a bit more generalized output.

 ie.   print '(g0)', 42.0

I really do not understand what you want to do, so clarify some if possible. I tried this:

print ‘(*(1x,g0.1))’, 1, int(2,kind=selected_int_kind(10)), 3., 4.d0
end

and it gives:

$ ./a.out
1 2 3. 4.

1 Like

While the compact form is, well, compact, a drawback is that you do not see the difference between single and double precision values. In that respect the last type of output combines compactness and type information.

To make things even more convoluted: I have used list-directed output to quickly print my data and relied on a representation of a constant width (for reals) to examine the values. Admitted, that was shear laziness.

Also: list-directed is meant for quick-and-dirty output, the precise formatting is expressively left to the discretion of the compiler developers. So the lack of a standard is exactly on purpose.

In response to @Arjen comment about type info, only the first 3 truly preserve the type information. One can see the kind of real by the number of digits, and the kind of integer by the amount of leading whitespace (granted that’s harder to see). So if you want to preserve all type information, one of those is prefered.

I personally don’t find preserving the type information to be particularly useful to users. As a user of some program I would consider the internal representations to be an implementation detail I shouldn’t have to care about. Round trip preservation is usually important, so that should be supported (i.e. don’t truncate or round). Your proposed format does just that (I assume), while being compact. That’s what I would prefer.

As a matter of style, I prefer not to leave the dangling decimal point. I think those should have one trailing zero to make the decimal point stand out more.

Yes, I like the compact output:

1 2 3. 4.

and I wouldn’t mind one trailing zero for whole real numbers as Brad suggested:

1 2 3.0 4.0

For single precision reals, I think all whole numbers smaller than 2^{23} should be represented exactly without trailing zeros. Of course, only the trailing zeros can be safely omitted from the output.

1 Like

Indeed, aligned output is nice. However, as a user knowing that list-directed output format is up to the compiler, I don’t expect alignment, especially if the code is to be compiled with different compilers.

I see how both aligned and compact formats can be nice depending on what I’m doing. So, currently I prefer compact output by default, and a --align-list-directed-output as a compiler flag.

1 Like

@pmk, for all the differences in the five different compilers you tried, you will note the floating-point output is essentially all the same, 3.00000000 and 4.0000000000000000.

And you may or may not be surprised to know many of your users (and who are likely not here) will have the same expectations with floating-point list-directed output with f18. Thus user experience and consistency with expectations can be a factor for you. Luckily the standard gives you the freedom to chart your path.

It was not clear to me you were implementing another Fortran compiler. In that case no objections on my part.

FYI: There was a discussion on the Fortran newsbook site about this topic. As I remember it anyone writing larger amounts of information preferred the alignment where each type is given enough space to print *, huge(I). So Integer values in particular seem very ugly when just printing a few values. The very same format mentioned here seemed to be what everyone thought was easy enough to use as an alternative when you wanted compact output as I remember it, with the caveat that produces totally different output for complex values. Tied up at the moment but I can find it if you are not familiar with the site. I have a hard time remembering it right now but I actually thought a nice solution would be that PRINT *, wrote “compact” but that WRITE(*,*) wrote with fields wide enough for any value of that type (ie. in “column” mode). The standard is pretty flexible with what list-directed output produces. Now-adays (there was no great alternative before g0) I use ‘(*(g0,1x))’ a lot, enough so I have a mini-module with a few pre-defined formats. So just writing
write(*,gen)a,b,c,string,i,j,k
gives me the compact format; but I like the (a,b) format for complex values and that doesn’t produce that.

PS:
The module also defines some little functions so things like
write(*,gen)ERROR(),a,b,c
writes a prefix of ‘ERROR:’ in white on a red background on ANSI terminal emulators if output is not a TTY; which shows I like overkill.

PSS:
There were several discussions about the leading space that list-directed output always produces too.