[comp.std.c] the logical xor operator!

maart@cs.vu.nl (Maarten Litmaath) (06/24/88)

Hello, it's me again.
This time the question is: why hasn't C got a logical xor operator?

	logical and: &&
	logical or:  ||
	logical xor: ^^		<- fits nicely!

Of course the way to come around the deficiency is:

	if (!(expr1) != !(expr2)) {
		...
	}

but this only works if !0 always evaluates to the same value (viz. 1), that is,
the optimizer is not allowed to f*ck around with the comparison.
!0 is indeed a constant, isn't it?
-- 
I'd rather live in Russia             |Maarten Litmaath @ Free U Amsterdam:
              than in South-Africa... |maart@cs.vu.nl, mcvax!botter!ark!maart

karl@haddock.ISC.COM (Karl Heuer) (06/25/88)

In article <1310@ark.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>This time the question is: why hasn't C got a logical xor operator?

Since you posted the question to comp.std.c, I'll interpret the question as
"Why wasn't logical xor added to ANSI C?".  The answer is that it isn't useful
enough to be worth the change to the language.

>Of course the way to come around the deficiency is: [ !x != !y ]
>but this only works if !0 always evaluates to the same value (viz. 1),

Which it is and always has been.

Of course, if you're dealing with true boolean expressions (and I hold the
opinion that nothing else should be handed to a logical operator), then either
"x != y" or "x ^ y" will do.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

jfh@rpp386.UUCP (John F. Haugh II) (06/26/88)

In article <1310@ark.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>Of course the way to come around the deficiency is:
>
>	if (!(expr1) != !(expr2)) {
>		...
>	}
>
>but this only works if !0 always evaluates to the same value (viz. 1), that is,
>the optimizer is not allowed to f*ck around with the comparison.
>!0 is indeed a constant, isn't it?
>-- 
>I'd rather live in Russia             |Maarten Litmaath @ Free U Amsterdam:
>              than in South-Africa... |maart@cs.vu.nl, mcvax!botter!ark!maart

