What is the one-size-fits-all format for numeric number output?

Dear all,

A quick question, when I output numeric number, I usually use format such as g15.7. It usually works fine. The format g should automatically take care of the format.
However the problem is that, if the number is like something times 10^-xxx or so, it will show things like, eg,

1.610456-162

However, the correct format should be really

1.610456E-162

The letter E is missing.
So if I save a number as 1.610456-162 in the txt file, and read this txt file using other programs such as Origin Pro, it cannot recognize what is 1.610456-162.

I wonder, is there a one-size-fits-all format for numeric number output?

Thanks much!

It is up to you to read Fortran manuals and the documentation of your compiler regarding how it treats various output format specifiers. I don’t think that there is a “one-size-fits-all” format specifier.

For the numbers that you showed, try 1P,G12.5E3.

Regarding your claim, " However, the correct format should be really 1.610456E-162 ", you are wrong, since the string is already 13 characters long and, had the number been negative, the string would be 14 characters long, whereas you specified a field width of 12.

1 Like

The last I was aware, admittedly a while ago, it was relatively easy to import delimited data (comma-separated, etc.) into Origin Pro.

If that is still case, it may be worth for you to look at the Fortran CSV Module by @jacobwilliams for writing CSV files of your work and consider using such files to import your data into Origin Pro:

Library solutions such as above can help you bypass a lot of the intricacies with data edit descriptors and custom data and file formats, etc. and instead focus on your domain expertise with the science.

1 Like

This feature allows large exponents to be printed in the narrow exponent field. Otherwise the exponent, or the whole number field, would need to be filled with asterisks, and no information at all would be written to the output field. There are two solutions. Either tell the program that reads the data how to read the floating point numbers correctly without the e, or change the output format to have a larger exponent field so there is room for the e. In your case, something like g15.7e3 should work where you say explicitly that there are three digits in the exponent field. If you don’t need to maintain a fixed 15-character field width, then try something like g0.7. This prints the 7 significant digits, but the field width expands as necessary for the optional leading sign, 0, and E characters.

The fortran format fields are a language by themselves that allow the programmer both enormous flexibility and somehow simultaneously are frustratingly restrictive.

1 Like

