From: James Giles on
James Van Buskirk wrote:
....
> [,,,] You know, a compiler could return .TRUE. for
>
> logical function is_broken()
> is_broken=transfer(transfer(2,.TRUE.).NEQV.transfer(4,.TRUE.),0)==1
> end
>
> and one could argue forever about whether that is reasonable
> behavior, but it would not mean that the compiler was usable.

It wouldn't mean the compiler was UNusable either. It's not at all
difficult to contrive a possible case. Suppose you have a platform
with instructions that XOR (the actual operation of .NEQV.) whole
words of the operands or, faster, just an individual byte of the
operands. Suppose further that the Fortran implementor chose
to use the leading bit (the sign, if the data is interpreted as an
INTEGER) as the significant bit as far as LOGICAL goes. It would
not be at all surprising for the implementation to choose to XOR
only the leading byte of the operands and store only that leading byte
into the result location (register or memory) - it's faster. Well, that
means contents of the operand values below the top byte are not
relevant or even referenced. It means that the contents of the result
value below the top byte will remain whatever was there before
the .NEQV. result was stored there - it's all irrelevant anyway.
It could be the value one (1). Who cares? Hey maybe the
implementor chose to guarantee by some means that all LOGICAL
results have odd parity regardless of their value.

There are two aspects to this question. The first is that TRANSFER
portably guarantees only one thing: If (and only if) type2 is at least
as large as type1, TRANSFER from type1 to type2 and back again is
guaranteed to yield that same value as you started with. If you do any
operations on the data as a type2 entity all bets are off (this includes
assignment, since the implementation is free to "normalize" during
assignment). Beyond that one guarantee, the use of TRANSFER is
entirely implementation dependent. To assume that ANY property
of those implementation dependent things is portably reliable is
not justified.

The second aspect of this issue is that I've never seen, and can't
even imagine, any case where I would want to TRANSFER between
LOGICAL and anything else. At least, not any cases where I would
do any operations on the intermediate results of such a transfer.
Sure, if I wanted a heterogenous array I might transfer a logical
value into what the compiler considers an integer array element.
But in that case I'd remember what that element really was and
*never* use it as an integer.

--
J. Giles

"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies." -- C. A. R. Hoare


From: James Van Buskirk on
"James Giles" <jamesgiles(a)worldnet.att.net> wrote in message
news:pP6lj.469042$kj1.58346(a)bgtnsc04-news.ops.worldnet.att.net...

> The second aspect of this issue is that I've never seen, and can't
> even imagine, any case where I would want to TRANSFER between
> LOGICAL and anything else. At least, not any cases where I would
> do any operations on the intermediate results of such a transfer.
> Sure, if I wanted a heterogenous array I might transfer a logical
> value into what the compiler considers an integer array element.
> But in that case I'd remember what that element really was and
> *never* use it as an integer.

I guess you've not done much C interop, then. If you check out
N1601.pdf, table 15.2, LOGICAL(C_BOOL) is considered interoperable
with C type _Bool which may just mean int. Suppose you are given
a *.mod file which contains interfaces to C functions. You may not
be able to recompile it so you may have to follow the choices made
by the programmer who compiled that *.mod file. Such a programmer
could quite easily have typed dummy arguments as LOGICAL(C_BOOL).
Since you have to send a LOGICAL actual argument, but there is no
way of guaranteeing that .TRUE. and .FALSE. correspond to C99 true
and false which are defined as 1 and 0, you would have to use
actual arguments of TRANSFER(1,.TRUE.) and TRANSFER(0,.FALSE.).

Similarly if a function result were LOGICAL(C_BOOL) we would
need to use TRANSFER once again to convert to an integer which
could be tested for its zero or nonzero properties. Instead of

if(f(x)) then
....

we would need to write

if(TRANSFER(f(x),0) /= 0) then
....

ISTR that I have seen some Win32 API *.mod files where you had
to do this, but looking at kernel32.f90 from the ifort
distribution, everything is typed as INTEGER(BOOL) so the
TRANSFERs aren't necessary. Perhaps my recollection is wrong
on this point.

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end


From: James Giles on
James Van Buskirk wrote:
....

> I guess you've not done much C interop, then. If you check out
> N1601.pdf, table 15.2, LOGICAL(C_BOOL) is considered interoperable
> with C type _Bool which may just mean int. [...]

And that's relevant to what I said how? LOGICAL(B_BOOL) isn't
(as your last example supposed) a *default* LOGICAL KIND. At
least it isn't necessarily so.