[ hey, chris, here is topic #2 for your posting ]

which, as this discussion went a few months ago, is exactly the case.  the
code you've writen above, modulo any blindness on my part, should work perfect
as an exclusive-or function.  remember, exclusive-or is the not-equivalant
operator, it returns true when the two operands are not boolean equivalant.
the implied comparision against zero is performed automagically in the case
of || and &&.

as far as the optimizer is concerned, it should still be able to perform
any optimizations which would be allowed for any other boolean operators,
except you can't short circuit != or == as you can && and ||.  in particular,
i believe (a == 0)^(b == 0) most likely can't be as well optimized as
(a == 0)!=(b == 0).  i can't prove this to be true, but i very well suspect
it may be.

- john.

ps - your .signature is thought provoking.  i can't decide whether i'd
     rather live in a large communist country which oppresses blacks
     or a small capitalist country which oppresses blacks.  hard choice.
-- 
John F. Haugh II                 +--------- Cute Chocolate Quote ---------
HASA, Division "S"               | "USENET should not be confused with
UUCP:   killer!rpp386!jfh        |  something that matters, like CHOCOLATE"
DOMAIN: jfh@rpp386.uucp          |             -- with my apologizes

karl@haddock.ISC.COM (Karl Heuer) (06/28/88)

In article <3254@rpp386.UUCP> jfh@rpp386.UUCP (The Beach Bum) writes:
>in particular, i believe (a == 0)^(b == 0) most likely can't be as well
>optimized as (a == 0)!=(b == 0).

Nonsense.  "!=" and "^" produce the same result when both operands are
normalized booleans; therefore a compiler which detects this situation is free
to generate its favorite logical-xor construct for both.  In practice, few if
any compilers bother to do this.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

chris@mimsy.UUCP (Chris Torek) (06/28/88)

>In article <3254@rpp386.UUCP> jfh@rpp386.UUCP (The Beach Bum) writes:
>>in particular, i believe (a == 0)^(b == 0) most likely can't be as well
>>optimized as (a == 0)!=(b == 0).

In article <4772@haddock.ISC.COM> karl@haddock.ISC.COM (Karl Heuer) writes:
>Nonsense.  "!=" and "^" produce the same result when both operands are
>normalized booleans; therefore a compiler which detects this situation is free
>to generate its favorite logical-xor construct for both.  In practice, few if
>any compilers bother to do this.

In fact, the 4BSD Vax PCC does nothing special about either one.  For

	if ((a==0) != (b==0))
		code;

one gets code of the form

	test a
	branch if not 0 -+
	r0 = 1		 |
    +---branch		 |
    |  	r0 = 0 <---------+
    +->	test b
	branch if not 0 -+
	r1 = 1		 |
    +---branch		 |
    |  	r1 = 0 <---------+
    +->	compare* r0,r1
	branch if equal -+
	<code>		 |
	... <------------+

The only difference for

	if ((a==0) ^ (b==0))

is that the `compare' (marked with `*') turns into an `xor'.

For assignment statements, however,

	c = (a==0) != (b==0);	/* or c = (a==0) ^ (b==0) */

the code remains the same up until the `*' above; from there it
reads either

	compare r0,r1
	branch if equal -+
	r0 = 1		 |
    +---branch		 |
    |	r0 = 0 <---------+
    +->	move r0,c

or

	xor r0,r1
	move result,c

Hence in a dumb compiler (PCC) the logical operation is more efficient.
A good `rule of thumb' is this:

    -------------------------------------------------------------
    Logical operators are never slower than arithmetic operators.
    -------------------------------------------------------------

[are there any machines for which this is false?]

Comparison of two arbitrary quantities (as by a dumb compiler above)
is a subtraction, which is arithmetic, hence ^ either ties or wins.

Incidentally, in the 4.3BSD-tahoe Vax compiler, neither

	if ((a == 0) != (b == 0))

nor

	if ((a == 0) ^ (b == 0))

produce the best code.  The best logical exclusive or sequence for
`if' statements comes from

	if (a ? !b : b)

which generates only 7 instructions, of which at most 5 are executed.
Part of this is from some tweaks Donn Seeley and I installed recently
to improve the code generated for the getc() and putc() macros.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

maart@cs.vu.nl (Maarten Litmaath) (06/28/88)

In article <3254@rpp386.UUCP> jfh@rpp386.UUCP (The Beach Bum) writes:
\ps - your .signature is thought provoking.  i can't decide whether i'd
\     rather live in a large communist country which oppresses blacks
\     or a small capitalist country which oppresses blacks.  hard choice.

I CAN decide whether I'd rather live in a country in which maybe not all
people get on equally well, but at least are equal for the law, or a country
in which the law states there be an apartheid between black and white (very
smart solution to prevent the black majority from regaining the power and
wealth stolen from them)...

Now about the topic in the subject line.
Karl W. Z. Heuer writes:
>"Why wasn't logical xor added to ANSI C?".  The answer is that it isn't useful
>enough to be worth the change to the language.

Well, it's strange K&R didn't think of it in the first place (don't give
me the story about shortcutting: that's no reason for not implementing ^^).
Furthermore, it can be implemented WITHOUT breaking existing code, and
(admittedly) sometimes one would wish for ^^; but then again: isn't Karl
having problems himself to get \c accepted by ANSI? :-)

ANSI committee: "Yes, it's kinda nice. Go back and do your homework properly!
		Come back to us if you've found something dubious, about which
		we can spend a week arguing!"

>Of course, if you're dealing with true boolean expressions (and I hold the
>opinion that nothing else should be handed to a logical operator), then either
>"x != y" or "x ^ y" will do.

Come on Karl, what's so terrible about the following common practice?

	if (some_pointer && ...) {
		...
	}
-- 
I'd rather live in Russia             |Maarten Litmaath @ Free U Amsterdam:
              than in South-Africa... |maart@cs.vu.nl, mcvax!botter!ark!maart

karl@haddock.ISC.COM (Karl Heuer) (06/29/88)

Let's leave the political discussions in a different newsgroup, okay?

In article <1312@ark.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>Karl W. Z. Heuer writes:
>>"Why wasn't logical xor added to ANSI C?".  The answer is that it isn't
>>useful enough to be worth the change to the language.
>
>Well, it's strange K&R didn't think of it in the first place...

"Why didn't K&R make a logical xor operator?" is a different question.  I'll
be glad to take this up in comp.lang.c after I return from vacation.

>Furthermore, it can be implemented WITHOUT breaking existing code,

This (or a weakened form of it) is a necessary condition for ANSI acceptance,
but it is not sufficient.

>and (admittedly) sometimes one would wish for ^^; but then again: isn't Karl
>having problems himself to get \c accepted by ANSI? :-)

It's hard to get anything new accepted by X3J11.  This is as it should be.

Do you want this operator just for completeness, or do you think you'd
actually use it a lot?  I can only think of one routine I've ever written in
which I've needed a logical xor, and since both operands were boolean I simply
used "^".

>>(and I hold the opinion that nothing else [but a normalized boolean] should
>>be handed to a logical operator)
>
>Come on Karl, what's so terrible about the following common practice?
>	if (some_pointer && ...) { ... }

Well, since you asked: The problem with it is that, by looking at this code
fragment in isolation, it's not obvious that some_pointer is a pointer, rather
than an integer, a character, a real, or a boolean.  If you explicitly write
	if (p != NULL && i != 0 && c != '\0' && r != 0.0 && b) { ... }
the reader has more information, which often makes the code more readable.

As I said, this is my personal opinion; I certainly don't think that the
language should be changed to enforce it.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

smryan@garth.UUCP (Steven Ryan) (06/29/88)

The lack of a boolean exclusive-or is probably cultural. Xor is important
for bit fiddling, but few predicates use it. An equivalence operator (even
if it is just a negated xor), on the other hand is useful for boolean but
not bits.

>A good `rule of thumb' is this:
>
>    -------------------------------------------------------------
>    Logical operators are never slower than arithmetic operators.
>    -------------------------------------------------------------
>
>[are there any machines for which this is false?]

Depends how you implement booleans. On a pipelined machine, jumps kill you.

castillo@mit-amt.MEDIA.MIT.EDU (Brian C. Anderson) (06/29/88)

In article <1312@ark.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
>In article <3254@rpp386.UUCP> jfh@rpp386.UUCP (The Beach Bum) writes:
>\ps - your .signature is thought provoking.  i can't decide whether i'd
>\     rather live in a large communist country which oppresses blacks
>\     or a small capitalist country which oppresses blacks.  hard choice.
>
>I CAN decide whether I'd rather live in a country in which maybe not all
>people get on equally well, but at least are equal for the law, or a country
>in which the law states there be an apartheid between black and white (very
>smart solution to prevent the black majority from regaining the power and
>wealth stolen from them)...

"equal for the law"?  Fat chance if you've got Jewish parents, or are
a practicing Christian, or you fall into any number of unacceptable
political/religious/ethnic catagories.  One of the most unbelievable
spectacles of the modern age is seeing people favorably compare the
Soviet Union's human rights record with that of South Africa.  At
least in that country if you don't like the status quo you can
emigrate without waiting 20 years for an exit visa.

BTW, what does any of this have to do with C?

alex@dbase.UUCP (Bob Alexander) (07/02/88)

In article <833@garth.UUCP> smryan@garth.UUCP (Steven Ryan) writes:
>The lack of a boolean exclusive-or is probably cultural. Xor is important
>for bit fiddling, but few predicates use it. An equivalence operator (even
>if it is just a negated xor), on the other hand is useful for boolean but
>not bits.
>

I would guess that much of the reason for including || and && in C was
to provide the advantage of their short-circuiting properties.
Short-circuiting could not be done with ^^.  I wouldn't mind having it
around, for completeness, but I would probably rarely use it.  Just for
kicks, how about:

#define logical(x) ((x) != 0)	/* logical affirmation */
#define logicalxor(x,y) (logical(x) != logical(y)) /* logical xor */
#define logicaleq(x,y) (logical(x) == logical(y)) /* logical equivalence */

For as often as I would use them, this would suffice.  I would probably
use the first one the most.

Disclaimer: I accept no liability for possible misuse of the word
"affirmation".  Furthermore, the above definitions might not work at
all -- haven't tried 'em.

mmengel@cuuxb.ATT.COM (~XT4103000~Marc Mengel~C25~G25~6184~) (07/02/88)

>>Of course, if you're dealing with true boolean expressions (and I hold the
>>opinion that nothing else should be handed to a logical operator), then 
>>either "x != y" or "x ^ y" will do.
>Come on Karl, what's so terrible about the following common practice?
>	if (some_pointer && ...) {
>		...
>	}

