Tabular output in Fortran, that is robust against unexpected values? - 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

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

Related

How do Fortran and MPI_Reduce deal with integer overflow?

Following this thread, I want to cast a single / double precision real number "AA" into an integer "II" to compute the checksum of a distributed variable.
Following comments, I have used the intrinsic 'transfer' and rewritten completely this post. Below is a small fortran module that can be used to compute checksums of distributed arrays which depends on the library 2DECOMP&FFT. The module seems to work on my workstation (gfortran 4.9.2, openmpi 1.6.5, 4 processors). Any comment / remark that may improve the portability of the code will be highly appreciated. Main question regarding portability is: do fortran and MPI_reduce deal with integer overflow in the same way according to standards?
module checksum
use MPI
use decomp_2d, only : mytype, nrank, &
xsize, ysize, zsize, &
transpose_x_to_y, transpose_y_to_z, &
transpose_z_to_y, transpose_y_to_x
implicit none
private ! Make everything private unless declared public
real(mytype), parameter :: xx=1.
integer, parameter, public :: chksum_size = size(transfer(xx,(/0,0/)))
integer, dimension(chksum_size) :: chkr1, chkr2, chkr3
logical, save :: chksum_is_working
! Temporary work variables / arrays
integer :: code
integer, dimension(chksum_size) :: tmprchk
public :: init_chksum, chksum, equal_chksum
contains
!
! Function to compute the checksum of a real 3D array var
!
function chksum(var,nx,ny,nz)
integer, intent(in) :: nx, ny, nz
real(mytype), dimension(nx,ny,nz), intent(in) :: var
integer, dimension(chksum_size) :: chksum
tmprchk = sum(transfer(var,(/0,0/)))
call MPI_ALLREDUCE(tmprchk,chksum,chksum_size,MPI_INTEGER,MPI_SUM,MPI_COMM_WORLD,code)
end function chksum
!
! Subroutine to make sure input arrays have the same checksum
! First / second / third array are in X / Y / Z pencil
! If switch is provided, reference array is var3.
! Otherwise, reference array is var1
!
subroutine equal_chksum(var1, var2, var3, switch)
real(mytype), dimension(xsize(1),xsize(2),xsize(3)), intent(inout) :: var1
real(mytype), dimension(ysize(1),ysize(2),ysize(3)), intent(inout) :: var2
real(mytype), dimension(zsize(1),zsize(2),zsize(3)), intent(inout) :: var3
logical, optional, intent(in) :: switch
if (chksum_is_working) then ! compute checksums
chkr1 = chksum(var1,xsize(1),xsize(2),xsize(3))
chkr2 = chksum(var2,ysize(1),ysize(2),ysize(3))
chkr3 = chksum(var3,zsize(1),zsize(2),zsize(3))
else ! generate checksums
chkr1 = 1
chkr2 = 2
chkr3 = 3
endif
if (present(switch)) then
if (any(chkr3.ne.chkr2)) call transpose_z_to_y(var3,var2)
if (any(chkr3.ne.chkr1)) call transpose_y_to_x(var2,var1)
else
if (any(chkr1.ne.chkr2)) call transpose_x_to_y(var1,var2)
if (any(chkr1.ne.chkr3)) call transpose_y_to_z(var2,var3)
endif
end subroutine equal_chksum
!
! Subroutine used to check we have a working checksum
!
subroutine init_chksum(var1,var2,var3)
real(mytype), dimension(xsize(1),xsize(2),xsize(3)), intent(out) :: var1
real(mytype), dimension(ysize(1),ysize(2),ysize(3)), intent(out) :: var2
real(mytype), dimension(zsize(1),zsize(2),zsize(3)), intent(out) :: var3
! Same random data inside all arrays
call random_number(var1)
call transpose_x_to_y(var1,var2)
call transpose_y_to_z(var2,var3)
! Compute checksums
chkr1 = chksum(var1,xsize(1),xsize(2),xsize(3))
chkr2 = chksum(var2,ysize(1),ysize(2),ysize(3))
chkr3 = chksum(var3,zsize(1),zsize(2),zsize(3))
! Check checksums
if (any(chkr1.ne.chkr2).or.any(chkr1.ne.chkr3)) then
chksum_is_working = .false.
if (nrank.eq.0) print *,'Checksums based on integer overflow do not work'
else
chksum_is_working = .true.
endif
end subroutine init_chksum
end module checksum
do fortran and MPI_reduce deal with integer overflow in the same way according to standards?
Neither the Fortran standard nor the MPI 3.0 standard even mentions integer overflow so you are at the mercy of the implementers.
However, I see you are only using integers of default kind, use a larger integer kind for intermediate results and you can implement your own overflow detection.
Integer overflow is not defined by the Fortran standard. In C signed integer overflow is undefined behaviour.
If you enable undefined behaviour santizations in gfortran, your program will be stopped with an error message. (Happened to me when I was using a 3rd party random number generator.)
You can perform the operation using larger integers and crop the result or call a C function which uses unsigned integers. Integer overflow is only well defined for signed integers.

