What the ??!!? How does a bug this blatant get through testing?
Carlie Coats
Baron Advanced Meteorological Systems
----------------------------------------------------------
PROGRAM TEST_ENVYN
LOGICAL :: ENVYN, LVAR
INTEGER :: ISTAT
LVAR = ENVYN('LVAR','logical variable',.TRUE.,ISTAT)
IF ( .TRUE. .EQV. LVAR ) THEN
PRINT*, 'LVAR = TRUE'
ELSE
PRINT*, 'LVAR = FALSE'
ENDIF
END
> I am the primary maintainer of an open-source environmental-data
> I/O library, and one of my (historically very astute) users reports
> that the program below does not behave correctly when compiled with
> PGI F90 (of which I don't have a copy). Either replacing the
> IF-clause by the logically equivalent "IF ( LVAR ) THEN" or using
> an alternate compiler (g95, ifort, sunf95) causes the program to behave
> as expected.
> What the ??!!? How does a bug this blatant get through testing?
What is the actual bit value of LVAR? If it is not the
same as .TRUE. or .FALSE. then it might be that it is
non-standard, and fails.
LOGICAL LVAR,ENVYN
INTEGER IVAR
EQUIVALENCE(LVAR,IVAR)
LVAR = ENVYN('LVAR','logical variable',.TRUE.,ISTAT)
WRITE(*,'(Z8)') IVAR
END
(specifically, .EQV. may be the bitwise exclusive-NOR
operation, and IF may test for any non-zero value.)
> I am the primary maintainer of an open-source environmental-data
> I/O library, and one of my (historically very astute) users reports
> that the program below does not behave correctly when compiled with
> PGI F90 (of which I don't have a copy). Either replacing the
> IF-clause by the logically equivalent "IF ( LVAR ) THEN" or using
> an alternate compiler (g95, ifort, sunf95) causes the program to behave
> as expected.
> What the ??!!? How does a bug this blatant get through testing?
> PROGRAM TEST_ENVYN
> LOGICAL :: ENVYN, LVAR
> INTEGER :: ISTAT
> LVAR = ENVYN('LVAR','logical variable',.TRUE.,ISTAT)
> IF ( .TRUE. .EQV. LVAR ) THEN
> PRINT*, 'LVAR = TRUE'
> ELSE
> PRINT*, 'LVAR = FALSE'
> ENDIF
> END
This happens because Fortran compilers tend to get written in C.
Thus, the compiler programmers don't have a lick of Fortran sense
and think that it's reasonable for the internal representation of
LOGICAL variables to be such that transfer(0,.TRUE.) is .FALSE.
and anything else is .TRUE. .
If they then mapped .AND. to && and .OR. to || and .NOT. to !
and .NEQV. to ^^ and .EQV. to ^^!, everything would at least
be consistent and truth tables would be valid. Unfortunately
C has no ^^ operator and it is also found useful to use the
logical operators to achieve masking operations as &, |, ~, ^,
and ^~ .
Thus when a C function is invoked and the result comes back it
could have any bit pattern and the internal representation of
LOGICAL variables matters if the result is interpreted as being
of type LOGICAL. The only internal representation that is
consistent with truth tables and masking is one where only a
single bit of the variable is tested. DVF/CVF did this, ifort
may or may not do this currently, but UNIX based compilers
typically use C internal representation and violate truth
tables.
The only safe thing to do with mixed languages is to consider
the result variable to be of type INTEGER and use the callee's
rules to convert to LOGICAL. Thus you would declare ENVYN to be
of type INTEGER and write:
LVAR = ENVYN('LVAR','logical variable',.TRUE.,ISTAT) /= 0
or it could still work if ENVYN were LOGICAL:
LVAR = transfer(ENVYN('LVAR','logical variable',.TRUE.,ISTAT),1) /= 0
with the above line probably being too long for fixed format.
I wish vendors could be persuaded to preserve truth tables, but
you simply can't talk sense into them on this issue. C programs
have the same problem that you can assume a true result always
has the value 1 and this assumption can survive all testing, but
in the field the value can be anything and cause a failure. C
also allows pretty much any garbage to be converted to something
which can be the condition of an if or while, but Fortran doesn't
but then Fortran compiler vendors want to allow any garbage in
Fortran (the compiler writers think it's normal) and then you
get problems when the compiler accepts mistakes as valid logical
conditions leaving the Fortran programmer a much more difficult
task of isolating the error. Oh, you can enable error checking
if you require standard conformance, but for many programs you
are using a set of extensions that causes too many error
messages if you do.
--
write(*,*) transfer((/17.392111325966148d0,6.5794487871554595D-85, &
6.0134700243160014d-154/),(/'x'/)); end
> that the program below does not behave correctly when compiled with
> PGI F90 (of which I don't have a copy). Either replacing the
> IF-clause by the logically equivalent "IF ( LVAR ) THEN" or using
> an alternate compiler (g95, ifort, sunf95) causes the program to behave
> as expected.
...
> IF ( .TRUE. .EQV. LVAR ) THEN
Well, stylistically, I far prefer the simpler "IF (LVAR)", but that
aside...
1. As Glen and James note, if your LVAR is not a properly defined
logical variable, then all bets are off; that would be a program bug,
not a compiler one, even if the program happened to work with many other
compilers. That's the kind of thing that happens with nonstandard code.
In particular, if the value comes from C, then as James mentioned, you
should use an integer, not a logical Fortran variable to correspond to
most C usage. Much as James might prefer otherwise, any different usage
is nonstandard, nonportable, and yes, can well cause obscure symptoms.
Fortran is not C; logical and integer are diferent types and do not have
any standardized bit-level correpondence. Any use of TRANSFER or other
bit-level twiddling to get logical values is nonportable and *WILL*
cause problems (as James has nicely proven). Making a Fortran logical
variable argument-associate with a C integer also constitutes such
bit-level twidling.
If you happen to have a both a C99 (I think it is new to C99, but I'm
not 100% sure of that) compiler with the _Bool type and an F2003
compiler that supports that C compiler as a companion processor, then
you can use a Fortran logical to interoperate with a C _Bool. I'm
guessing that the odds of that being what you are doing are negligable.
If that's not what you are doing, then use a Fortran integer and convert
it to logical as desired using normal Fortran such as
logical_var = integer_var==0
(/=0, whichever the C-ish rule is; I always get it confused).
2. Or if that's not it and the lvar variable is indeed properly defined
as a Fortran logical, then maybe you just found yet another PGI bug. I
don't recall that one, but I sure do recall quite a few other PGI bugs
on trivial things.
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
> If they then mapped .AND. to && and .OR. to || and .NOT. to !
> and .NEQV. to ^^ and .EQV. to ^^!, everything would at least
> be consistent and truth tables would be valid. Unfortunately
> C has no ^^ operator and it is also found useful to use the
> logical operators to achieve masking operations as &, |, ~, ^,
> and ^~ .
I know DEC compilers were allowing integers as operands
of logical operators generating bitwise logical results
in the Fortran 66 days. They would also convert between
INTEGER and LOGICAL transferring the bits over. As well
as I remember, they only tested one bit, though I am not
sure which one.
The bitwise logical functions seem a better way to me.
It would be nice if C had the ^^ and ^^= operators.
-- glen
Well, a^^b would be the same as !a ^ !b. So, a^^=b would
be a = !a ^ !b. Of course if the thing on the left of the assignment
were a lvalue whose generaton involved side-effects, you would
have to introduce a temporary pointer variable. It's C after all.
--
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
"Simplicity is prerequisite for reliability" -- E. W. Dijkstra
>This happens because Fortran compilers tend to get written in C.
>Thus, the compiler programmers don't have a lick of Fortran sense
>and think that it's reasonable for the internal representation of
>LOGICAL variables to be such that transfer(0,.TRUE.) is .FALSE.
>and anything else is .TRUE. .
I don't read your postings very often, and this statement is a good
example of why. Every compiler team that I'm familiar with has some
real Fortran guys on it, and several of them read this newsgroup.
Just because you think representing LOGICALs has an obvious answer
doesn't mean that they agree with you, especially when you're
arguing against historical choices, and in a rude fashion.
-- greg
>I am the primary maintainer of an open-source environmental-data
>I/O library, and one of my (historically very astute) users reports
>that the program below does not behave correctly when compiled with
>PGI F90 (of which I don't have a copy). Either replacing the
>IF-clause by the logically equivalent "IF ( LVAR ) THEN" or using
>an alternate compiler (g95, ifort, sunf95) causes the program to behave
>as expected.
I suppose that if LVAR's value was not the canonical .TRUE. or .FALSE. value
(that is, it got defined in some manner other than the result of a LOGICAL
assignment or expression) then the standard perhaps is silent on the behavior,
but... Since using IF (LVAR) worked, something very odd is happening and an
inquiry to PGI is in order. Nevertheless, I agree with others that IF(LVAR)
is the preferred syntax.
Some years ago I wrote an article on common misimpressions regarding the
LOGICAL type and its uses, with a special note regarding .EQV.. You can find
it at http://tinyurl.com/4aqgtn
--
Steve Lionel
Developer Products Division
Intel Corporation
Nashua, NH
For email address, replace "invalid" with "com"
User communities for Intel Software Development Products
http://softwareforums.intel.com/
Intel Fortran Support
http://support.intel.com/support/performancetools/fortran
My Fortran blog
http://www.intel.com/software/drfortran
> If you happen to have a both a C99 (I think it is new to C99, but I'm
> not 100% sure of that) compiler with the _Bool type and an F2003
> compiler that supports that C compiler as a companion processor, then
> you can use a Fortran logical to interoperate with a C _Bool.
Um, no. If we look at n1124.pdf, section 6.3.1.2:
"When any scalar value is converted to _Bool, the result is 0 if the
value compares equal to 0; otherwise, the result is 1."
This is a property not shared by most Fortran LOGICAL implementations
so we might anticipate that C_BOOL will be -1 on just about
everything. Well, maybe not: if the implementation of _Bool on the
companion processor adopts the convention that only the low bit
contributes to the value of such a variable with the rest being
padding, then there is sort of a compatibility with DVF except that
on the C side the high bits, being undefined, could change without
notice. _Bool seem to me to be rather an unusual entity even in
C and LOGICAL(C_BOOL) to also be unusual in Fortran. Assuming that
interoperability is going to be possible through this path seems to
me to be asking for trouble.
Responses received have served to confirm my assertion that you
can't talk sense to the vendors on the issue of adhering to the
truth tables given in the standard document. The logical fallacies
that caused them to choose otherwise are about as difficult to
bring to the attention of their originators as any delusion is.
> Responses received have served to confirm my assertion that you
> can't talk sense to the vendors on the issue of adhering to the
> truth tables given in the standard document.
I vehemently disagree with you on this (which is to say that I agree
with the vendors). There is not a single vendor that fails to "adhere to
the truth tables given in the standard document."
Those truth tables, like every other operation definition in the
standard, apply only to operand values that are "defined" in the precise
sense of the standard. If you have done any form of bit twiddling on the
logical values, then the standard clearly and explicitly says that the
values are not defined.
Once you reference an undefined value, all bets are off. That is the
program violating the standard, not the compiler. The compiler is under
no obligation to detect such problems or to give any consistent results.
You are, of course, free to dislike this and to berate the vendors for
not doing things the way you want. You are also free to argue about how
good an idea it would be to do things as you want, how horrid you think
some vendor implementations are, and for that matter how horrid the
standard is. But your dislike does *NOT* make things a violation of the
standard.
Never, ever have I seen a single case of any vendor failing to "adhere
to the truth tables given in the standard" for cases where the standard
actually says that they apply.
> James Van Buskirk <not_...@comcast.net> wrote:
>> Responses received have served to confirm my assertion that you
>> can't talk sense to the vendors on the issue of adhering to the
>> truth tables given in the standard document.
> I vehemently disagree with you on this (which is to say that I agree
> with the vendors). There is not a single vendor that fails to "adhere to
> the truth tables given in the standard document."
I suppose you don't discern the irony in your above response?
> Those truth tables, like every other operation definition in the
> standard, apply only to operand values that are "defined" in the precise
> sense of the standard. If you have done any form of bit twiddling on the
> logical values, then the standard clearly and explicitly says that the
> values are not defined.
Now that's a tricky statement. What standard are we talking about:
N1601.pdf, or that of the companion processor, say, n1124.pdf? ISTR
that you said something to the effect of:
>>> If you happen to have a both a C99 (I think it is new to C99, but I'm
>>> not 100% sure of that) compiler with the _Bool type and an F2003
>>> compiler that supports that C compiler as a companion processor, then
>>> you can use a Fortran logical to interoperate with a C _Bool.
So that if we want to interoperate we have to conform to the standard
of the companion processor as well as to the Fortran standard. Some
randomly selected quotations from n1124.pdf:
Section 6.2.5
"An object declared as type _Bool is large enough to store the values 0
and 1."
"The type _Bool and the unsigned integer types that correspond to the
standard signed integer types are the standard unsigned integer types."
Section 6.2.6.1
"Values stored in non-bit-field objects of any other object type consist
of n*CHAR_BIT bits, where n is the size of an object of that type, in
bytes."
Section 6.2.6.2
"For unsigned integer types other that unsigned char, the bits of the
object representation shall be divided into two groups: value bits and
padding bits (there need not be any of the latter). If there are N
value bits, each bit shall represent a different power of 2 between
1 and 2**(N-1), so that objects of that type shall be capable of
representing values from 0 to 2**N-1 using a pure binary representation;
this shall be known as the value representation. The values of any
padding bits are unspecified."
Section 6.3.1.2
"When any scalar value is converted to _Bool, the result is 0 if the
value compares equal to 0; otherwise, the result is 1."
I am being driven towards the conclusion that _Bool only can represent
the values 0 and 1 and so has only one value bit. The number of bits
in its representation must be a multiple of CHAR_BIT, so there must also
be padding bits, whose values are unspecified. Thus in Fortran, if we
declare an object to be of type LOGICAL(C_BOOL) it must also have only
one value bit, the other bits being padding bits. To consider all bits
clear to be .FALSE._C_BOOL is OK, but certainly bits can be set and the
value must still evaluate to .FALSE._C_BOOL. For example,
C:\gcc_mingw64a\test\_Bool>type _Bool.f90
use ISO_C_BINDING
write(*,*) trim(merge("You are", "I am ", &
transfer(IAND(-1,NOT(transfer(.TRUE._C_BOOL,1))),.TRUE._C_BOOL)) &
)//" right."
end
C:\gcc_mingw64a\test\_Bool>x86_64-pc-mingw32-gfortran _Bool.f90 -o_Bool
C:\gcc_mingw64a\test\_Bool>_Bool
You are right.
Now, this clearly demonstrates a bug in gfortran: we started with
a value that was .TRUE. so that its single value bit was set,
.TRUE._C_BOOL . Then we came up with a quantity that had the
corresponding bit turned off, NOT(transfer(.TRUE._C_BOOL,1)) . We
then really did nothing to it: IAND(-1,NOT(transfer(.TRUE._C_BOOL,1)))
and converted back to LOGICAL(C_BOOL),
transfer(IAND(-1,NOT(transfer(.TRUE._C_BOOL,1))),.TRUE._C_BOOL)) .
Thus the value bit must be turned off any any padding bits should be
ignored, so we should print out the correct answer which is always,
for future reference, "I am right" (without the quotes). Note that
the original DVF internal representation of LOGICAL values is in fact
interoperable with _Bool unsigned integer types in this sense so that
DVF/CVF/ifort needed no modification to be interoperable but lesser
compilers need to either change their internal representation or add
add least one logical KIND with similar characteristics if they don't
want to take the coward's way out and set C_BOOL = -1 . How did this
happen: did Stan manage to infiltrate the committee that generated
n1124.pdf?
> "Richard Maine" <nos...@see.signature> wrote in message
> news:1iistey.1rtxo5k7kt6b8N%nos...@see.signature...
>
> > James Van Buskirk <not_...@comcast.net> wrote:
>
> >> Responses received have served to confirm my assertion that you
> >> can't talk sense to the vendors on the issue of adhering to the
> >> truth tables given in the standard document.
>
> > I vehemently disagree with you on this (which is to say that I agree
> > with the vendors). There is not a single vendor that fails to "adhere to
> > the truth tables given in the standard document."
>
> I suppose you don't discern the irony in your above response?
You are right that I don't. I am arguing about what the standard says
rather than about whether vendors should conform to it. Those seem to me
to be different points in a rather major way.
> > Those truth tables, like every other operation definition in the
> > standard, apply only to operand values that are "defined" in the precise
> > sense of the standard. If you have done any form of bit twiddling on the
> > logical values, then the standard clearly and explicitly says that the
> > values are not defined.
>
> Now that's a tricky statement. What standard are we talking about:
> N1601.pdf, or that of the companion processor, say, n1124.pdf?
I'm talking about the Fortran standard, that being what the tables in
question are in.
[elided quotes from the C standard]
> I am being driven towards the conclusion that _Bool only can represent
> the values 0 and 1 and so has only one value bit. The number of bits
> in its representation must be a multiple of CHAR_BIT, so there must also
> be padding bits, whose values are unspecified.
I have my doubts about that deduction, but I won't argue with you about
the C standard.
> Thus in Fortran, if we
> declare an object to be of type LOGICAL(C_BOOL) it must also have only
> one value bit, the other bits being padding bits.
No. That one I can argue about. The Fortran standard does not define
things at the bit level. It does not, for example, require that
interoperable C and Fortran entities have the same bit representations.
That is certainly the simplest and most obvious way for things to be,
but it is not required.
I repeat again, any construction using TRANSFER or other bit twiddling
gives a processor-dependent result, which need not act according to any
notion of sensibility. All your deductions about what you think the
bit-level representations must be are simply not requirements of the
standard. You are assuming things all over the place that the standard
just does not say (the Fortran standard here, in all cases). As soon as
you do *ANYTHING* with TRANSFER, you are in that regime. I pretty much
don't have to look any further; as soon as I see TRANSFER, I know that
you have gone outside of the standard.
The C part I don't know, but that applies only to C - not to your
assumption that C bit representations necessarily imply things about
Fortran ones. Yes, a Fortran processor could theoretically translate
representations at run-time via C interop - horrible mess and not likely
to happen, but it is alowed. If you think you know things that the C
standard guarantees, then actually do it in C where those guarantees
would actually apply (if your deductions about the guarantees are
correct there, which I can't authoritatively judge, though I wonder
whether you might be applying the same kind of assumptions there that
you are in Fortran). Don't do it in Fortran that you claim must emulate
C at the bit level, because I don't accept that claim.
Actually generate your _Bool in C, convince the C folk that your
generation is valid C, and then pass it to Fortran. Then you might have
a case.
> Now, this clearly demonstrates a bug in gfortran:
Not according to me. See above.
It would be nice if the internal representation of a logical conformed
to the common definition logic 1 (integer value 1) = true and logic 0 =
false...but it appears a hopeless case
--
Gary Scott
mailto:garylscott@sbcglobal dot net
Fortran Library: http://www.fortranlib.com
Support the Original G95 Project: http://www.g95.org
-OR-
Support the GNU GFortran Project: http://gcc.gnu.org/fortran/index.html
If you want to do the impossible, don't hire an expert because he knows
it can't be done.
-- Henry Ford
I forgot to comment on this part before, but I'l note that if it turns
out that your deductions about the C side are correct (I abstain on
judging that), it is quite plausible for a Fortran vendor to take your
so-called coward's way. For the most part, I don't think it was
envisioned that Fortran vendors would add new intrinsic kinds to match C
ones, but rather to have ISO_C_BINDING document the interoperability of
the already existing intrinsic kinds.
After all, adding a new intrinsic kind can be quite a lot of work by the
time you include support or it in all the intrinsic procedures and
everywhere else it is required to work. If you don't include support in
every relevant intrinsic procedure, then you haven't done a
standard-conforming job of implementing the kind.
If there happen to be no kinds that interoperate properly, the -1 value
is appropriate. I wouldn't consider that a "coward's way", but rather
correct reflection of the properties of the existing implementation.
Thus, if all of your other deductions turn out to be completely correct,
I would say that all you would have proven was that the value of C_BOOL
should have been -1, which is a pretty easy fix.
Now if you can identify a example, without *any* use of
TRANSFER, where companion processors fail to handle
the association of LOGICAL(C_BOOL) on the Fortran
side with _BOOL on the C side in compliance with both
standards, then maybe there's something to talk about.
But to pursue your current course, you need *proof*, not
arm-waving. It's not relevant that *you* are "being driven
to the conclusion that" something. Who cares but you?
You need two things: 1) an implementation independent
definition of what TRANSFER does with LOGICAL (lots
of luck), and 2) actual *proof* of some particular internal
representation requirements on C's _BOOL type. I don't
think you'll find either.
I know (from asking) that Fortran's specification of LOGICAL
is *deliberately* vague, so that implementations can choose
whatever is most convenient or fastest. An implementation
doesn't even need to be self-consistent. It can use a different
internal form from one statement to the next. All it needs to
do is consult the data-flow graph to insure that subsequent
uses of each value are consistent with the definitions. I suspect
the same vagueness is deliberately part of the C _BOOL
definition. All they need to guarantee is that subsequent uses
of things like &&, ||, !, and using the values in control constructs
are all properly defined. They can even change internal
representations of the *same* result on the fly.
Given that the internal representaton of LOGICAL (of any
KIND) is not specified by the standard, TRANSFER involving
LOGICAL (either direction) is simply undefined. Whether
the standard officially says so or not is beside the point - there
is no definition of specific meaning. That's the meaning of
undefined, isn't it?
It would be nicer if no one wanted to violate the specification
of the data type in the first place. Then it wouldn't matter what
the internal representation is.
> Actually generate your _Bool in C, convince the C folk that your
> generation is valid C, and then pass it to Fortran. Then you might have
> a case.
I guess it's a bug in gcc as well:
C:\gcc_mingw64a\test\pointer_arith>type _Bool.c
#include <stdio.h>
#include <stdint.h>
int main()
{
int8_t i;
for(i = 1; i != 0; i <<= 1)
{
if(*((_Bool*)(&i)))
{
printf("%d was true\n",i);
}
else
{
printf("%d was false\n",i);
}
}
return 0;
}
C:\gcc_mingw64a\test\pointer_arith>x86_64-pc-mingw32-gcc _Bool.c -o_Bool
C:\gcc_mingw64a\test\pointer_arith>_Bool
1 was true
2 was true
4 was true
8 was true
16 was true
32 was true
64 was true
-128 was true
For a quantity that only has values 0 and 1 gcc considers all 8 bits
to be value bits rather than selecting one only as the value bit and
the other 7 to be padding. So you don't think this is a bug, either,
right? Any bug resulting from this perverse mistreatment of boolean
data is really just a feature of the compiler in your view. Here's
another example:
C:\gcc_mingw64a\test\pointer_arith>type test2.c
#include <stdio.h>
#include <stdint.h>
_Bool fun(_Bool x);
int main()
{
int8_t i;
for(i = 1; i != 0; i <<= 1)
{
if(*((_Bool*)(&i)))
{
printf("%d was true\n",i);
}
else
{
printf("%d was false\n",i);
}
printf("(_Bool)%d = %d\n", i, fun(*((_Bool*)(&i))));
}
return 0;
}
_Bool fun(_Bool x)
{
_Bool result;
result = x;
return result;
}
C:\gcc_mingw64a\test\pointer_arith>x86_64-pc-mingw32-gcc test2.c -otest2
C:\gcc_mingw64a\test\pointer_arith>test2
1 was true
(_Bool)1 = 1
2 was true
(_Bool)2 = 2
4 was true
(_Bool)4 = 4
8 was true
(_Bool)8 = 8
16 was true
(_Bool)16 = 16
32 was true
(_Bool)32 = 32
64 was true
(_Bool)64 = 64
-128 was true
(_Bool)-128 = 128
Now if all bits of an _Bool were value bits, then function fun()
should check that any bit of x is set and then set result to 1 if so
and 0 otherwise. It doesn't do that, just copying all bits as though
only one of them were a value bit and the others were padding. So
this is an inconsistency, but once we have established the important
lemma that .TRUE. .EQV. .FALSE. then the truth or falsehood of any
other proposition follows directly and all such inconsistencies are
brushed aside.
As of this year I've been programming for 40 years. Programming,
as an activity involving the preparation of texts that are then
processed automatically, isn't older than about 55 years. So,
I've been around for almost 80% of the history of programming.
I think you're wrong.
I believe that it's always been the case that programmers are
invited to treat most of the internal programming environment
as a black-box. It's none of your business whether the hardware
uses 5-4 NAND gates as its basic building block, or whether the
compiler is LR or recursive descent, etc.
Sure, lots of people (usually with other bad character traits as well)
have often tried to break into those black boxes. When they report
that something is broken and blame others for the failure of their
code, the correct answer is (and always has been) that no, it's
their program that's broken due to their own folly of ill advised
breaking of the rules. I certainly see no reason to encourage
such bad behavior by complicating the programming environment
with some required internal specifications.
I want to go back to the original, in the vein of Steve Lionel's
article.
".EQV." should mean "equal in the sense of Fortran LOGICALs".
In that case the following should be equivalent, independent
of the bit-representations:
LOGICAL LVAL
...
IF ( LVAL ) THEN...
and
LOGICAL LVAL
...
IF ( LVAL .EQV. .TRUE. ) THEN...
When I put on my (MIT-earned) logic-professor hat, I say:
*IT*IS*AN*ERROR* for PGI Fortran to give different
results for the two IF's above.
And I'm not happy about it.
FWIW -- Carlie Coats
Agreed. In fact it may be the first time I've ever seen a compiler
make such an error. Of course, it's a bit understandable: I've
used Fortran for decades and can't remember ever using .EQV.
So it is the kind of thing that might slide through undetected.
All C/C++ guarantee is that true and false have values of 0 and 1
respectively. For example:
bool tf;
...
tf = 1 == 0;
cout << "1 == 0 returns: " << tf << endl;
tf = 1 == 1;
cout << "1 == 1 returns: " << tf << endl;
You will get 0 and 1.
However - when testing something, C requires a zero/non-zero test. For
example:
bool tf;
...
tf = 42;
if (tf)
cout << "tf is true" << endl;
else
cout << "tf is false" << endl;
Even though the low order bit is 0, the if test will evaluate to true.
Fortran implementations have been known to use the sign bit, the low
order bit, zero/non-zero, and others when setting/evaluating logical
data type. As Richard says, once you use TRANSFER, you are on your
own.
W.
> ".EQV." should mean "equal in the sense of Fortran LOGICALs".
> In that case the following should be equivalent, independent
> of the bit-representations:...
Yes, but that is true only if the values in question are valid Fortran
logicals. If that isn't part of what "equal in the sense of Fortran
LOGICALs" means, then it ain't so. If they are not valid Fortran
logicals, then the code is illegal and the compiler is allowed to do
anything. That is sort of the whole point. You can't just ignore it.
Well, you can, but you won't win many debates that way.
There is perhaps some debate as to whether the values constructed are
valid logicals. If they are, then all the rest would follow. But if they
aren't, I'm afraid that logic professor or not, all bets are off. This
is Fortran, not logic class, and the same rules don't always apply. For
example, x+0.0 does not necessarily equal x (because normalization might
have happened, or for that matter, x might be a NaN).
The compiler might not even sucessfully execute one statement or other,
much less give the same result. For example, it would be quite valid for
a compiler to throw an error on trying to execute either statement,
noticing the invalid logical, but not to notice it on th eother
statement.
Actually what C/C++ guarantee is that, when treated as integers,
the result of logical operators will be 0 or 1. If you never treat
them as integers, the as-if rule applies (the internal value can
be anything the implementation chooses as long as the resulting
computation behaves identically as-if the answers had been 0
and 1).
> bool tf;
> ...
> tf = 1 == 0;
> cout << "1 == 0 returns: " << tf << endl;
> tf = 1 == 1;
> cout << "1 == 1 returns: " << tf << endl;
>
> You will get 0 and 1.
Yes, if you write it out, it is required to have the external value
of either 0 or 1. But suppose you go further. To paraphrase
your next example:
bool tf;
...
tf = (x<=y); // for some x and y
if (tf)
cout << "tf is true" << endl;
else
cout << "tf is false" << endl;
Here the variable tf may never even be assigned. In fact, if
there are no other references to it, the compiler (at high optimization
levels) might not even allocate memory for tf. And the branch
might be decided on one or more flags in the CPU's status word
and not on any value of tf at all (zero, nonzero, or anything.)
In between these extremes, the compiler is free to choose any
method it likes. Suppose that optimization isn't so aggressive
as to completely eliminate tf, but that the compiler *is* aware
that no integer operations will ever be applied to it, does it
really still have to be reduced to a canonical form? The answer
is no. Even if subsequent integer operations are performed
on it, does it have to be internally stored in a canonical form, or
can the compiler convert it to 0/1 as part of referencing it? The
latter is the case. Etc. At least that's the case for C. Your examples
are C++ and I'm less familiar with the language (and have never
read the standard). It would surprise me if the as-if rule didn't
apply threre too.
For the purposes of this discussion C and C++ are the same. Also,
when talking about Fortran and C/C++ interoperability, the internal
representation IS important. (Unless, I guess, a really smart
compiler can inline C code into Fortran or vice-versa.)
W.
> All C/C++ guarantee is that true and false have values of 0 and 1
> respectively. For example:
> bool tf;
> ...
> tf = 1 == 0;
> cout << "1 == 0 returns: " << tf << endl;
> tf = 1 == 1;
> cout << "1 == 1 returns: " << tf << endl;
> You will get 0 and 1.
I don't know why I'm replying because it's clear that you haven't
been reading my messages. It doesn't make sense for me to reply
because given consistent behavior you won't read this one either.
Although our social norms dictate that if I can't make you under-
stand what I'm saying I should yell louder in Spanish at my cell
phone, I will try again with patience:
C has a brand new type, _Bool, which, although it's an unsigned
integer type, has nothing really to do with preexisting integer
types. C++ does not have a corresponding data type, AFAIK.
When you assign anything to an _Bool, the result must be 0 or 1 .
That was the point of the quotation of Section 6.3.1.2 of n1124.pdf
that I gave in
http://groups.google.com/group/comp.lang.fortran/msg/87c3edb7d75f3137?hl=en
Here is an example of that behavior:
C:\gcc_mingw64a\test\pointer_arith>type bool_test.c
#include <stdio.h>
int main()
{
_Bool tf;
tf = 42;
printf("tf = %d\n", tf);
return 0;
}
C:\gcc_mingw64a\test\pointer_arith>x86_64-pc-mingw32-gcc
bool_test.c -obool_test
C:\gcc_mingw64a\test\pointer_arith>bool_test
tf = 1
See how assignment converted 42 to 1? I don't know what that bool
thing you declared was, but it has nothing to do with _Bool. It's
not obvious at first that _Bool is such a different animal, but
careful examination of the appropriate standard document shows the
differences from other integer types like the above.
I claim that it's consistent with the C99 standard that in the
following code snippet:
int8_t x;
_Bool y;
x = 42;
y = *((_Bool*)(&x));
x = y;
At this point it's reasonable that x has the value 0 . Why? Because
through type casting we have created an _Bool value with the low bit
clear, so it should evaluate to 0 . Any other bit could have been
chosen to be the value bit, of course, but it makes it easier to
convert to other types if it's the least significant bit. Now, if we
further wrote
x = *((int8_t*)(&y));
it would be equally reasonable that x would be 42 because the simplest
way to carry out assignment between _Bool variables is just to copy
them.
If the above seems odd, then consider what would happen if I had
instead written:
int32_t x;
float y;
x = 0x3f800000;
y = *((float*)(&x));
x = y;
Most of you in the audience would assume that x should have the value
1 at this point, and that if I wrote:
x = *((int32_t*)(&y));
that x would now acquire the value 1065353216 . Why should it be any
more surprising with _Bool?
At least the companion processors must agree about how to
handle each-other's conventions. Still, no TRANSFERs can
safely be applied. And no assumptions can be made at the
user level.
> LOGICAL LVAL
> ...
> IF ( LVAL ) THEN...
> and
> LOGICAL LVAL
> ...
> IF ( LVAL .EQV. .TRUE. ) THEN...
> When I put on my (MIT-earned) logic-professor hat, I say:
> *IT*IS*AN*ERROR* for PGI Fortran to give different
> results for the two IF's above.
> And I'm not happy about it.
I have been trying to argue your case both in this thread but in
many previous threads that LOGICAL variables should obey the truth
tables given in the standard document and in fact I have given
examples before of compilers that fail to do so and every time I
am shouted down as in this thread.
It is supposed to be more ergonomic that truth tables are violated
at will than for Fortran to have different internal representation
of LOGICAL variables than what C would consider triggering true and
false results for int variables. The fact is that C is what is
error prone as my first example with _Bool variables showed that
even gcc assumes that an _Bool variable will have the value 0 or
1 and then creates _Bool variables with values that are neither.
No need to get testy...
> ...
> C has a brand new type, _Bool, which, although it's an unsigned
> integer type, has nothing really to do with preexisting integer
> types. C++ does not have a corresponding data type, AFAIK.
Yes it does. It is called 'bool'. The C99 _Boolean was patterned
after the C++ bool. But since the C committee didn't want to break
existing programs, they called it _Boolean instead. You can #include
<stdbool.h> which includes a typedef to equate bool to _Boolean.
> When you assign anything to an _Bool, the result must be 0 or 1 .
Same with bool in C++.
> ...
> I claim that it's consistent with the C99 standard that in the
> following code snippet:
>
> int8_t x;
> _Bool y;
>
> x = 42;
> y = *((_Bool*)(&x));
> x = y;
>
> At this point it's reasonable that x has the value 0 . Why? Because
> through type casting we have created an _Bool value with the low bit
> clear, so it should evaluate to 0 .
No - because casting is kinda like TRANSFER. There are combinations
where you are on your own.
Nonetheless, when the data item is tested, the compiler will generate
a zero/nonzero test.
Consider the following - which is a bit more conventional looking
than using casts:
#include <iostream>
using namespace std;
main () {
union {
bool tf;
int tfi;
};
tfi = 0 == 1;
cout << "0 == 1 returns: " << tf << endl;
tfi = 1 == 1;
cout << "1 == 1 returns: " << tf << endl;
tfi = 42;
if (tf)
cout << "tf is true: " << tf << endl;
else
cout << "tf is false: " << tf << endl;
}
When I run the above I get:
$ g++ booltest.c
$ a
0 == 1 returns: 0
1 == 1 returns: 1
tf is true: 42
$
You can recode the above into C99 and will get the same results.
W.
> James Van Buskirk wrote:
>> C has a brand new type, _Bool, which, although it's an unsigned
>> integer type, has nothing really to do with preexisting integer
>> types. C++ does not have a corresponding data type, AFAIK.
> Yes it does. It is called 'bool'. The C99 _Boolean was patterned
> after the C++ bool. But since the C committee didn't want to break
> existing programs, they called it _Boolean instead. You can #include
> <stdbool.h> which includes a typedef to equate bool to _Boolean.
OK, let's test this (BTW, it's called _Bool, not _Boolean, at least
the Fortran standard says that LOGICAL(C_BOOL) is supposed to be
interoperable with an type called _Bool):
C:\gcc_mingw64a\test\pointer_arith>type test5.cpp
#include <iostream>
bool fun(bool x);
int main()
{
unsigned char i;
for(i = 1; i != 0; i <<= 1)
{
if(*((bool*)(&i)))
{
printf("%d was true\n",(unsigned int)i);
}
else
{
printf("%d was false\n",(unsigned int)i);
}
printf("(bool)%d = %d\n", (unsigned int)i, fun(*((bool*)(&i))));
}
return 0;
}
bool fun(bool x)
{
bool result;
result = x;
return result;
}
C:\gcc_mingw64a\test\pointer_arith>x86_64-pc-mingw32-g++ test5.cpp -otest5
C:\gcc_mingw64a\test\pointer_arith>test5
1 was true
(bool)1 = 1
2 was true
(bool)2 = 2
4 was true
(bool)4 = 4
8 was true
(bool)8 = 8
16 was true
(bool)16 = 16
32 was true
(bool)32 = 32
64 was true
(bool)64 = 64
128 was true
(bool)128 = 128
I'll be darned. So C++ bool does have the same characteristics
as C _Bool, and g++ even has the same bug here. But maybe C++
doesn't allow padding in intrinsic types; I couldn't find that
in the standard (it's difficult to find anything in the C++
standard) so it might have some weird behavior forced upon it.
Certainly C compilers have the choice to have consistent boolean
variables. Obviously anybody with common sense would consider it
a bug if they didn't avail themselves of this choice, but the C
language, being shorthand for pdp-11 assembler or so, is supposed
to be quirky, inconsistent, and unpredictable: we wouldn't want to
break Duff's device for the sake of clarity.
Yeah - a senior moment on my part. My excuse is that I invariably
use #include <stdbool.h> in C so as to get the typedef. Then declare
my logical variables as 'bool' so they look more like C++.
The other thing <stdbool.h> gives you are the constants 'true' and
'false', whereas these are built-in reserved words in C++. (Again,
there were compatibility concerns - since it seems to be quite common
for older C programs to define their own values for true and false.)
W.
Anyone with common sense wouldn't be messing around with
explicitly undefined operations in the first place. Certainly anyone
with common sense would not have the gall to claim that anything
about the exercise had anything to do with clarity. Used as intended,
LOGICAL in fortran has nothing quirky, inconsistent, or unpredictable
about it. I suspect the same is true of _BOOL in C. Perhaps the
languages should explicitly disallow things like TRANSFER (and
casts) on LOGICAL (and _BOOL), but language designers have
a penchant for orthogonality - even when it's meaningless or
undesirable.
Still, explicit mention that such things are implementation dependent
should be sufficient for programmers with common sense to avoid
them. And, if an implementor's document doesn't say anything
clear and specific about it, then it's unambiguously undefined there.
Hence, common sense would suggest they a programmer has no
right to expect *anything* from it.
But then, you have to have common sense before appeals to it
are meaningful.
> James Van Buskirk wrote:
>> int8_t x;
>> _Bool y;
>> x = 42;
>> y = *((_Bool*)(&x));
>> x = y;
>> At this point it's reasonable that x has the value 0 . Why? Because
>> through type casting we have created an _Bool value with the low bit
>> clear, so it should evaluate to 0 .
> No - because casting is kinda like TRANSFER. There are combinations
> where you are on your own.
I have been thinking about this comment and have decided to create a
test to determine exactly which bit patterns are valid and which are
not. My test goes like this: if you have _Bool variables x and z and
int y, then if x contains a valid bit pattern it must be the case
that z = x sets z to 0 or 1 . Then y = z will set y to 0 or 1, so
the sequence z = x; y = z; must result in y holding the value 0 or
1 if x was a valid bit pattern.
C:\gcc_mingw64a\test\pointer_arith>type _Booltest.c
#include <stdio.h>
#include <stdint.h>
void table_row(_Bool x);
void printb(_Bool x);
int main()
{
uint8_t i;
unsigned j;
printf(" bits value (int)x 6.3.1.3\n");
for(j = 0; j < 255; j++)
{
i = j;
table_row(*((_Bool*)(&i)));
}
return 0;
}
void table_row(_Bool x)
{
int y;
_Bool z;
y = x;
printb(x);
if(x)
{
printf(" true");
}
else
{
printf(" false");
}
printf("%5d", y);
z = x;
y = z;
if(y == 0 || y == 1)
{
printf(" passed\n");
}
else
{
printf(" failed\n");
}
}
void printb(_Bool x)
{
uint8_t i;
uint8_t y;
y = *((uint8_t*)(&x));
for(i = 0x80; i != 0; i >>= 1)
{
printf("%d", (_Bool)(i&y));
}
}
C:\gcc_mingw64a\test\pointer_arith>x86_64-pc-mingw32-gcc
_Booltest.c -o_Booltest
C:\gcc_mingw64a\test\pointer_arith>_Booltest
bits value (int)x 6.3.1.3
00000000 false 0 passed
00000001 true 1 passed
00000010 true 2 failed
00000011 true 3 failed
00000100 true 4 failed
00000101 true 5 failed
00000110 true 6 failed
00000111 true 7 failed
00001000 true 8 failed
00001001 true 9 failed
...
11110101 true 245 failed
11110110 true 246 failed
11110111 true 247 failed
11111000 true 248 failed
11111001 true 249 failed
11111010 true 250 failed
11111011 true 251 failed
11111100 true 252 failed
11111101 true 253 failed
11111110 true 254 failed
You will have to take my word for it that the middle 236 lines of
output indicated test failures. The result shows that the only
valid bit patterns on this compiler:
C:\gcc_mingw64a\test\pointer_arith>x86_64-pc-mingw32-gcc -v
Using built-in specs.
Target: x86_64-pc-mingw32
Configured with:
../gcc/configure --prefix=/var/tmp/rt --with-sysroot=/var/tmp/r
t --host=i686-pc-mingw32 --target=x86_64-pc-mingw32 -q --silent
Thread model: win32
gcc version 4.4.0 20080611 (experimental) (GCC)
are b'00000000' and b'00000001'. Thus the low-order bit is the
value bit and all other bits are padding because they don't
affect the value, only whether it's valid or not.
How about Fortran? Here I tested the sequence (for LOGICAL(C_BOOL)
x and z)
z = .TRUE.
z = x .AND. z
Now if the true branch of if(x) then... is taken, I set LOGICAL
variable a to .TRUE., otherwise to .FALSE. Similarly if the true
branch of if(z) then... is taken, I set LOGICAL variable b to
.TRUE., otherwise .FALSE. If the starting point x had a valid
bit pattern, then a and b should test equivalent to x hence to
each other.
C:\gcc_mingw64a\test\pointer_arith>type LOGICALtest2.f90
module funcs
use ISO_C_BINDING
implicit none
contains
subroutine table_row(x)
logical(C_BOOL) x
integer(C_INT8_T) y
logical(C_BOOL) z
logical a, b
y = transfer(x,y)
z = .TRUE.
z = x .AND. z
if(x) then
a = .TRUE.
else
a = .FALSE.
end if
if(z) then
b = .TRUE.
else
b = .FALSE.
end if
write(*,'(b8.8,1x,a,i5,4x,a,4x,a)') y, &
merge(' .TRUE.','.FALSE.',x),y, &
merge(' .TRUE.','.FALSE.',z), &
merge('passed','failed',x.EQV.z)
end subroutine table_row
end module funcs
program LOGICALtest2
use funcs
use ISO_C_BINDING
implicit none
integer(C_INT8_T) i
integer j
write(*,'(a)') " bits value int(x) x.AND..TRUE. Boolean"
do j = 0, 255
i = j
call table_row(transfer(i,.TRUE._C_BOOL))
end do
end program LOGICALtest2
C:\gcc_mingw64a\test\pointer_arith>x86_64-pc-mingw32-gfortran
LOGICALtest2.f90 -
oLOGICALtest2
C:\gcc_mingw64a\test\pointer_arith>LOGICALtest2
bits value int(x) x.AND..TRUE. Boolean
00000000 .FALSE. 0 .FALSE. passed
00000001 .TRUE. 1 .TRUE. passed
00000010 .TRUE. 2 .FALSE. failed
00000011 .TRUE. 3 .FALSE. failed
00000100 .TRUE. 4 .FALSE. failed
00000101 .TRUE. 5 .FALSE. failed
00000110 .TRUE. 6 .FALSE. failed
00000111 .TRUE. 7 .FALSE. failed
00001000 .TRUE. 8 .FALSE. failed
00001001 .TRUE. 9 .FALSE. failed
...
11110110 .TRUE. -10 .FALSE. failed
11110111 .TRUE. -9 .FALSE. failed
11111000 .TRUE. -8 .FALSE. failed
11111001 .TRUE. -7 .FALSE. failed
11111010 .TRUE. -6 .FALSE. failed
11111011 .TRUE. -5 .FALSE. failed
11111100 .TRUE. -4 .FALSE. failed
11111101 .TRUE. -3 .FALSE. failed
11111110 .TRUE. -2 .FALSE. failed
11111111 .TRUE. -1 .FALSE. failed
Again the reader will have to take my word for it that the middle
236 bit patterns failed the test. Again we see that the only
valid bit patterns are b'00000000' and b'00000001'. So only the
LSB is again meaningful because bit patterns where any other bit is
set are invalid.
This is actually rather good news because upward compatibility
dictates that we can't change the behavior of existing valid
programs but we may deem previously invalid programs valid and
ascribe any meaning we desire to them. Thus it is still not too
late to consider all bit patterns for _Bool and LOGICAL(C_BOOL)
to be valid, the value only deriving from the least significant
bit (0 or 1 for _Bool, .FALSE. or .TRUE. for LOGICAL(C_BOOL))
and then we may compute the action of && via &, || via |, ! via ~,
.NOT. as by the NOT function, .AND. as by the IAND function, .OR.
as by the IOR function, .NEQV. as by the IEOR function, and .EQV.
as by a combination of the NOT and IEOR functions, surrounded
by TRANSFERS of course in the Fortran cases.
In short, gfortran is already consistent with DVF in all valid
programs, so if it were desired to remove all the cases where
an invalid bit pattern can occur which can't be easily tested for
and causes unpredictable results every time, it is possible to
validate them all, resulting in predictable behavior of _Bool
and LOGICAL variables that is consistent with existing practice.
At one time, before C became important, DEC hardware tested the sign bit
for LOGICAL true/false comparison. Then they switched to testing the
low order bit; it was fairly well known that LOGICAL tested an integer
for odd-ness, with no warning by default. As optimizing compilers could
generate efficient code for
IF (IAND(I,1) /= 0)
most people sanely avoided writing the shortcut which compiled to the
same code on those platforms.
The value of operating on bit patterns as both LOGICAL and INTEGER (or
something else) has been disproved repeatedly.
Here's a test program:
$ cat count.f90
program main
implicit none
integer, parameter :: n=2
integer, dimension(n, n) :: i
logical, dimension(n, n) :: m
i = 256
m = reshape(transfer(i,m),shape(m))
print *,m
print *,count(m)
print *,count(m,dim=1)
end program main
$ gfortran count.f90
$ ./a.out
T T T T
4
0 0
> James,
>
> All C/C++ guarantee is that true and false have values of 0 and 1
> respectively. For example:
>
> bool tf;
> ...
> tf = 1 == 0;
> cout << "1 == 0 returns: " << tf << endl;
> tf = 1 == 1;
> cout << "1 == 1 returns: " << tf << endl;
>
> You will get 0 and 1.
In fact, one sometimes sees the construct
int tf = !!i;
Where the "not not" operator would seem to do nothing, whereas, in
fact, it really converts integer-to-boolean, where "boolean" in the
James Van Buskirk sense. If the variable "i" holds any non-zero
value, "tf" is assigned 1, otherwise "tf" is assigned 0.
Chip
--
Charles M. "Chip" Coldwell
"Turn on, log in, tune out"
GPG Key ID: 852E052F
GPG Key Fingerprint: 77E5 2B51 4907 F08A 7E92 DE80 AFA9 9A8F 852E 052F
> James Van Buskirk wrote:
>> ...
>> C has a brand new type, _Bool, which, although it's an unsigned
>> integer type, has nothing really to do with preexisting integer
>> types. C++ does not have a corresponding data type, AFAIK.
>
> Yes it does. It is called 'bool'.
Only if you #include <stdbool.h>. Otherwise, it is called _Bool.
Most amusing. I only tested logical operators:
C:\gcc_mingw64a\test\pointer_arith>type L1.f90
function notfun(x)
logical notfun, x
notfun = .NOT. x
end function notfun
function andfun(x,y)
logical andfun, x, y
andfun = x .AND. y
end function andfun
function orfun(x,y)
logical orfun, x, y
orfun = x .OR. y
end function orfun
function eqvfun(x,y)
logical eqvfun, x, y
eqvfun = x .EQV. y
end function eqvfun
function neqvfun(x,y)
logical neqvfun, x, y
neqvfun = x .NEQV. y
end function neqvfun
C:\gcc_mingw64a\test\pointer_arith>x86_64-pc-mingw32-gfortran -S -momit-leaf-fra
me-pointer L1.f90
C:\gcc_mingw64a\test\pointer_arith>type L1.s
.file "L1.f90"
.text
.globl _notfun_
.def _notfun_; .scl 2; .type 32; .endef
_notfun_:
movq %rcx, 8(%rsp)
movq 8(%rsp), %rax
movl (%rax), %eax
xorl $1, %eax
movl %eax, -12(%rsp)
movl -12(%rsp), %eax
ret
.globl _andfun_
.def _andfun_; .scl 2; .type 32; .endef
_andfun_:
movq %rcx, 8(%rsp)
movq %rdx, 16(%rsp)
movq 8(%rsp), %rax
movl (%rax), %eax
xorl $1, %eax
testl %eax, %eax
jne L4
movq 16(%rsp), %rax
movl (%rax), %eax
xorl $1, %eax
testl %eax, %eax
jne L4
movl $1, -28(%rsp)
jmp L5
L4:
movl $0, -28(%rsp)
L5:
movl -28(%rsp), %eax
movl %eax, -12(%rsp)
movl -12(%rsp), %eax
ret
.globl _orfun_
.def _orfun_; .scl 2; .type 32; .endef
_orfun_:
movq %rcx, 8(%rsp)
movq %rdx, 16(%rsp)
movq 8(%rsp), %rax
movl (%rax), %eax
testl %eax, %eax
jne L8
movq 16(%rsp), %rax
movl (%rax), %eax
testl %eax, %eax
je L9
L8:
movl $1, -28(%rsp)
jmp L10
L9:
movl $0, -28(%rsp)
L10:
movl -28(%rsp), %eax
movl %eax, -12(%rsp)
movl -12(%rsp), %eax
ret
.globl _eqvfun_
.def _eqvfun_; .scl 2; .type 32; .endef
_eqvfun_:
movq %rcx, 8(%rsp)
movq %rdx, 16(%rsp)
movq 8(%rsp), %rax
movl (%rax), %edx
movq 16(%rsp), %rax
movl (%rax), %eax
cmpl %eax, %edx
sete %al
movzbl %al, %eax
movl %eax, -12(%rsp)
movl -12(%rsp), %eax
ret
.globl _neqvfun_
.def _neqvfun_; .scl 2; .type 32; .endef
_neqvfun_:
movq %rcx, 8(%rsp)
movq %rdx, 16(%rsp)
movq 8(%rsp), %rax
movl (%rax), %edx
movq 16(%rsp), %rax
movl (%rax), %eax
cmpl %eax, %edx
setne %al
movzbl %al, %eax
movl %eax, -12(%rsp)
movl -12(%rsp), %eax
ret
We can see from the above that .NOT. flips the LSB, .AND. considers
1 to be .TRUE. and everything else to be .FALSE., .OR. considers 0 to
be .FALSE. and everything else to be .TRUE., .EQV. returns 1 on
equality as integers and 0 otherwise, and .NEQV. returns 0 on
inequality as integers and 1 otherwise.
Does someone at C Central think up these things so that they might
be imposed on Fortran compilers?
BTW, it is so annoying that when gfortran generates a *.s file on
Windows that the records aren't properly terminated for a text file
in that context. Is there any possibility for gfortran's output
text files to receive line termination appropriate to the host OS?
Also if you look at the disassembly above negative offsets from rsp
are used but Windows is documented not to have a safe red zone.
> [ ... ]
> I guess it's a bug in gcc as well:
> [ ... ]
> int8_t i;
> if(*((_Bool*)(&i)))
You aren't *converting* i to a _Bool, you are *type-punning* the
bits of i to _Bool bits and all bets are off. If you want conversion
use a simple cast,
if((_Bool)i)
not an address/cast-to-pointer/dereference.
--
pa at panix dot com
> We can see from the above that .NOT. flips the LSB, .AND. considers
> 1 to be .TRUE. and everything else to be .FALSE., .OR. considers 0 to
> be .FALSE. and everything else to be .TRUE., .EQV. returns 1 on
> equality as integers and 0 otherwise, and .NEQV. returns 0 on
> inequality as integers and 1 otherwise.
Considering that only 0 and 1 are valid bit values for logicals,
all of the above make perfect sense.
> Does someone at C Central think up these things so that they might
> be imposed on Fortran compilers?
:-)
Actually, all of these come from telling the gcc middle and
back end that only 0 and 1 are valid values. gfortran could
do otherwise, but at the expense of speed.
> Is there any possibility for gfortran's output
> text files to receive line termination appropriate to the host OS?
Now PR 36603 .
Why? Most good text editors support all three different style of line
endings (Windows, Unix and Mac). WordPad is a free text editor that
comes with Windows that supports all three with no issues. This is
how I read most files that come from Unix under Windows.
Thanks,
Andrew Pinski
> Actually, all of these come from telling the gcc middle and
> back end that only 0 and 1 are valid values. gfortran could
> do otherwise, but at the expense of speed.
I don't understand where the speed thing comes in. Bitwise logical
operations certainly would have been faster than the abominations
the gfortran generated for logical operations upthread.
I just type "notepad L1.s" and get unreadable stuff. Then I start
Windows Explorer, navigate to the directory where L1.s resides,
right-click the file, select open with... then Wordpad, hit space,
backspace, <ALT>, F, X, <ENTER>, and then I can hit the up-arrow key
and restart the "notepad L1.s" command. I know I could do things
differently, like put Wordpad on my path, but I am used to using
Windows in a very basic format so that I can go onto someone else's
computer and be able to do the usual stuff without having to reorient
myself.
Same goes for UNIX: gfortran could terminate its *.s files with
achar(13)//achar(10), but many users like to examine such a file
with vi and would be inconvenienced by such line termination. When
I'm in UNIX I just grit my teeth (well, I would already be gritting
my teeth) and fix the line terminations with pico then restart vi.
It just makes sense to me to speak the language of the country you're
in if it's not too inconvenient to do so.
> I have a better solution. TRANSFER should be disallowed
> from being applied when either the source or the destination
> type is any KIND of LOGICAL (or indeed a derived type
> with any KIND of LOGICAL as a component). Evidently
> the warning that TRANSFER is ->*INHERENTLY*<-
> implementation dependent is insufficient. And the observation
> that the internal representation of a LOGICAL is ->*INHERENTLY*<-
> none of the programmer's business doesn't seem to penetrate
> either.
How about instead only licensed TRANSFER programmers are
allowed to use TRANSFER. A special exam that shows that
they only use it properly is required before the license
is issued. Compilers are required to detect the license
before compiling programs using TRANSFER.
(Maybe a license for floating point arithmetic would
also be a good idea.)
-- glen
As a sometimes hardware designer, I need in a language the capability to
inspect and change the internals of any particular data type, realizing
that it isn't portable. It's a "system programming" issue. These types
of capabilities are absolutely essential. It may be essential for me to
reference a particular value as various or particular data types in some
circumstances and as bit buckets in other circumstances.
>
> -- glen
>
--
Gary Scott
mailto:garylscott@sbcglobal dot net
Fortran Library: http://www.fortranlib.com
Support the Original G95 Project: http://www.g95.org
-OR-
Support the GNU GFortran Project: http://gcc.gnu.org/fortran/index.html
If you want to do the impossible, don't hire an expert because he knows
it can't be done.
-- Henry Ford
Having done embedded systems, I understand the need for the access at a
bit level; I don't understand why it _has_ to be "any particular data
type".
I admit I've done none of the embedded work I've done w/ Fortran,
however; but don't quite see why, for example, it would be mandatory to
operate on LOGICAL as opposed to INTEGER. UNSIGNED would surely be
handy, if not absolutely essential even for stuff I have done in
Fortran, however...
--
>When I put on my (MIT-earned) logic-professor hat, I say:
>
> *IT*IS*AN*ERROR* for PGI Fortran to give different
> results for the two IF's above.
>
>And I'm not happy about it.
I'm not surprised that it gets different results, especially if you
don't have optimization turned on. But I'm not an MIT-earned
logic-professor.
At high optimization, it might be a performance bug if they give
different answers. But not a bug bug.
-- greg
I see no good reason to arbitrarily restrict it, especially for
logical type.
>
> --
I see a rather obvious reason to restrict it. And it's not arbitrary:
the internal structure of a LOGICAL is undefined. I can think of
only one reason to mess around with the bits of a LOGICAL and
that's to reverse-engineer their internals in order to try to do
non-standard things with them. Such things are (and should
be) prohibited.
Quite agreed. Historically, I think this arose because F77 didn't supply
intrinsics for bitwise operations on integers, so people fumbled around
with various extensions to get that done. And we are still seeing the
results of that fumbling today.
If you want a LOGICAL, use one and live with the fact that, as James
says, the internal structure is undefined - just its semantics are
defined. If you want to manipulate an array of bits in the form of an
integer, do that; you just need to live with, and try to handle, the
fact that in this case, implementation has a tendency to shine through.
Jan
This whole thing started because *PGI violated the semantics*...
and if I understand the rest of the thread, so does "gfortran".
-- Carlie Coats
I believe that is open to debate - the actual value was set using
methods (e.g., TRANSFER) that are labelled "here be dragons, use at your
own risk".
Jan
> This whole thing started because *PGI violated the semantics*...
> and if I understand the rest of the thread, so does "gfortran".
No.
Use TRANSFER at your own risk, and at the compiler vendor's whim.
Not clear...I just went through the thread from your original post and
each response you made.
Nowhere have you provided the definition of ENVYN in the sample test code
> LOGICAL :: ENVYN, LVAR
> INTEGER :: ISTAT
>
> LVAR = ENVYN('LVAR','logical variable',.TRUE.,ISTAT)
or any other indication of how the actual value of LVAR was set except
as a result of the above function invocation.
Hence, as others have noted, unless only valid logical operations within
the scope of the Standard are used in the function, a bug has not been
demonstrated unequivocally. If, otoh, "tricks" such as TRANSFER or
other bit-twiddling are involved, all bets are off and it is the code
that is invalid, not the results.
It would take looking at ENVYN to make a judgment.
--
> I see no good reason to arbitrarily restrict it, especially for
> logical type.
That seems to me an entirely different thing than saying it is "needed
for any type".
I agree for some uses such as you've outlined interacting w/ hardware or
other reasons for a bit bucket the unsigned nature of LOGICAL vis a vis
INTEGER is appealing conceptually.
But, of course, as you've mentioned it isn't fair to expect that kind of
usage to be consistent w/ the Standard usage of LOGICAL or can
consistency of any sort be demanded or expected which is the problem of
the original poster (apparently altho there's still the possibility of a
bug; insufficient data on his particular sample code to tell so far afaict).
--
> I agree for some uses such as you've outlined interacting w/ hardware or
> other reasons for a bit bucket the unsigned nature of LOGICAL vis a vis
> INTEGER is appealing conceptually.
I don't understand that. Logical isn't so much unsigned as having no
concept of a sign, sort of like character. When I want just a bucket of
bits, as I occasionally do, I find integer to be the closest
approximation and that's what I use. Yes, the sign bit of integer has
some potential portability issues, which can be handled, but with
logical, all the bits have portability issues, so I don't see the appeal
of logical here.
It seems to me that integer is the least likely standard existing type
to have "surprising" behavior when treated as a bucket of bits. I've
seen people advocate using character, which strikes me as a really bad
idea (that's asking for things like stripping the top bit or "funny"
treatment of some special characters). Real has potential problems with
things like normalization. Although I haven't seen it, I could imagine
similar things happening to logical; something seemingly "innocuous"
like assignment could potentially "normalize" a logical value.
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
> I don't understand that. Logical isn't so much unsigned as having no
> concept of a sign, sort of like character. When I want just a bucket of
> bits, as I occasionally do, I find integer to be the closest
> approximation and that's what I use. Yes, the sign bit of integer has
> some potential portability issues, which can be handled, but with
> logical, all the bits have portability issues, so I don't see the appeal
> of logical here.
I can sort of see it. Not counting portability, LOGICAL does
have the advantage that all bits are equal. (That is, no
place values.) Well, I still remember the OS/360 days
when the only one byte type was LOGICAL*1. The problem
being that there was no way to compare two values.
With integer, the sign bit can be a problem. For ones
complement and sign magnitude a negative zero can easily
become positive zero. (Not that there are many such
machines around, though.)
> It seems to me that integer is the least likely standard existing type
> to have "surprising" behavior when treated as a bucket of bits. I've
> seen people advocate using character, which strikes me as a really bad
> idea (that's asking for things like stripping the top bit or "funny"
> treatment of some special characters). Real has potential problems with
> things like normalization. Although I haven't seen it, I could imagine
> similar things happening to logical; something seemingly "innocuous"
> like assignment could potentially "normalize" a logical value.
Assuming twos complement, I agree that integer should be most
portable. The main one I have heard about for LOGICAL is not
copying all the bytes on assignment. (That is, for the
variables separate from the question of logical operators.)
Given the preponderance of twos complement hardware, I
would have to agree that integer is more portable, but the
standard is neutral on that.
Other than I/O, I would agree that CHARACTER should be
a fine choice. I suppose it is non-portable in the size
of the character set. C guarantees at least eight bits,
though, so that should also be true for Fortran with
C interoperability.
-- glen
It (LOGICAL) is "unsigned" in my view precisely as you're saying--it
doesn't have any concept of sign; ergo, it doesn't have the sign that
Fortran INTEGER does have explicitly.
I can relate that places where I have used it, the difficulties of the
sign bit could/would have required fixup or a longer than default
integer. Either requires some additional effort. In limited hardware
environments typical of embedded systems both are major drawbacks where
both memory and clocks are often in very short supply. I agree it's
"do-able" which was the point of the query as to why Gary considered it
mandatory rather than a nicety but that's somewhat assuming that nay
system using Fortran won't be _too_ limited (but it's sometimes amazing
to find what tools are being used in what environments).
So, I'm not nor was I advocating it's use, simply saying I can see that
as a reason some might choose.
UNSIGNED INTEGER would be more suited for the purpose, but we don't have
it (or at least didn't--not sure if it has made it to latest/proposed
Standard or not).
I already agreed _anything_ done with it outside the definitions of the
Standard is unportable.
I've done very little that I can recall (over nearly 40 years, there's
quite a bit I don't remember much) that actually did bit-twiddling in
Fortran (as noted, my embedded/hardware era work was almost exclusively
in the province of Forth/assembler, not Fortran) and I can remember
precious few places where needed the facility in Fortran work I have done.
--
> Richard Maine wrote:
> > dpb <no...@non.net> wrote:
> >
> >> I agree for some uses such as you've outlined interacting w/ hardware or
> >> other reasons for a bit bucket the unsigned nature of LOGICAL vis a vis
> >> INTEGER is appealing conceptually.
> >
> > I don't understand that....
> I can relate that places where I have used it, the difficulties of the
> sign bit could/would have required fixup or a longer than default
> integer.
But how is logical any better? True, the "fixup" needed to deal with
sign bit issues can be a hassle, particularly if you want to be
portable. But it isn't as though logical has anything better. In fact, I
can't think of any way that is even close to standard or portable to put
individual bits into or get them out of a logical variable. When I
compare "requires some additional effort" to "can't be done", then
"requires some additional effort" wins pretty easily.
Note, for example, that the standard Fortran bit intrinsics are defined
only for integers. They do "punt" on the sign bit. But they don't apply
to *ANY* of the bits of any other type.
In fact, if you were to ask me how to put arbitrary bits into a logical
variable, the only ways that occur to be off-hand are to first put the
bits into an integer and then type-pun that integer to a logical (using
any of several methods - transfer, equivalence, intentional argument
mismatch, binary I/O, etc).
Now if you are talking implementations where "logical" seems to be just
a funny way of spelling "unsigned integer", complete with numeric
arithmetic, then I'd agree that would fit, but that's a quirk of some
implementations rather than anything that has much to do with normal
Fortran logicals. I suspect that might be the basis for some people's
pereference - either that or some notion that a logical is conceptually
like a bit. I'd even agree with the notion that a logical is like a
single bit, but I have trouble translating that into one logical being
like an array of multiple bits.
> So, I'm not nor was I advocating it's use, simply saying I can see that
> as a reason some might choose.
And I'm certainly not arguing that people should have to follow my
preference on the matter. None of the answers being "right", there is
obviously an element of personal perspective on which one fits "best".
Oh. And I should add that I was not agreeing with the suggestions that
TRANSFER be prohibitted on logicals. I was assuming that those
suggestions were not really serious, but if they were serious, then I
disagree. TRANSFER between types is fundamentally non-portable. If one
wants to play whack-a-mole with ways that people can misuse it, before
long you'll have eliminated the whole feature. There are times when one
needs it, portable or not.
I'm saying only that when dealing with hardware, portability isn't
usually a huge concern for my applications. I'm trying to get some
work done with the equipment I'm given. I'm designing a hardware
system tied to a particular architecture, OS, and 3rd party support
libraries. Sure, I might be able to kludge an interface with another
data type, however, in some particular circumstatnce, logical might be
just the right thing. I'm just saying don't take my choices away.
> [...] in some particular circumstatnce, logical might be
> just the right thing. I'm just saying don't take my choices away.
Denying a choice that you never really had is not the same
as taking a choice away.
I don't recall ever claiming anything about "better"; only that I could
understand why some would choose a LOGICAL since there is not UNSIGNED.
I also (as noted) specifically disclaimed recollecting I had ever used
it for the purpose; noting I don't even recall any bit-twiddling in
Fortran of the type Gary was referring to as my realtime/embedded
systems work was never in Fortran(*).
I also noted it was admittedly non-Standard to do anything of the sort
and so one would presume if one chose to do so, that whatever intrinsics
were supported on the particular system would be fair play. I again
would also presume that for embedded systems where processor and memory
limits are constraining and for realtime there would be specific
compiler/target combinations.
It seems you're trying to make far more out of what I said than I did
and I'll not try to defend something I didn't say.
Oh...it just came to me where I indeed did do some "near realtime"
although certainly not embedded FORTRAN--would have been on HP1000-based
5451C Fourier Analyzer instrument data acq system at the K-25 seismic
test facility during the gas centrifuge separation project in the '70s
and early '80s. It did have a fair amount of that kind of stuff in a
particular portion of the code but I can no longer remember the code
well enough nor the HP dialect to even recall how it was done. Quite
possibly FORTRAN-callable HP assembler altho there were quite a number
of implementation-specific extensions on that instrument I don't recall
whether BIT operations on LOGICAL was one or not. I can well imagine if
it didn't and I wanted to work around the sign I wouldn't have been
above EQUIVALENCE'ing two, either.
In situations like that which are the sort of thing I think Gary is
into, the question of portability is totally moot--there's no other
system in the world the code could ever possibly run on, anyway.
It's a completely different environment than you're used to if I
understand at all your background.
--
--
In that context, I agree wholeheartedly. See my response to Richard in
which I wax on (I won't claim poetically :) ) about the type of thing...
--
IMO, it's problematic to be claiming any sort of a bug when you're doing
what are effectively reinterpret_cast games (although the exact
mechanism here is C-style casts which may or may not translate to a
combination of C++-style casts). I believe that C++ has the same sorts
of disclaimers about your standing with the standard as Fortran has for
TRANSFER.
I'd be far more interested if you were using something like
static_cast<bool>(i), which IIRC should have a well-defined result.
I think you missed the transition in the discussion to C++. bool is a
built-in type in C++ that does not require any includes, and at least as
of C++98, _Bool does not exist at all.
>> Only if you #include <stdbool.h>. Otherwise, it is called _Bool.
> I think you missed the transition in the discussion to C++. bool is a
> built-in type in C++ that does not require any includes, and at least as
> of C++98, _Bool does not exist at all.
C99 was also in the discussion, but I don't know it well enough
to comment about it. I believe C99 is doing better than Fortran 2003
in getting compilers to accept it, though I don't know that so
many people actually use it.
-- glen
F03 is doing better than C99 did with uptake, I think, in the sense that
I don't know (offhand) of any vendors who are outright refusing to add
features because they don't think it's worth the effort. IIRC, for a
time period Microsoft was basically punting on most of C99 because they
didn't think their customers were interested. (This may have changed
more recently, I haven't been paying much attention to what happens with
Microsoft compilers since I stopped using them regularly.) F03 features
are slow in arriving, but they seem to be creeping into every compiler
still undergoing active development.