Ah, but fortunately C gave us a cheap and easy way to convert to
"Boolean" --  so "!!x ^ !!y" does pretty well.  I think the short
circuit evaluation is the real reason -- after all, you could
short circuit evaluate "x & y" if x is 0, and "x | y" if x is ~0,
but you *don't* because then you would have to use explicit temp
variables to evaluate both sides of the bitwise operator.
-- 
 Marc Mengel			       
				
 attmail!mmengel	
 {lll-crg|mtune|ihnp4}!cuuxb!mmengel

bill@proxftl.UUCP (T. William Wells) (07/03/88)

In article <1310@ark.cs.vu.nl>, maart@cs.vu.nl (Maarten Litmaath) writes:
> Hello, it's me again.
> This time the question is: why hasn't C got a logical xor operator?
>
>       logical and: &&
>       logical or:  ||
>       logical xor: ^^         <- fits nicely!
>
> Of course the way to come around the deficiency is:
>
>       if (!(expr1) != !(expr2)) {
>               ...
>       }

The main reason for not adding your logical xor operator is this:
the other two operators imply sequencing of evaluation, the
logical xor operator can't.

	A && B

means evaluate A and if it is true, evaluate B. The result of the
expression is true (1) if the last expression evaluated is nonzero.

However, your xor operator requires that both operands be
evaluated; this makes it a bad idea to create an operator
analogous to the others.

This is not meant to imply that the functionality of a logical
xor is not useful, only that that syntax would be a bad idea.

If I really needed this functionality, I would create a macro
logxor(a,b), using the expression you specified.  However, it is
rarely the case that I need this for arbitrary arguments;
normally, I do this with logical values and for these the
regular xor (^) operator is sufficient.