How do I make all the calculations in double precision in fortran?

In the Fortran code given below, I have made all numbers involving calculation of PI as double precision but the value of PI I get is just a real number with a large number of zero or 9 at the end. How do I make the program give PI in double precision? I am using gfortran compiler.
!This program determines the value of pi using Monte-Carlo algorithm.
program findpi
implicit none
double precision :: x,y,radius,truepi,cnt
double precision,allocatable,dimension(:) :: pi,errpi
integer :: seedsize,i,t,iter,j,k,n
integer,allocatable,dimension(:) :: seed
!Determining the true value of pi to compare with the calculated value
truepi=4.D0*ATAN(1.D0)
call random_seed(size=seedsize)
allocate(seed(seedsize))
do i=1,seedsize
call system_clock(t) !Using system clock to randomise the seed to
!random number generator
seed(i)=t
enddo
call random_seed(put=seed)
n=2000 !Number of times value of pi is determined
allocate(pi(n),errpi(n))
do j=1,n
iter=n*100 !Number of random points
cnt=0.D0
do i=1,iter
call random_number(x)
call random_number(y)
radius=sqrt(x*x + y*y)
if (radius < 1) then
cnt = cnt+1.D0
endif
enddo
pi(j)=(4.D0*cnt)/dble(iter)
print*, j,pi(j)
enddo
open(10,file="pi.dat",status="replace")
write(10,"(F15.8,I10)") (pi(k),k,k=1,n)
call system("gnuplot --persist piplot.gnuplot")
end program findpi
Your calculation is in double precision, but I see two issues:
The first is a systematic error... You determine pi by
pi(j)=(4.D0*cnt)/dble(iter)
iter is at most 2000*100, so 1/iter is at least 5e-6, so you can't resolve anything finder than that ;-)
The second issue is that your IO routines print the results in single precision! The line
write(10,"(F15.8,I10)") (pi(k),k,k=1,n)
and more specifically the format specifier "(F15.8,I10)" needs to be adjusted. At the moment it tells the compiler to use 15 characters overall to print the number, with 8 digits after the decimal point. As a first measure, you could use *:
write(10,*) (pi(k),k,k=1,n)
This uses 22 characters in total with all 15 digits for double precision:
write(10,"(F22.15,I10)") (pi(k),k,k=1,n)

FORTRAN 77 - packing 8 a1 characters into an integer*8

