Open Beliavsky opened 2 years ago
Some ammo for a tweet from my old FAQ:
You cannot use a simple compare of two arrays in an IF(3f), because a comparison of two arrays returns a logical array, not a single scalar logical. So this IF(3f) statement will return a compiler error:
integer :: A(3)=[1,2,3], B(3)=[1,2,3]
write(*,*)A.eq.B ! This returns an array
if(A.eq.B)then ! SO THIS WILL NOT WORK
write(*,*) "A and B are equal"
endif
end
There is not a specific intrinsic function to compare arrays in Fortran. but you can use the very flexible and generic ALL(3f) and ANY(3f) functions:
integer :: A(3)=[1,2,3], B(3)=[1,2,3]
write(*,*)A==B ! Note this returns an array, not a scalar
if(all(A.eq.B)) then
write(*,*) "A and B are equal"
else
write(*,*) "A and B are NOT equal"
endif
write(*,*) all(A.eq.B)
write(*,*) all(A.eq.B+2)
end
Results:
T T T
A and B are equal
T
F
which works for all arrays as long as they have the same type and length.
Of course, you can loop through the elements with a DO(3f):
integer :: A(3)=[1,2,3], B(3)=[1,2,3]
logical :: answer
COMPARE: block
integer :: i
answer=.false.
if(size(a).ne.size(b)) exit COMPARE
do i=1,size(a)
if(A(i).ne.B(i)) exit COMPARE
enddo
answer=.true.
endblock COMPARE
write(*,*)'equality of A and B is ',answer
end
Results:
equality of A and B is T
Writing a function and returning .TRUE. or. .FALSE. is straight-forward, but for each type of array there has to be another function or you have to use CLASS(*).
As an example, an alternative lacking the generic character of ALL(3f) or ANY(3f) is:
integer :: A(3)=[1,2,3], B(3)=[1,2,3]
if(equal(A,B))then
write(*,*) "A and B are equal"
else
write(*,*) "A and B are NOT equal"
endif
contains
pure logical function equal( array1, array2 )
integer,dimension(:),intent(in) :: array1, array2
integer :: i
equal=size(array1)==size(array2)
if(equal) then
do i=1,size(array1)
equal=array1(i) == array2(i)
if(.not.equal)exit
enddo
endif
end function equal
end
Results:
A and B are equal
If the arrays are INTEGER or CHARACTER, then the comparison can be exact. However, if the arrays contain floating-points values such as REAL, DOUBLEPRECISION or COMPLEX variables, you should consider using a suitably small tolerance when comparing values. For example:
!real :: A(3)=[1.0,2.0,3.0], B(3)=[1.0,2.0,2.9999999999999] ! this might test as equal
real :: A(3)=[1.0,2.0,3.0], B(3)=[1.0,2.0,2.999999] ! this should be close enough
real :: tolerance=0.00001 ! just a sample tolerance
if(all(A==B))then ! testing for exact matches can be problematic
write(*,*) "A and B are equal"
elseif (all( abs(A - B) < tolerance) )then
write(*,*) "A and B are close enough to equal"
else
write(*,*) "A and B are NOT equal"
endif
end
Most modern compilers do a good job at allowing programmers to compare floating point values, but there are several good sources on why you want to compare using a tolerance and how to determine what that tolerance should be.
The ANY(3f) and ALL(3f) functions may generate a logical array the size of the input arrays or always test all elements; depending on how they are implemented. This could cause comparisons of large arrays to require a significant amount of memory or do unneeded tests. The functions may or may not take advantage of parallel or vector processing when available. So if you are doing many array comparisons of very large arrays you might want to create your own functions, but I suspect most ANY(3f) and ALL(3f) functions will perform as well or better than your own routines.
If anyone has examples using Coarrays, OpenMP, or MPI that would be useful. Timing information on various methods for large arrays would also be very interesting. If I get the time I will try to add that.
suggested by @urbanjost