Reputation: 4728
I am intentionally casting an array of boolean values to integers but I get this warning:
Warning: Extension: Conversion from LOGICAL(4) to INTEGER(4) at (1)
which I don't want. Can I either
(1) Turn off that warning in the Makefile?
or (more favorably)
(2) Explicitly make this cast in the code so that the compiler doesn't need to worry?
The code will looking something like this:
A = (B.eq.0)
where A
and B
are both size (n,1)
integer arrays. B
will be filled with integers ranging from 0
to 3
. I need to use this type of command again later with something like A = (B.eq.1)
and I need A
to be an integer array where it is 1
if and only if B
is the requested integer, otherwise it should be 0
. These should act as boolean values (1
for .true.
, 0
for .false.
), but I am going to be using them in matrix operations and summations where they will be converted to floating point values (when necessary) for division, so logical
values are not optimal in this circumstance.
Specifically, I am looking for the fastest, most vectorized version of this command. It is easy to write a wrapper for testing elements, but I want this to be a vectorized operation for efficiency.
I am currently compiling with gfortran
, but would like whatever methods are used to also work in ifort
as I will be compiling with intel
compilers down the road.
update:
Both merge
and where
work perfectly for the example in question. I will look into performance metrics on these and select the best for vectorization. I am also interested in how this will work with matrices, not just arrays, but that was not my original question so I will post a new one unless someone wants to expand their answer to how this might be adapted for matrices.
Upvotes: 2
Views: 1356
Reputation: 6999
For this particular example you could avoid the logical all together:
A=1-(3-B)/3
Of course not so good for readability, but it might be ok performance-wise.
Edit, running performance tests this is 2-3 x faster than the where
construct, and of course absolutely standards conforming. In fact you can throw in an absolute value and generalize as:
integer,parameter :: h=huge(1)
A=1-(h-abs(B))/h
and still beat the where
loop.
Upvotes: 1
Reputation: 18118
I have not found a compiler option to solve (1).
However, the type conversion is pretty simple. The documentation for gfortran
specifies that .true.
is mapped to 1
, and false
to 0
.
Note that the conversion is not specified by the standard, and different values could be used by other compilers. Specifically, you should not depend on the exact values.
A simple merge
will do the trick for scalars and arrays:
program test
integer :: int_sca, int_vec(3)
logical :: log_sca, log_vec(3)
log_sca = .true.
log_vec = [ .true., .false., .true. ]
int_sca = merge( 1, 0, log_sca )
int_vec = merge( 1, 0, log_vec )
print *, int_sca
print *, int_vec
end program
To address your updated question, this is trivial to do with merge
:
A = merge(1, 0, B == 0)
This can be performed on scalars and arrays of arbitrary dimensions. For the latter, this can easily be vectorized be the compiler. You should consult the manual of your compiler for that, though.
The where
statement in Casey's answer can be extended in the same way.
Since you convert them to floats later on, why not assign them as floats right away? Assuming that A
is real
, this could look like:
A = merge(1., 0., B == 0)
Upvotes: 2
Reputation: 6915
Another method to compliment @AlexanderVogt is to use the where
construct.
program test
implicit none
integer :: int_vec(5)
logical :: log_vec(5)
log_vec = [ .true., .true., .false., .true., .false. ]
where (log_vec)
int_vec = 1
elsewhere
int_vec = 0
end where
print *, log_vec
print *, int_vec
end program test
This will assign 1 to the elements of int_vec
that correspond to true
elements of log_vec
and 0 to the others.
The where
construct will work for any rank array.
Upvotes: 1