> [...] Suppose you are given
> a *.mod file which contains interfaces to C functions. You may not
> be able to recompile it so you may have to follow the choices made
> by the programmer who compiled that *.mod file. Such a programmer
> could quite easily have typed dummy arguments as LOGICAL(C_BOOL).
> Since you have to send a LOGICAL actual argument, but there is no
> way of guaranteeing that .TRUE. and .FALSE. correspond to C99 true
> and false which are defined as 1 and 0, you would have to use
> actual arguments of TRANSFER(1,.TRUE.) and TRANSFER(0,.FALSE.).

No, you have to use TRUE._C_BOOL and .FALSE._C_BOOL.

> Similarly if a function result were LOGICAL(C_BOOL) we would
> need to use TRANSFER once again to convert to an integer which
> could be tested for its zero or nonzero properties. Instead of
>
> if(f(x)) then
> ...
>
> we would need to write
>
> if(TRANSFER(f(x),0) /= 0) then

The function f(x) has an interface does it not? That interface declared
the return type as LOGICAL(C_BOOL) did it not? You shouldn't need
TRANSFER at all. IF(F(X)) then ... should work just fine. Complain
to your vendor if it gets something that simple so very wrong.

Yes, because of the fact that the C standard makes specific requirements
on booleans, the KIND C_BOOL of LOGICAL can be relied to portably
have those properties. The Fortran companion processor should get those
right without the need for TRANSFER.

--
J. Giles

"I conclude that there are two ways of constructing a software
design: One way is to make it so simple that there are obviously
no deficiencies and the other way is to make it so complicated
that there are no obvious deficiencies." -- C. A. R. Hoare


From: James Van Buskirk on
"James Giles" <jamesgiles(a)worldnet.att.net> wrote in message
news:wf8lj.469433$kj1.393176(a)bgtnsc04-news.ops.worldnet.att.net...

> The function f(x) has an interface does it not? That interface declared
> the return type as LOGICAL(C_BOOL) did it not? You shouldn't need
> TRANSFER at all. IF(F(X)) then ... should work just fine. Complain
> to your vendor if it gets something that simple so very wrong.

> Yes, because of the fact that the C standard makes specific requirements
> on booleans, the KIND C_BOOL of LOGICAL can be relied to portably
> have those properties. The Fortran companion processor should get those
> right without the need for TRANSFER.

Oog. Looking at the C99 standard, I see that _Bool, although it's
an integer type, has semantics distinct from int. In particular,
when conversion to _Bool is required any mumeric value that isn't
zero gets converted to 1 (didn't carefully check what happens to
imaginary values, though) and the example given in the standard is
that (int)0.5 = 0, but (_Bool)0.5 = 1 . That means that I have
introduced a red herring into my own argument in that LOGICAL(C_BOOL)
is to preexisting Fortran LOGICAL kinds what IEEE-754 data types are
to, say, VAX floating types like G_FLOAT.

G_FLOAT has different internal representation from IEEE-754 double
precision (T_FLOAT), but it is still a floating point data type and
operations between T_FLOATs should yield predictable T_FLOAT values
while operations between G_FLOATs should yield predictable G_FLOAT
values and operations between a G_FLOAT and a T_FLOAT should convert
one of the inputs to the kind of the other before computing a
result. Similar rules would be expected for preexisting LOGICAL
kinds and LOGICAL(C_BOOL). Just as implementing IEEE-754 is
optional, implementing LOGICAL(C_BOOL) is optional, and would have
different internal representation from other kinds. In particular,
transfer(.NOT.transfer(1,.TRUE._C_BOOL),0) would have to come out 0,
whereas the more normal result (due to the use of masking
expressions pre-MIL-STD 1753) would be -2 .

The big difference is the conversion of true values to 1 for a C99
_Bool which doesn't happen for C ints. What I was thinking about
was for pre-C99 where _Bool might get defined as int, but this is
not the case covered by f03 and LOGICAL(C_BOOL). So I am back to
discussing interoperability with pre-99 C where you really could
get passed values with internal representations identical to those
of 2 or 4 . I wanted to find an instance of that in Win32 API
modules included with Fortran compilers, but in at least the more
recent versions of CVF, BOOL is properly typed an INTEGER(BOOL).

In LF95 there is a file call win32mod.mod and if you USE win32mod
you get type definitions and interfaces to Win32 API functions but
I couldn't find any documentation about the module and if you don't
correctly guess the type LF95 chose for a dummy argument it prints
an error statement that doesn't tell you which one it didn't like,
so I don't know how to get enough working so that I could tell
whether LF95 is using LOGICAL or INTEGER types to represent Win32
API BOOL.

And I was going to write up an example where a C function passed
values of 2 and 4 to a Fortran callback function which took them
to be LOGICAL and then preformed the .NEQV. binary operation
between them and passed the result back to the C function and
caused some undesirable action if the result were 1 , but I'm just
too tired so I will have to leave it as an exercise for the reader.

--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end