An odd issue here. I am converting legacy F77 code that packed 8 a1 characters into a 64 bit integer, and the code worked back in the day. But in today's world to get 8 characters in a word I need to move to integer*8 variables, and the code is failing in that case. It works until it's packed 4 characters, but character 5 simply rotates around and overwrites the first packed character. Here is a sample output for "hello world”, of note is that the integer*8 variable OUTBUF suddenly shrinks and appears to convert to an integer*4 variable too:
hello world
in='hello world '
cn= 8,k=1,j='h',outbuf(k)='h '
cn=16,k=1,j='e',outbuf(k)='he '
cn=24,k=1,j='l',outbuf(k)='hel '
cn=32,k=1,j='l',outbuf(k)='hell'
cn=40,k=1,j='o',outbuf(k)='oell'
# SPACE
cn= 8,k=2,j='w',outbuf(k)='w '
cn=16,k=2,j='o',outbuf(k)='wo '
cn=24,k=2,j='r',outbuf(k)='wor '
cn=32,k=2,j='l',outbuf(k)='worl'
cn=40,k=2,j='d',outbuf(k)='dorl'
# SPACE
I've distilled the code to this snippet, anyone know F77 anymore and see what’s happening? thanks.
program cow
implicit integer*8 (a-z)
integer inbuf(72)
integer*8 outbuf(40)
c changing the integer*1 to integer*8 fixes the problem !!!!!!!!!
integer*1 j
read 99, inbuf
99 FORMAT(BZ,72a1)
print 1, inbuf
1 format("in='",72a1,"'")
IP=0
k = 1
DO 100 I=1,40
OUTBUF(I)= 8H
100 CONTINUE
200 IP=IP+1
IF(IP.GT.72) GO TO 6000
J= INBUF(IP)
IF(J.EQ." ") GO TO 6000
CN = (MOD(CP,8)+1) * 8
outbuf(k) = outbuf(k) .and. (.not. lshift('377'O, (cn-8)))
OUTBUF(K) = OUTBUF(K) .OR. lSHiFT( j,(CN-8))
print 4301, cn, k, j, outbuf(k)
4301 format("cn=",i2,",k=",i1,",j='",a1,"',outbuf(k)='",a8,"'")
CP=CP+1
GO TO 200
6000 continue
print *,'# SPACE'
call exit
end
Note that this code is for little endian machines.
" So ignore the code, the task at hand - in FORTRAN 77 - is to pack 8
a1 characters into an integer*8 variable. "
Ok, but I will have to wash my hands after this:
INTEGER*8 i
CHARACTER*8 ch
EQUIVALENCE (i, ch)
ch = "abcdefgh"
Of course, transfer is nicer, but certainly not FORTRAN 77.
This is quite horrible code to do something trivial in newer Fortran
(since F77...).
You can replace the whole routine by something like
integer, parameter :: llen=72
character(len=llen) inbuf
character(len=llen) output
integer :: i,k
read (*,'(BZ,A)') outbuf
k = 1
do i=1,llen
if (inbuf(i) /= ' ') then
outbuf(k) = inbuf(i)
if (mod(k,8) == 0) then
! Some output goes here
endif
k = k + 1
end if
end do
OK, here is the second answer; not preferred, it is really better to rewrite the program using characters.
Caveat: integer*8 is non-standard.
program main
character(len=8) :: c
integer*8 output(10)
c = "12345678"
call foo(output,10,c)
write (*,'(Z16)') output(1)
end program main
subroutine foo(a,n,b)
integer*8 :: a(1)
character(len=8) :: b
a(1) = transfer(b,mold=a(1))
end subroutine foo
As I mentioned, this input parser took Hollerith data and compared it against Hollerith data in a database, in a case-insensitive manner. The code was written for 60-64 bit, big-endian machines, and used hardware specific shifts, masks, ANDs and ORs to pack the A1 input characters into an A8 INTEGER word for comparison against the database.
And although I did manage to adjust all those shifts, masks and bitwise operations to work on 64-bit little-endian machines (x86_64), it was a PITA, and only worked with pgf77 and ifort, gfortran was having none of that crap! So this is what I came up with, much nicer, hardware independent, and works on all the compilers available to me:
The parser still accepts A1 input characters stored in INTEGERs (80a1), and returns packed A8 characters in INTEGER*8 words, for backwards compatibility.
It converts 80a1 input data to CHARACTER*80 variable IN0:
write( in0, '80a1' ) (inbuf(i), i=1,80)
Since this is all ASCII data, I used this code to lower-case everything in IN0 to a new character*80 variable IN:
do i = 1, len(in0)
#if (! defined __GFORTRAN__)
j = ichar(in0(i:i))
if (j>= ichar("A") .and. j<=ichar("Z") ) then
in(i:i) = char(ichar(in0(i:i))+32)
else
in(i:i) = in0(i:i)
end if
#else
j = iachar(in0(i:i))
if (j>= iachar("A") .and. j<=iachar("Z") ) then
in(i:i) = achar(iachar(in0(i:i))+32)
else
in(i:i) = in0(i:i)
end if
#endif
end do
It was then a simple matter to loop through the CHARACTER*80 variable IN, collect non-space characters into the character variable WORD, and then encode them into INTEGER*8 array OUTBUF:
read( word(1:8), ‘a8' ) outbuf(outbufc)
Thanks all.

Concatenate two integers

What is the best way to concatenate two integers to an integer in Fortran?
integer a = 999
integer b = 1111
integer c should be 9991111
Thanks,
SM.
Here is an example code that does what you need. It writes integers into character strings, trims and concatenetes them, and then reads the result integer from concatenated character string:
integer :: a,b,c
character(len=99) :: char_a,char_b,char_c
a = 999
b = 1111
write(unit=char_a,fmt=*)a
write(unit=char_b,fmt=*)b
char_c = trim(adjustl(char_a))//trim(adjustl(char_b))
read(unit=char_c,fmt=*)c
print*,c
end
Edit: Note that this example is general for any integer lengths, assuming they fit into their respective kind (no integer overflow).
You can use the information of the order of the number:
integer :: a = 999
integer :: b = 1111
integer :: c
c = a * 10**(ceiling(log10(real(b)))) + b
write(*,*) c
Your best bet is to use internal files to convert your two integers to a character, and then convert this back to an integer.
There is no intrinsic procedure for converting a numeric value to a character/string representation. See this discusson on Fortran Wiki for more information (see the part headed "Note").
As an example, in your case you could use the following:
program test_conversion
implicit none
integer :: a=999
integer :: b=1111
integer :: c
character(len=7) :: temp
write(temp, '(i3.3, i4.4)') a, b ! You may need to change these format specifiers
read(temp, *) c
print*, c ! This prints 9991111
end program test_conversion
You will have to change the format string if you want different widths of the character representation of your integers.

How do I handle logical statements being applied to integer values with gfortran?

I'm rewriting some code to make a program compile with the gfortran compiler as opposed to ifort compiler I usually use. The code follows:
_Subroutine SlideBits (WORD, BITS, ADDR)
Implicit None
Integer(4) WORD
Integer(4) BITS
Integer(4) ADDR
Integer(4) ADDR1
ADDR1 = 32 - ADDR
WORD = (WORD .And. (.Not.ISHFT(1,ADDR1))) .Or. ISHFT(BITS,ADDR1)
End_
When I compile the above code using the gfortran compiler, I recieve this error:
WORD = (WORD .And. (.Not.ISHFT(1,ADDR1))) .Or. ISHFT(BITS,ADDR1)
Error: Operand of .NOT. operator at (1) is INTEGER(4)
All three of the variables coming into the subroutine are integers. I've looked around a bit and the gfortran wiki states that the gfortran compiler should be able to handle logical statments being applied to integer values. Several other sites I've visited either quote from the gnu wiki or agree with it. This is the first time I've seen this error as the Intel Fortran compiler (ifort) I normally use compiles cleanly.
The comments/answers above "may .Not. be" the correct responses, depending on your ultimate objective.
The likely purpose of that "WORD = .." statement is .NOT. to arrive at a boolean/logical result, but rather to obtain a kind of integer enumerator.
To see this, first "ignore" the bit shifting (iShift() etc), and just look at something like IntR = Int1 .Or. Int2. This will produce a "proper" integer result. The value will depend on not only the values of the int's, but also on their declared "type" (e.g. Integer(1), Integer(2), etc)
That is, the resulting value of WORD will be a "proper" integer; something like "33504" .. or whatever, (likely) .NOT. a 0/1 or -1/0 or .True./.False. etc
If you replace = Int1 .Or. Int2 with = (Int1 /= 0) .Or. (Int2 /= 0) ... you will get an "integer logical" (i.e. 0/1 etc) and WILL NOT produce the
desired enumerator ... if that is what you are looking for.
The .Or. on two Int's is a kind of bit-wise addition that produces a new num based on how the bits align/word size etc.
e.g. 3 == 011, 2 = 010 ... so, 3 .Or. 2 ==> 011 = 3
e.g. 3 == 011, 5 = 101 ... so, 3 .Or. 5 ==> 111 = 7
e.g. 5 == 101, 5 = 101 ... so, 5 .Or. 5 ==> 101 = 5
... similarly the .And. provides a kind of multiplication.
This technique is sometimes used to create enumerators somewhat like the use of powers of two (1,2,4,8...) are used to assign a value. Then, any sum of those
values can be decomposed, for example, into its constituent elements. For instance, if a(1) = 2, and a(2) = 8, then the sum 10 can be decomposed to
show the selections were the 1st and 4th elements of (1,2,4,8,...) etc.
It may help conceptualise this by noting that bit-shifting is like multiplying by 2 (for left shift) and dividing by 2 (for right shift).
BTW, you don't need to restrict to Fortran for this. Whack it into a VBA function and see the result in your spreadsheet VBA does not
have bit shift intrinsics, but they are available ... in any case it will demonstrate the Int1 .Or. Int2 behaviour even without bit shifting, such as
Function TwoIntsOr(Int1 As Long, Int2 As Long) As Long
'
TwoIntsOr = Int1 Or Int2
'
End Function
-- .Or. in Fortran
Function TwoIntsOr(Int1, Int2)
Integer :: TwoInstOr
Integer, Intent(In) :: Int1, Int2
!
TwoIntsOr = Int1 .Or. Int2
!
End Function
).
It is not standard Fortran to apply logical/boolean operators to integer variables. If the goal is a boolean result, the ideal solution would be to convert the types to logical. If, as it appears from casual examination, the code is really doing bit-wise operations, then it would be better to use the IAND and IOR intrinsic functions.
gfortran is expecting booleans for the logical operators and the code is providing integers. Use comparisons with zero instead of logical operators.
WORD = ((WORD /= 0) .And. (ISHFT(1,ADDR1) == 0)) .Or. (ISHFT(BITS,ADDR1) /= 0)
gfortran and ifort use different representations for .true. and .false. values, so it's best to stick to booleans when that's what the code needs. In a conversion from ifort to gfortran I got bit by the former representing .true. as -1 and the latter using 1 for the same purpose, instead of the traditional (C-like) not 0.