Reason for the existence of non-short-circuit logical operators
Asked Answered
L

6

43

When used with boolean operands, & and | become logical operators per Section 15.22.2 of the JLS. Unlike && and ||, however, these don't short-circuit; they always evaluate both sides. I have a silly question: Why are the less-efficient non-short-circuit logical operators (&, |) still there, when we have the more-efficient short-circuit logical operators (&&, ||)? I mean, what is the real usage of the non-short-circuit logical operators, as opposed to with the short-circuit logical operators? In other words, what is the usage of always evaluating both sides by using the non-short-circuit logical operators?

Lord answered 13/2, 2012 at 17:20 Comment(8)
It's not about efficiency, they have different semanticsCarlson
It's what C does. / Actually in some circumstances they can be faster, because they may reduce branching. There can also be side-effects in evaluating the rhs (evil).Salvia
@TomHawtin-tackline: C doesn't short-circuit & and |. Like Java, it short-circuits && and ||, but not & and |.Laudable
this may be of help: effect-of-a-bitwise-operator-on-a-boolean-in-javaAuspicious
Thanx for the edits T.J. CrowderLord
Short-circuit operators are not always more efficient, see Code efficiencyRegulable
I think you mean what is the use, not what is the usage; I wasn't completely certain, though, so I didn't change it. Please confirm.Winn
Read this codahale.com/a-lesson-in-timing-attacks and this news.ycombinator.com/item?id=761059 thoroughly to understand the difference.Hedwig
L
35

Consider the case where you want any side-effects to always occur, regardless of whether the left-hand expression evaluates true or false. E.g., contrast:

if (foo() & bar()) {
    // Only call this if both operations returned true
}

with

if (foo() && bar()) {
    // Only call this if both operations returned true
}

Let's assume both foo and bar have effects that we want to have happen regardless of whether foo returns true or false. In the first one above, I know that bar will always get called and have its effect. In the latter, of course, bar may or may not get called. If we didn't have the non-short-circuit version, we'd have to use temporary variables:

boolean fooResult = foo();
boolean barResult = bar();
if (fooResult && barResult) {
    // ...
}

You might argue (I probably would) that you should do that anyway, because it's way too easy to misread if (foo() & bar()), but there we go, a pragmatic reason for having non-short-circuit versions.

Laudable answered 13/2, 2012 at 17:21 Comment(5)
In Java & and | does not work with bits if applied to boolean type.Lacefield
Sorry, I would remove downvote if I can. I just tried it because you were wrong, aren't you agree? :>Lacefield
@SuzanCioc: Thanks, I had skimmed right over the word "logical" in the question! Updated the answer and edited the question to help others avoid the same mistake.Laudable
There are, in fact, efficiency reasons for not short-circuiting: branching can be expensive, and if both conditions are cheap, it can actually be faster to use & and |. I wouldn't recommend it unless a) you're really really keen on performance, and b) you can produce benchmark data to support you.Trapeziform
Relying on non-short-circuit logic(especially not everywhere, but in some places) seems like a flaw in code design for me. It makes you keep in mind the fact that all elements of expression will have its effect.Gnni
K
16

There are instances where the components of a boolean expression involve operations that you'd want to have executed in all cases. Consider the following example of checking a password for validity:

while ( !password.isValid() & (attempts++ < MAX_ATTEMPTS) ) {

    // re-prompt

}

If the second condition was not evaluated due to short-circuiting, attempts would never be incremented. Thus greater programmer flexibility is enabled.

Kite answered 13/2, 2012 at 17:43 Comment(1)
But this can be written with && too if change the order :)Lacefield
A
2

My case (C++):

void setFields(Parameters bundle)
{
  if (setIfDifferent(&field1, bundle.value1) | 
      setIfDifferent(&field2, bundle.value2) |
      setIfDifferent(&field3, bundle.value3)) {
    storeState();
  }
}

setIfDifferent() sets the object's field with new value if they differ, in which case it returns true; or it returns false in case the field's and the new value are the same. So, we want to try to set all fields, and if any of them changed, then we want to store new object's state.

Apogee answered 8/12, 2014 at 11:21 Comment(0)
L
1

You can have some side-effects in logical expression, for example you can assign simultaneously with checking. This may work wrongly if only one part evaluated.

Can't remember good example now, but remember that I was in need of "non-short-circuit" operators sometimes.

Hmmm.... Below is WRONG example, which won't work without "non-short-circuit" OR:

if( (object1=getInstance1()).getNumber() == 1 || (object2=getInstance2()).getNumber() == 2 ) {

    // do something which requires bot object1 and object2 assigned

}
Lacefield answered 13/2, 2012 at 17:35 Comment(1)
+1 for pointing out "some side-effects in logical expression".Lord
A
0

In my case, I have two methods that compare two different but related objects (Object2 is an attribute of Object1) to see if there were any changes. An update needs to happen if either are updated, but both need to be evaluated so that the objects will be modified if both have been changed. Therefore, a single pipe "OR" comparison is required.

EX:

if (compare(object1, currentObject1) | comparison(object2, currentObject2)) {
    updateObject1(object1);
}
Armful answered 9/7, 2014 at 14:23 Comment(0)
D
0

Technically, & and | are not logical, they're bitwise operators that become logical operators when associated with booleans.

There are times when you'd want to include assignment expressions inside your logical expressions.

Say:

if(a = (checkForSomeCondition()) | b = checkForAnotherCondition())
{
 //now do something here with a and b that has been cached
}

If I had used ||, I'd not be able to perform the above check and would have had to split the assignments into separate statements. I've never come across scenarios like this during application development, but come across it a few times while writing algorithms.

Of course, you could use unary operators on the logical expressions or pass variables by reference into a predicate, but those seem like less common cases than the above.

Delectable answered 17/12, 2015 at 17:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.