How do Fortran and MPI_Reduce deal with integer overflow? - casting

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.

Related

Issue with boolean operations with MPI (MPICH2) and Fortran

When performing reductions on logical values in Fortran with MPI (MPI_LAND operation), MPICH2 returns invalid values of the logical type, which return .true. to both value and .not.value.
For example, this is the result of an MPI_ALLREDUCE of the logical my_value = rank>=0 in the sample program below:
on CPU 0, the local boolean value is 00000000000000000000000000000001
on CPU 0, the logical and of all values is T; its negation is T
on CPU 0, logical AND bits = 11111111111111111111111111111111 negation bits = 11111111111111111111111111111110
MPI seems to have performed a bitwise AND instead of an operation on the logical variables. MPI bug or am I just doing something wrong here? BTW - note that:
using old-style mpif.h for portability
MPICH2 v1.4.1 + gfortran 10.2.0
program test_mpi_land
implicit none
include 'mpif.h'
! Local variables
integer :: ierror,ncpu,cpuid
logical :: my_bool,all_ok
integer, parameter :: master_node = 0
! Init MPI; get basic info
CALL MPI_Init(ierror)
CALL MPI_Comm_size(MPI_COMM_WORLD, ncpu, ierror)
CALL MPI_Comm_rank(MPI_COMM_WORLD, cpuid, ierror)
! Each MPI process sends .true. to reduction
my_bool = cpuid>=master_node
CALL MPI_AllReduce(my_bool,all_ok, 1, MPI_LOGICAL, MPI_LAND, MPI_COMM_WORLD, ierror)
if (cpuid==master_node) then
print '(A,I0,A,B32.32)', 'on CPU ',cpuid,', the local boolean value is ',my_bool
print '(A,I0,2(A,L0))', 'on CPU ',cpuid,', the logical and of all values is ', all_ok, '; its negation is ',.not.all_ok
print '(A,I0,2(A,B32.32))', 'on CPU ',cpuid,', logical AND bits = ',all_ok,' negation bits = ',.not.all_ok
endif
CALL MPI_Finalize(ierror)
end program test_mpi_land

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

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

integer, do loop, fortran, error

I have the following fortran code defined under. I am trying to change the length of the do loop if i change the value of n. When i try to compile i get the error:
‘a’ argument of ‘floor’ intrinsic at (1) must be REAL. But when i change q and w to be defined as real i get another error message. How can i fix this? q and w is clearly a integer when i use floor(...)
subroutine boundrycon(n,bc,u,v)
!input
integer :: n,bc
!output
real(8) :: u(n+2,n+2), v(n+2,n+2)
!lokale
integer :: j,i,w,q
n=30
q=floor(n/2)
w=(floor(n/2)+floor(n/6))
do j=q,w
u(q,j)=0.0;
v(q+1,j)=-v(q,j);
u(w,j)=0.0;
v(w+1,j)=-v(w,j);
end do
do i=q,w
v(i,q)=0.0;
u(i,q)=-u(i,q+1);
u(i,w+1)=-u(i,w);
v(i,w)=0;
end do
end subroutine boundrycon
Many people have already pointed this out in the comments to your question, but here it is again as an answer:
In Fortran, if you do a division of two integer values, the result is an integer value.
6/3 = 2
If the numerator is not evenly divisible by the denominator, then the remainder is dropped:
7/3 = 2
Let's look at your code:
q=floor(n/2)
It first evaluates n/2 which, since both n and 2 are integers, is such an integer division. As mentioned before, this result is an integer.
This integer is then passed as argument to floor. But floor expects a floating point variable (or, as Fortran calls it: REAL). Hence the error message:
"[The] argument of floor ... must be REAL."
So, the easiest way to get what you want is to just remove the floor altogether, since the integer division does exactly what you want:
q = n/2 ! Integer Division
If you need to make a floating point division, that is if you want two integer variables to divide into a real variable, you have to convert at least one of them to floating point before the division:
print *, 3/2 ! wrong, prints 1
print *, real(3)/2 ! right
print *, 3/2.0 ! right
print *, (3 * 1.0) / 2 ! right
print *, real(3/2) ! wrong, prints 1.0

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)

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.