9

Bitwise operators work on bits, logical operators evaluate boolean expressions. As long as expressions return bool, why don't we use bitwise operators instead of logical?

In this example I use bitwise instead of logical:

#include <iostream>


int main(){

    int age;
    std::cin >> age;

    if( (age < 0) | (age > 100) ) // eg: -50: 1 | 0 = 1
        std::cout << "Invalid age!" << std::endl;

//  if( (age < 0) || (age > 100) )
//      std::cout << "Invalid age!" << std::endl;

  return 0;
}
Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
  • 7
    For simple booleans, sure. The question I'd pose to you is _why_? Is it worth potential bugs to save 1 keystroke per operator? – erip Oct 31 '16 at 11:08
  • @erip still didn't get what you mean –  Oct 31 '16 at 11:12
  • 2
    @Alex24 erip is asking the opposite question. Why would you use | instead of ||. Just to shortcut one byte?? – Humam Helfawi Oct 31 '16 at 11:15
  • Possible duplicate of [Using bitwise operators for Booleans in C++](http://stackoverflow.com/questions/24542/using-bitwise-operators-for-booleans-in-c) – GingerPlusPlus Oct 31 '16 at 11:22
  • To counter example all the excellent answers you have got on why you shouldn't do this, there is one case where you should: Cryptography. This is the case where you actively want to *avoid* the optimization. If it takes less time to answer in one case than the other, this gives the attacker the opportunity to work out *why* you gave the answer you did (which is bad). – Martin Bonner supports Monica Oct 31 '16 at 11:49
  • @erip - using bitwise operators doesn't even save keystrokes, since you need extra parentheses to make the expression correct. `if (age < 0 || age > 100)` versus `if ((age < 0) | (age > 100))`. – Pete Becker Oct 31 '16 at 13:05
  • @PeteBecker I can see you've taken away the right message from my rhetorical question. ;) – erip Oct 31 '16 at 13:06

5 Answers5

13

One possible answer is: optimization. For example:

if ((age < 0) | (age > 100))

Let assume that age = -5, no need to evaluate (age > 100) since the first condition is satisfied (-5<0). However, the previous code will do evaluate the (age > 100) expression which is not necessary.

With:

if ((age < 0) || (age > 100))

Only the first part will be evaluated.

Note: As @Lundin mentioned in the comments, sometimes | is faster than || due to the accuracy of branching for the second option (and the problem of mis-prediction). So in cases where the other expression is so inexpensive, the | option may be faster. So the only way to know in those cases is to benchmark the code on the target platform.


The most important answer is to avoid undefined behaviors and errors:

You may imagine this code:

int* int_ptr = nullptr;
if ((int_ptr != nullptr) & (*int_ptr == 5))

This code contains undefined behaviour. However, if you replace the & with &&, No undefined behaviour exists anymore.

Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
3

You shouldn't. Assume you receive two values which allow you to proceed only if they are both non-zero.

int b = foo(1); // returns 0x1
int c = foo(2); // returns 0x2

Than the following conditions result in the follows: b && c == true, while b & c == 0

if (b && c)
{
  // This block will be entered
}

if (b & c)
{
  // This block won't
}
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 2
    Implicitly you're checking that `b != 0 && c != 0`, so it would still work if you wrote the long-form version. Still a good answer and most people (myself included) would write it as you have. – erip Oct 31 '16 at 11:18
  • @StoryTeller, you should compare with `b!=0 & c!=0`. Not `b&c`. – Pacerier Jun 05 '20 at 03:45
2

There is a distinct difference between || and |.

Unlike most other operators in the language, the logical || operator explicitly specifies the order of evaluation. The first operand of || must be evaluated before the second. The second need not be evaluated at all.

This is fundamentally different from | which behaves as most operators: the order of evaluation is unspecified, and both expressions will be evaluated. Even in the case where one operand is found to be non-zero, the other operand will still be evaluated for side effects.

Meaning that code like f1() || f2() will always evaluate to this pseudo code:

if(f1() != 0)
{
  f2();
}

whereas f1() | f2() will execute both functions, in an unspecified order that the programmer can't know.

This also means that statements like "|| is faster than |" are naive. Sure, in case of || the second operand isn't necessarily evaluated, but this comes at the cost of a branch, as well as restrictions for how the compiler is allowed to re-order the expression. Which operator that is generally faster isn't obvious.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    Citation needed for "unspecified order". – Pacerier Jun 05 '20 at 03:46
  • @Pacerier All operators follow the rule in C17 6.5/3 unless explicitly specified otherwise: "Except as specified later, side effects and value computations of subexpressions are unsequenced." This part was far more readable in older versions of the language, but the meaning is the very same. From C99 6.5/3: "Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified." – Lundin Jun 05 '20 at 06:31
  • @Pacerier And a C programmer is expected to know this without reading standard quotes. Order of evaluation is an important part of the language. Unfortunately, beginner classes and books rarely address the subject, but are instead obsessed with operator precedence only. – Lundin Jun 05 '20 at 06:34
1

Even if you achieve the same result with the bit-wise operator, it's better to use logical operator here due to performance reason.

In expression (age < 0) || (age > 100) second condition (age > 100) will be calculated only if (age < 0) is false. For such expression compiler produce code like this:

cmpl   $0x0,-0x4(%rbp)                 
js     1004010f9 <main+0x19>  // <-- Skip right expr evaluation if left true
cmpl   $0x64,-0x4(%rbp)               
jle    100401100 <main+0x20>

|| doesn't produce any extra branching to be able to skip second expression evaluation.

Nikita
  • 6,270
  • 2
  • 24
  • 37
  • It isn't obvious that `||` is faster. The "short-circuit" evaluation means that the `||` operator must come with a branch, unlike `|`. – Lundin Oct 31 '16 at 14:04
  • @Lundin You are right. But in terms of evaluation of left and right part of expression it's faster. I don't say it always faster. `|` could give better performance result, but the original expression should be rewritten in form with less number of `<` operations. – Nikita Oct 31 '16 at 14:51
0

The answer is yes, you can. The question is why would you want to? I can name a few reasons you shouldn't:

  1. It can be very confusing for other programmers.
  2. It's easy to miss that one of the operands is not of type bool, which can lead to subtle bugs.
  3. The order of operand evaluation is undefined.
  4. It messes up the short-circuiting rule.

To illustrate the last point:

bool f1() {
    cout << "f1" << endl;
    return true;
}

bool f2() {
    cout << "f2" << endl;
    return true;
}

int main() {
    if (f1() || f2()) {
        cout << "That was for ||" << endl;
    }
    if (f1() | f2()) {
        cout << "That was for |" << endl;
    }
    return 0;
}

It prints:

f1
That was for ||
f1
f2
That was for |

Assuming f2 may have significant side effects (if (okToDestroyWorld && destroyWorld()) ...), the difference can be enormous. It wouldn't surprise a Java programmer (where | and || actually defined for booleans by the language specification), but it is not a usual practice in C++.

I can think of just one reason to use a bitwise operator for booleans: if you need XOR. There is no ^^ operator, so if (a ^ b) is fine as long as both a and b are bool and there are no side effects involved.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73