Can I use bitwise operators instead of logical ones?

前端 未结 5 1688
青春惊慌失措
青春惊慌失措 2021-01-18 04:52

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

相关标签:
5条回答
  • 2021-01-18 05:06

    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.

    0 讨论(0)
  • 2021-01-18 05:21

    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.

    0 讨论(0)
  • 2021-01-18 05:22

    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.

    0 讨论(0)
  • 2021-01-18 05:25

    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.

    0 讨论(0)
  • 2021-01-18 05:26

    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
    }
    
    0 讨论(0)
提交回复
热议问题