In the standard, I think the discussion on how list-directed output is generated is very informative. In addition to the libraries mentioned, note stdlib has functions for converting floats to strings which can let you compose complex output with a format where the format only has to handle A formats (because you already converted the numbers to strings with the functions) ; and the list-directed output (using an asterisk for a format) may already be doing this for you; and that you can compose a line from multiple write statements using ADVANCE=‘no’ and/or use STREAM I/O when a single Format is not doing what you want (ie. you can “divide and conquer” when creating complex output lines.

1 Like

According to @milancurcic (link) the edit descriptors

  character(*), parameter :: &
    FMT_INT = '(*(i0,1x))', &
    FMT_REAL_SP = '(*(es15.8e2,1x))', &
    FMT_REAL_DP = '(*(es24.16e3,1x))', &
    FMT_REAL_QP = '(*(es44.35e4,1x))', &
    FMT_COMPLEX_SP = '(*(es15.8e2,1x,es15.8e2))', &
    FMT_COMPLEX_DP = '(*(es24.16e3,1x,es24.16e3))', &
    FMT_COMPLEX_QP = '(*(es44.35e4,1x,es44.35e4))'

allow exact round-trip of data for real32, real64, and real128 with gfortran and ifort, with minimum whitespace between the columns. Exact format also allows aligned columns for easier reading.

1 Like

Yes, and you can even simply do

use stdlib_io, only: FMT_INT, FMT_REAL_SP, FMT_REAL_DP, FMT_COMPLEX_SP, FMT_COMPLEX_DP

using stdlib (specs) without defining the parameters yourself.

4 Likes

Thank you all. The std library’s fmormat is very cool!

I see there are * symbol there which is great.
Just wonder does std lib have format without * ? like

  character(*), parameter :: &
    FMT_INT_1 = '((i0,1x))', &
    FMT_REAL_1 = '((es15.8e2,1x))', &
    ...

Because if there are * symbol, if I do

print "( 'Show 1 INT 2 DP :  ' , ("//FMT_INT//", "//FMT_REAL_DP//") )", 100, 256.248, 456.147

it will have compiler error saying that the unlimited symbol * can only be used once. Because both FMT_INT and FMT__REAL_DP contains *, so there are two * here, so complier error.

In the case above, I just need,

print "( 'Show 1 INT 2 DP :  ', ("//FMT_INT_1//", "//FMT_REAL_DP//") )", 100, 256.248, 456.147

Unfortunately, because the formats require a surrounding pair of parentheses, you cannot concatenate individual formats together into one single format string. That requirement dates back to f77 when character strings were introduced into the language, and that requirement was itself based on the earlier format statements which required the parentheses. So that makes it one of those things that would be difficult to change in future revisions of the language. If you want to print an integer and a real in a single write statement, then just use a format that does that. If you are willing to do it in multiple write statements, then you can do that with canned format strings using the appropriate sequence of advance=‘no’ and advance=‘yes’ (which is the default).

1 Like

The ES format seems cannot display integer correctly?

Eg, if I do ,

print "( 'show number fancy =' , ("//FMT_REAL_DP//") )", 100

I got a wrong display

show number fancy = 1.4012984643248171E-043 ! should be 100

However if use 100.0 instead of 100 it shows the 100 correctly.

Please pay some attention to what you are trying to do. The E and ES formats are for real type variables and expressions, not integers.

1 Like

Yeah, but I mean wouldn’t it be better if E and ES at least can show integer in a float number format, instead of some weird number such as showing 1.4012984643248171E-043 instead of 100?
I mean at least the G format can show both integer and float correctly.

I mean ES is for scientific notation basically, scientific notation does not display 100 as 1.4012984643248171E-043 :rofl:

1 Like

The integer value 100 is stored in a bit pattern. If you interpret that bit pattern as a real number, then its value is that number that is printed. As others have stated, the F, E, and ES (and more recently EX) formats are for real numbers. The I format prints integers. The more recent G formats print both integer and real values. The O and Z formats originally printed the bit patterns for integer values, but more recently print also the bit patterns of real numbers. So all of these things are different, and the programmer can choose which is appropriate, and how to set the field widths, number of digits that are printed, and so on.

If you do want to display an integer value with scientific notation, then you must convert it first. Something like

write(*,‘(es11.4)’) real(i)

will work.

1 Like

There is nothing weird about the number 1.401…E-043. It has a 32-bit IEEE representation of Z’00000064’, which is the same as the hexadecimal representation of the decimal integer 100.

It is the programmer’s responsibility to match format specifiers with list items and to choose formats that are appropriate for displaying the corresponding list items. In this case, the programmer failed to live up to that responsibility.

Here is a program with a similar error. There is nothing weird about its output, either.

print '(A1)',100
end

Some Fortran processors may, perhaps on request, check for such errors, but the Fortran Standard does not require that such mismatches should be detected and reported.

1 Like

Thank you all.
Well, yeah, hopefully the compiler can at least give a warning if the there are some format mismatch happens. like if I use es24.16E3 to display 100, it should give me a warning.

On the other hand, is there any chance we can make a
GS
Format which can handle integer and float correctly/automatically?
This seems to be more or less close to one-size-fits all solution for formatting numeric numbers, :laughing:

1 Like

I really agree with this, and gfortran seems to give an error when this happens (even without check options), e.g.

print "(es24.16e3)", 100
end
$ gfortran-10 test.f90 && ./a.out
At line 1 of file test.f90 (unit = 6, file = 'stdout')
Fortran runtime error: Expected REAL for item 1 in formatted transfer, got INTEGER
(es24.16e3)
  ^

Is this not the case for your environment…? (e.g. not for ifort or windows?)

RE more flexible formatted output, it might be useful to prepare some local functions overloaded for integers and reals (of different kinds) and returns a formatted string, like to_str(x [,fmt]) (Sometimes I use such a function in my codes.)

1 Like

Thanks @septc . Yes, those function for format conversion can be very useful.
Yeah, intel OneAPI does not seem to give any warning or error even if I enabled warn all,


perhaps there are options to turn on this warning for intel Fortran, just currently did not figure out what is the flag.
Good to know that gfortran can catch such format error :wink:

But those ES xxx.xxx E-3 and PG Format stuff guys mentioned are really great. For now, for me the format G is an OK and safe solution, although did not show scientific notation as nice as ES format, I defined some FMT_G stuff to use, lol.

    FMT_G = '(*(g24.16e3,1x))', &
    FMT_G_1 = '((g24.16e3,1x))', &
    FMT_G_COMPLEX = '(*(g24.16e3,1x,g24.16e3))', &
    FMT_G_COMPLEX_1 = '((g24.16e3,1x,g24.16e3))'

The PG format mentioned by @mecej4, I googled a little, they say that it may change the value of input variable?
If so, I may use G for now since it is safer than PG. Otherwise PG seems can also show scientific notation.

1 Like

The one-size-fits-all format is * aka list-directed I/O. It copes with integer, real and complex numbers of all the kinds available to the compiler, which are often more than the minimum three: kind(1) for integers and kind(1.0) and kind(1do) for real and complex. It worked well for me with this program
when compiled with gfortran, but it gave a run-time error with ifort.

  real(kind(1d0)):: bigin = -huge(1d0), bigout
  character(40) string
  write(string,*) bigin
  write(*,"(A)") string
  read(string,*) bigout
  write(*,*) bigout
end program

The gfortran output was
-1.7976931348623157E+308
-1.7976931348623157E+308

but the ifort output began with
-1.797693134862316E+308
forrtl: severe (64): input conversion error, unit -5, file Internal List-Directed Read

Ifort’s * format for -huge(1d0) was ES23.15E3; gfortran’s was ES24.16E3. Both rounded the answer correctly but the effect on reading the string was drastically different.

1 Like

The G edit descriptor can be used for anything, integer, real, logical, or even character. However, it sometimes wastes space in the output record, so often you want to use something more specific. After all, you (and the compiler) always know what you are writing, so the programmer can be very specific with field width, exponent digits, optional signs, and so on.

There are two edit descriptors for floating point values that I have long wanted to be defined. One is to specify the field width, and the edit descriptor would write out the value in some kind of F or E format that shows the maximum number of digits within that width. That means all optional + signs are eliminated and the exponent width is minimized or even eliminated if appropriate. The other is to specify the number of digits, and the compiler chooses some kind of F or E format that ends up with the minimum field width.

It is somewhat surprising that these two edit descriptors were not defined decades ago, and even now are not available. I think they would certainly be used frequently by programmers if they were available.

1 Like

Please consider also this stdlib draft PR by @zoziha, very promising!

1 Like