When I run the following code:
program foo
implicit none
logical :: a(2)
a = [.true., .true.]
print *, 'a = ', a
call evaluate(a)
a = [.true., .false.]
print *, 'a = ', a
call evaluate(a)
a = [.false., .false.]
print *, 'a = ', a
call evaluate(a)
contains
subroutine evaluate(a)
logical, intent(in) :: a(2)
if (a(1) .eqv. .true. .and. a(2) .eqv. .true.) then
print *, 'TT'
elseif (a(1) .eqv. .true. .and. a(2) .eqv. .false.) then
print *, 'TF'
elseif (a(1) .eqv. .false. .and. a(2) .eqv. .false.) then
print *, 'FF'
endif
end subroutine evaluate
end program
I get the following output:
a = T T
TT
a = T F
TF
a = F F
TT
Why the last call of the subroutine evaluate gives the wrong output (i.e. match the first if condition not the thrid)?
The code has been compiled with the command gfortran -Wall -fcheck=all foo.f90
.
You have discovered that the order of precedence of logical operators in Fortran can be a bit confusing. Let's extend your program slightly and see more weirdness:
ijb@ijb-Latitude-5410:~/work/stack$ cat eqv_2.f90
program foo
implicit none
logical :: a(2)
a = [.true., .true.]
print *, 'a = ', a
call evaluate(a)
a = [.true., .false.]
print *, 'a = ', a
call evaluate(a)
a = [.false., .true.]
print *, 'a = ', a
call evaluate(a)
a = [.false., .false.]
print *, 'a = ', a
call evaluate(a)
contains
subroutine evaluate(a)
logical, intent(in) :: a(2)
if (a(1) .eqv. .true. .and. a(2) .eqv. .true.) then
print *, 'TT'
elseif (a(1) .eqv. .true. .and. a(2) .eqv. .false.) then
print *, 'TF'
elseif (a(1) .eqv. .false. .and. a(2) .eqv. .true.) then
print *, 'FT'
elseif (a(1) .eqv. .false. .and. a(2) .eqv. .false.) then
print *, 'FF'
endif
end subroutine evaluate
end program
ijb@ijb-Latitude-5410:~/work/stack$ gfortran -std=f2008 -Wall -Wextra -fcheck=all -g -O eqv_2.f90
ijb@ijb-Latitude-5410:~/work/stack$ ./a.out
a = T T
TT
a = T F
TF
a = F T
TF
a = F F
TT
Hmmm, so not only [false,false] is [true,true] but [false,true] is [true, false]! How can this happen outside politics?
Well the problem is the precedence of the .eqv.
operator is lower than that of the .and.
operator, and so .and.
gets evaluated first. In fact the precedence of .eqv.
and .neqv.
are the lowest of any non-user defined operators in Fortran, so they will get evaluated last in any logical expression that only uses language defined operators. This is just the same as us evaluating 3 + 4 * 5 + 6
as 3 + (4*5) + 6 = 29
and not (3+4) * (5+6) = 77
, because the precedence of *
is higher than that of +
.
So you evaluate .false. .eqv. .true. .and. .false. .eqv. .true.
as
.false. .eqv. (.true. .and. .false.) .eqv. .true. =
( .false. .eqv. .false. ) .eqv. .true. =
.true. .eqv. .true. =
.true.
Hence the result you see. It is for this reason that I strongly recommend students to use brackets in long logical expressions - if we do this here we get what you expected:
ijb@ijb-Latitude-5410:~/work/stack$ cat eqv.f90
program foo
implicit none
logical :: a(2)
a = [.true., .true.]
print *, 'a = ', a
call evaluate(a)
a = [.true., .false.]
print *, 'a = ', a
call evaluate(a)
a = [.false., .false.]
print *, 'a = ', a
call evaluate(a)
contains
subroutine evaluate(a)
logical, intent(in) :: a(2)
if ( (a(1) .eqv. .true.) .and. (a(2) .eqv. .true.)) then
print *, 'TT'
elseif ((a(1) .eqv. .true.) .and. (a(2) .eqv. .false.)) then
print *, 'TF'
elseif ( (a(1) .eqv. .false.) .and. (a(2) .eqv. .false.)) then
print *, 'FF'
endif
end subroutine evaluate
end program
ijb@ijb-Latitude-5410:~/work/stack$ gfortran -std=f2008 -Wall -Wextra -fcheck=all -g -O eqv.f90
ijb@ijb-Latitude-5410:~/work/stack$ ./a.out
a = T T
TT
a = T F
TF
a = F F
FF
That said as Martin explains in the other answer a lot of this is redundant. In fact I would argue that expressions like a .eqv. .true.
are not good style, and in fact I can't remember when I last use .eqv.
or .neqv.
in a code.