Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tabular output in Fortran, that is robust against unexpected values?

Tags:

fortran

In Fortran, is it possible to print data in a tabular manner, without losing information, when more space is needed than specified?

For instance consider the program

! format.f90
program main
  real(8) :: arr(5)
  arr = [0.0, 1.111, 22.22, 333.3, 444444444444444444.44]
  print '(F10.3)', arr
end program main

Then by default the output for the last entry will be replaced by stars, indicating the lack of space.

>> ifort format.f90 -o format.bin
>> ./format.bin
     0.000
     1.111
    22.220
   333.300
**********

By comparison, C-style format specifiers automatically increase the column width when required, e.g.

// format.c
#include <stdio.h>
int main () {
  double arr[5] = {0.0, 1.111, 22.22, 333.3, 444444444444444444.44};
  for(int i=0; i<5; i++) {
    printf("%10.3f\n", arr[i]);
  }
}

>> gcc format.c -o format.bin
>> ./format.bin
     0.000
     1.111
    22.220
   333.300
444444444444444416.000

Is it possible to obtain such behavior in Fortran with built-in features?

Options, that don't fulfill the requirements

G descriptor. The G descriptor allows reliably outputting data in a tabular well-readable format and automatically adds exponentials when needed. However, it also wastes space if the exponentials are not needed and it doesn't line up the comma. For example, when switching F10.3 for G11.4,"¶" (paragraph sign added for emphasis):

>> ifort format.f90 -o format.bin
>> ./format.bin
  0.000    ¶
  1.111    ¶
  22.22    ¶
  333.3    ¶
 0.4444E+18¶

Building a formatting API based on the F0 specifier. The specifier F0.3 would allow variable-width output, but doesn't allow specifying a minimum width. This could be solved using a wrapper function akin to leftpad, but a built-in or widely-used solution would be preferable for a better chance of actually being used in a codebase. As an example:

! format.f90
program main
  real(8) :: arr(5)
  integer :: i
  arr = [0.0, 1.111, 22.22, 333.3, 444444444444444444.44]

  ! more complicated print statement, because 'float2char'
  ! cannot be 'elemental' due to needing the 'alloctable' property.
  print '(A)', (float2char('(F0.3)', 10, arr(i)), i=1,5)

contains

  function float2char(format, width, value) result(r)
    character(:), allocatable :: r
    character(*), intent(in) :: format
    integer, intent(in) :: width
    real(8), intent(in) :: value

    character(64) :: buffer ! better: calculate size from value?

    write(buffer, format) value
    allocate(character(max(width, len_trim(buffer))) :: r)
    r(:) = trim(buffer)  ! (:) needed to prevent reallocation in recent compilers
    r(:) = adjustr(r)
  end function float2char

end program main

>> ifort format.f90 -o format.bin
>> ./format.bin
      .000
     1.111
    22.220
   333.300
444444452740661248.000
like image 254
kdb Avatar asked Nov 25 '25 21:11

kdb


1 Answers

Yes, so in Fortran the fixed-width edit descriptors really are FIXED width. Sometimes useful, often annoying.

One thing you can do is to use the G edit descriptor, which is similar to %g in C, namely that it switches to scientific format when the number is large or small. That allows very large or small values to fit in a fixed width field. Note however that with G editing the d is the number of significant digits, not the number of digits after the decimal point as with F editing. Also it leaves space at the end for the exponent even if the number is in the range that no exponent is needed.

Your example could look like


! format.f90
program main
  real(8) :: arr(4)
  arr = [0.0, 1.111, 222222222222.222, 3.333]
  print '(F10.3)', arr
  print *, 'With G edit'
  print '(G10.4)', arr
end program main

with output

     0.000
     1.111
**********
     3.333
 With G edit
 0.000    
 1.111    
0.2222E+12
 3.333    
like image 65
janneb Avatar answered Nov 28 '25 18:11

janneb