Default unit of record length when writing unformatted files varies between compiler vendors

Hi all,

Today I realized something strange. See this simple program that writes a real array in a binary file:

 program writeit
   implicit none
   integer, parameter :: n = 32
   real, dimension(n) :: a
   integer :: iunit
   a(:) = 1.
   open(newunit=iunit,file='a.bin',status='replace',access='direct',recl=n*storage_size(1.)/8)
   write(iunit,rec=1) a(:)
   close(iunit)
 end program writeit

To my surprise, while in the gnu, cray and pgi compilers, the file size is, as expected = 32*4 = 128 bytes, with the intel compiler the corresponding file size is four times bigger: 512 bytes. It seems that the intel compiler (and perhaps others) assumes that recl is in units of 4 bytes (link below) unless the code is compiled with a specific flag. Shouldnā€™t the unit defining the record length be standardized? Iā€™m sure there is a good reason for intel to choose this default value, but I do not see it. This has caused me a bit of trouble today.

The solution as most of you know is to use the inquire statement to fetch the record length of the real number, but if one is not careful this can also cause issues: for a long time I was using the inquire statement to fetch the size of a real in bytes, and now I realize why at that time I was also having an issue when using an intel compiler.

I thought this may be useful to some of you who may not be aware of this.

(https://software.intel.com/content/www/us/en/develop/documentation/fortran-compiler-developer-guide-and-reference/top/compiler-reference/data-and-i-o/fortran-i-o/record-length.html)

Hi,

I believe to avoid any troubles about direct access files, you have to use the inquire with the full record and not with a single real. So that, youā€™ll have:

integer :: lrecl
ā€¦
inquire(iolength=lrecl) a(: )
open(newunit=iunit,file=ā€˜a.binā€™,status=ā€˜replaceā€™,access=ā€˜directā€™,recl=lrecl)
ā€¦

1 Like

Actually, this will work:

integer :: rlen
ā€¦
inquire(iolength=rlen) 1.
open(newunit=iunit,file=ā€˜a.binā€™,status=ā€˜replaceā€™,access=ā€˜directā€™,recl=n*rlen)
ā€¦

Still, I believe, it is better to use the full record in the ā€œinquireā€. In particular, when the record is composed of several variables with different types/kinds.

1 Like

The Intel compiler is DEC-heritage, and DEC compilers used the size of a ā€œnumeric storage unitā€ for RECL=. Fortran 77 said:

If the file is being connected for unformatted input/output, the length is measured in processor-dependent units.

This didnā€™t change until Fortran 2003, where it said (and still says):

If the file is being connected for unformatted input/output, the length is measured in file storage units.

Since the older standards didnā€™t nail this down, DEC used, numeric storage units, or the size of an integer. When F2003 changed this, it created an incompatibility that would break many existing programs.

So the solution was to add a new option -assume byterecl that changed the units to ā€œfile storage unitsā€ (or bytes). This is implied when you use -standard-semantics, the option to tell the compiler to change all defaults that conflict with the current standard.

2 Likes

@gardhor I agree!

Thank you @sblionel for making it clear! So it is actually part of the current standard that recl is defined in ā€˜storage unitsā€™. Then I also learned that I have been wrongly assuming that compiler defaults were complying to more recent standards. Best to explicitly use flags that impose them just in case. :+1:

@pcosta, specifically the standard (as of F2003) defines a new storage unit called ā€œfile storage unitā€.

Compilers that change behavior of standard-conforming programs tend to be unpopular. Over the decades, the standard has added language to specify things it previously left unsaid, and sometimes this differed from what implementations decided on and what programmers depended on.

1 Like