logical vs. binary operators
Martin Kalbfuß

Hi,

I realised that the the logical operators && and || are doing the same as & and |.

int res1 = 5 == 5 && 3 < 4
int res2 = 5 == 5 & 3 < 4

return the same result. I always used them without thinking about their difference. So why do I need extra operators for logical operations?

Thanks

anonymous

Short circuiting on logical operators. And you have to be careful to use the binary operators only with boolean values or you won't get the same results.

#SelectExpand
1#include <stdio.h> 2 3int one() { return 1; } 4int two() { return 2; } 5 6int main() 7{ 8 if (one() && two()) { 9 puts("Ok with logical and"); 10 } 11 if (one() & two()) { 12 puts("Ok with binary and"); 13 } 14 return 0; 15}

Matthew Leverton

& is binary:

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

So if both sides of (foo == 1) & (bar == 2) evaluate to true, then the expression is true. But that only works with boolean values. Consider:

if (1 & 2) {
  // not true
}

if (1 && 2) {
  // true
}

Also, logical operators will short circuit.

#SelectExpand
1int t() 2{ 3 return 1; 4} 5 6int f() 7{ 8 fire_nukes(); 9 return 0; 10} 11 12if (t() || f()) 13{ 14 // world is safe ... f() never called 15} 16 17if (t() | f()) 18{ 19 // world is blown up 20}

They are used for different things ... although many times they are interchangeable.

Arthur Kalliokoski

Or to rephrase:


001001001 binary A
bitwise or'ed with (|)
000100001 binary B
---------
001101001  Return all bits that are set as in "Is this particular bit set in A or B?"
Not the same as asking little Tommy if he wants cake OR ice cream for dessert and he 
says "Yes" (Exclusive OR is what was wanted there)

001001001 binary A
logically or'ed with {||)
000100001 binary B
---------------
NNYYNYNNY   <- where N means neither bit is set, and Y means at least one bit is set,
and Yes overrides No.  Since one or more Yes'es are there, return TRUE or 1.

Tobias Dammers

I realised that the the logical operators && and || are doing the same as & and |.

They don't.

int a = 2 & 4;
int b = 2 && 4;

printf("a = %i", a);
printf("b = %i", b);

The logical operators evaluate each operand as a boolean, perform their logic on them, and return a boolean.
The binary operators evaluate each operand as an integer, perform their logic on individual bits, and return an integer.

tobing

Oops.

& is a bit-wise operation on the operands, i.e. the result has a bit set only if both operands have the same bit set.

&& is a logical operation on two bool values and is true only if both operands evaluate to true.

| is a bit-wise operation, a bit in the result is set if the corresponding bit is set in one of both operands.

|| again is a logical operation between two bool operands.

So considering the bit-representation of numbers, you get 5 | 8 = 13, 5 | 7 = 5, 5 & 8 = 0, 5 & 7 = 5.

ImLeftFooted

I think | is always the same as ||... (in a condition)

Kitty Cat

|| gives a boolean result, | gives an integer..

if((a || b) == (c || d)) ...
// vs
if((a | b) == (c | d)) ...

Audric

Dustin: If both operands are booleans (only values 0x00000001 and 0x00000000), and if there are no side effects of evaluating the right one, and if you only care if the result equals zero; then yes, you get the same result.

Note that under the same conditions, + is the same as ||

ImLeftFooted
Audric said:

Note that under the same conditions, + is the same as ||

I dunno, not really.

if(UINT_MAX + 1)
  // nope

While

if(UINT_MAX | 1)
  // yup

My main point was that he probably thought | -> || and & -> && were equivalent because effectively | -> || really is. It would be logical (without knowing more) that & would expand to && as well, especially because it does if you're lucky.

Arthur Kalliokoski

Doesn't C++ require Booleans to be 1... uh, or 0 exclusively? 4 | 6 isn't the same as 4 || 6 then.

Jonatan Hedborg

| still does not short circuit.

Kitty Cat

Doesn't C++ require Booleans to be 1... uh, or 0 exclusively?

From what I remember being told, bools in C++ are not numbers, but are either true or false. Nothing else. False is interpreted as integer 0 (same as NULL is interpreted as integer 0, even if the NULL address is not 0x00000000), and true is a non-0 integer. Most compilers will use 1, but -1 and 124354245 are just as valid.

Arthur Kalliokoski

Sorry, I was misremembering what I'd read in http://www.agner.org/optimize/optimizing_cpp.pdf.

Quote:

Boolean variables are stored as 8-bit integers with the value 0 for false and 1 for true.
Boolean variables are overdetermined in the sense that all operators that have Boolean
variables as input check if the inputs have any other value than 0 or 1, but operators that
have Booleans as output can produce no other value than 0 or 1. This makes operations
with Boolean variables as input less efficient than necessary.
...
This is of course far from optimal. The branches may take a long time in case of
mispredictions (see page 38). The Boolean operations can be made much more efficient if it
is known with certainty that the operands have no other values than 0 and 1. The reason
why the compiler doesn't make such an assumption is that the variables might have other
values if they are uninitialized or come from unknown sources.

Audric

Dustin: I said "under the same conditions".... UINT_MAX is not equal to 0x00000001 or 0x00000000.

Unrelatedly, here are two valid uses of the operators, where you really shouldn't mistake one for the other.

// Use high quality sprite if available, or use the normal one.
// (If there was a big problem, none is available, so use a generic "missing" sprite)
BITMAP *sprite = high_quality_sprite || normal_quality_sprite || sprite_missing;

...

// Store multiple related booleans in a single field.
this->Collision_flags = COLLISION_SOLID | COLLISION_GRAVITY | COLLISION_PUSHABLE;

Kitty Cat
Audric said:

BITMAP *sprite = high_quality_sprite || normal_quality_sprite || sprite_missing;

This won't do what you think it does.

Audric

What did I miss ? Associativity is left-to-right, I just checked. I admit it's not a construct I actually use, so I'd rather know what's the mistake.

Arthur Kalliokoski

You're messing with the address of the bitmap struct location, not setting attributes.

Jonatan Hedborg

That should not matter. If the first one is zero, the next one will be returned (etc). I can't see why that would fail... Though I only ever used it in actionScript and ruby.

Audric

Arthur Kalliokoski: That was my intention... the two examples are not necessarily related. When I wrote the second I was tempted to rewrite the first as "this->sprite = ...", but then you wouldn't see that the type of "sprite" is "BITMAP *", and this seemed required for understanding...
My example relies on the fact that NULL is the only value of a pointer that "is not true".

Tobias Dammers

Doesn't C++ require Booleans to be 1... uh, or 0 exclusively? 4 | 6 isn't the same as 4 || 6 then.

No. It requires boolean false to evaluate to zero when cast to integer, and boolean true to something non-zero. 1 is common, but AFAIK any other non-zero value would be valid too. The important thing is that when you cast a boolean to int and then back to boolean, the original value must be retained.
4 | 6 returns 6, 4 || 6 returns true. If you interpret the result as a boolean, both are equivalent, but not if you interpret them as integers.

The most important point IMO however is readability. Use the distinction between logical and bit-wise operator to clearly indicate your intention, even if they happen to be interchangeable.

Matthew Leverton

That should not matter. If the first one is zero, the next one will be returned (etc). I can't see why that would fail... Though I only ever used it in actionScript and ruby.

printf("%d\n", 10 || 20 || 30);

What do you expect that to display (in C/C++)?

Correct Answer: 1

Jonatan Hedborg

Oh that's intresting. Never tried it in C/C++, just assumed it worked as in other languages :)

Thomas Fjellstrom

Indeed boolean operators evaluate to a boolean value. ie: 0 or 1.

Ron Novy

It's simple when you think about it like this. Boolean operators are either equal to 0 (FALSE) or not equal to zero (TRUE)... So any value that is not zero is TRUE.

#SelectExpand
1int x = 0; /* == FALSE */ 2int y = 10; /* == TRUE */ 3int z = -30; /* == TRUE */ 4 5if (x) 6 printf("x == 1 == TRUE\n); // <- This string is never displayed because x == 0 7 8if (z) 9 printf("z == 1 == TRUE\n"); // <- z evaluates to TRUE or 1 (same with y). 10 11if (x || y) 12 printf("x || y == 1 == TRUE\n"); 13 14if (x && y) 15 printf("x && y == 1 == TRUE"); // <- This string is never displayed... 16 17if (!x && z) 18 printf("!x && z == TRUE\n"); // <- !x == 1 and z == 1 so this is displayed.

Bottom line is, the two types of OR and AND operators should not be confused with each other. You will get unexpected results...

[edit]Arg... I was too slow...

bamccaig

The confusion comes from modern languages like ECMAScript[1] (and evidently Ruby and others). They do work as Audric described. The operation evaluates to whatever side is last evaluated (for both && and ||, IIRC). It's a little bit of a logical fallacy, I suppose, but it does result in some fun shortcuts. Search for "last value" in this Wikipedia article.

References

  1. The base of JavaScript and ActionScript.
Audric

I feel very embarrassed... :-[ Looks like the little of Perl that I had, years ago, has contaminated my knowledge of C :(

By the way, I just tested with gcc on win32, and the 'booleans' produced by logical operators seem to be 4-byte size, even when not "mixed" with other data types. I tested using sizeof((1==1)) and normal optimization.

Arthur Kalliokoski

Heh, I remember showing an instructor in the '80's how an Apple II wouldn't accept my "MOVE A TO B" command. He points out that MOVE is a COBOL keyword (yes, studying both languages at the same time).

ImLeftFooted

Technically || could return the second element for some given types. Also | can be used as a || for some given types.

template<class C, class FalseT>
C &operator ||&(C &a, C &b, FalseT f = false)
{
  if(a)
    return a;
  if(b)
    return b;
  return f;
}

Uh oh... whats going to happen now? :-X

int i = 0 || 5;

Tobias Dammers
Audric said:

By the way, I just tested with gcc on win32, and the 'booleans' produced by logical operators seem to be 4-byte size, even when not "mixed" with other data types.

It is pretty common for compilers to base booleans on the platform's native integer type. For x86, that's a 32-bit integer.
The reason is simple: There is little to gain and much to loose by using a smaller data type. There is no speed gain (since the CPU executes most operations on full 32-bit registers anyway), and addressing memory below the register size may cause misaligned memory, which actually decreases RAM performance.

Thread #603157. Printed from Allegro.cc