AbdelRahmane
AbdelRahmane

Reputation: 309

Confusing example about '&&' and '||' precedence

I was testing the precedence between && and || and I had an example that was confusing. In Java, && has higher operator precedence than the operator ||.

So if we have those 3 expressions:

//expr1  = true , expr2 = false; expr3 = false;

if(expr1 || expr2 && expr3);

It should be evaluated as:

if(expr1 || (expr2 && expr3));

So expr2 && expr3 should be evaluated before expr1. However, this example:

int a1 = 10;
int a2 = 20;
System.out.println(a1 < a2 || ++a1 > a2 && ++a2 < a1);
System.out.println(a1);
System.out.println(a2);

Outputs:

true
10
20

That proves that only a1 < a2 is evaluated. Can you explain why this is the case?

Upvotes: 9

Views: 4038

Answers (5)

kemparaj565
kemparaj565

Reputation: 385

This is because of short-circuiting. Where the first expression is evaluated first and if it's able to derive the result as true then it concludes there & would not even go for rest of the expression.If the first expression results as false then the other condition will be checked and the outcome will be derived.

To know more on operator precedence. Please refer below.

https://introcs.cs.princeton.edu/java/11precedence/

Upvotes: 0

Andrew Li
Andrew Li

Reputation: 57982

The expression is short-circuiting. From the link:

when the first argument of the AND function evaluates to false, the overall value must be false; and when the first argument of the OR function evaluates to true, the overall value must be true.

It sees that the rest of the condition doesn't matter because one of the operands of || is already true (10 < 20). If one of the operands is true, then no matter what the rest of the condition is, it's true.

You may use bitwise & and | to prevent this.


But, the ( expr2 && expr3 ) should be evaluated before expr1, no ?

No. You have to separate concepts of precedence and evaluation order.

  • Precedence: Dictates the parenthesization of an expression, not the order in which an expression is evaluated. For an example:

      true || false && false
    

    Is parenthesized to this because && has higher precedence than ||:

      true || (false && false)
    

    This does not mean that things in parentheses is evaluated first in Java's case. Precedence just clarifies what the operands of an operator are, in this case false and false, where as in this case:

      (true || false) && (false || false)
    

    The operands for && are true and false, not false and false.

  • Evaluation Order: Describes in what order each operand is evaluated and operator is applied and is sometimes language specific. This dictates how an expression is evaluated, unlike precedence.

In this case, your example:

true || false && false

As established earlier, becomes this due to precedence:

true || (false && false)

But Java, unlike C++, JavaScript, or a number of other languages has a strictly left to right evaluation. Per the Java Language Specification:

15.7. Evaluation Order

The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.

15.7.1. Evaluate Left-Hand Operand First

The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.

So, when you have:

true || (false && false)

Java first evaluates the left operand which turns out to be true. Then the whole condition short circuits. The right operand of || in the parentheses is never evaluated at all. The same goes for your other example:

a1 < a2 || (++a1 > a2 && ++a2 < a1)
           ^^^^^^^^^^^^^^^^^^^^^^^^
           Step 0, precedence and parenthesization

a1 < a2 || (++a1 > a2 && ++a2 < a1)
^^^^^^^
Step 1, left operand evaluated, variables resolved to values 10 and 20, condition is true

true || (++a1 > a2 && ++a2 < a1)
^^^^
Step 2, short circuits, left operand is not evaluated

Take another more complex example:

false || false || true && (false || true) || false

Due to precedence, it becomes:

false || false || (true && (false || true)) || false

Then, evaluation begins, left to right:

false || false || (true && (false || true)) || false
^^^^^^^^^^^^^^
Step 1, false || false, does not short circuit, right operand is evaluated, is false

false || (true && (false || true)) || false
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Step 2, false || (true && (false || true)), does not short circuit, right operand is evaluated
Step 2A, (true && (false || true)), does not short circuit, right operand is evaluated
Step 2B, (false || true), does not short circuit, right operand is evaluated, is true
Step 2C, (true && true), does not short circuit, right operand is evaluated, is true
Step 2D, false || true, does not short circuit, right operand is evaluated, is true

true || false
^^^^
Step 3, true || false short circuits, right operand is not evaluated, is true

Thus the whole expression evaluates to true. The whole expression was evaluated left to right the whole way through. Precedence only dictated the operands of an operator via parenthesization, not the evaluation order.

Further reading at Eric Lippert's explanatory article on precedence vs associativity vs evaluation order as mentioned by Daniel Pryden, it clears up a lot of the confusion.

The main takeaway is that precedence does not dictate in what an expression is evaluated. It only dictates how an expression should be parenthesized. Evaluation order, on the other hand, tells us exactly how an expression is evaluated, and in Java's case is always left to right.

Upvotes: 16

scottb
scottb

Reputation: 10084

Precedence:

boolean result = a || b && c

In order to have the correct value according to the rules of precedence, the compiler must logically evaluate this as:

boolean x = b && c
boolean result = a || x

This speaks to your point that b && c must be evaluated before result can be calculated. However, in Boolean algebra, it is possible to write expressions whose results do not depend on all the operands. This fact is exploited to enable a compiler optimization called short circuiting.

Short Circuiting:

For any Boolean expression:

a || b

the result does not depend on b if a evaluates to true. When a is true, it does not matter if b evaluates to true or false. Because of this, the compiler executes:

a || b && c

as if it had been written:

boolean result = a;
if (!a) result = b && c;

Note that executed this way, the rules of precedence are still respected. The expression is not evaluated as (a || b) && c. Since the overall result of the expression does not depend on (b && c) in the case where a is true, b && c is simply never evaluated.

This is a good thing. Short circuiting allows you to write correct programs when the correctness of one operand depends on the correctness of another. For example:

if (myString == null || !myString.isEmpty() && myString != "break") return;

Without short circuit evaluation, the boolean expression could throw a NullPointerException. However, because of short circuit evaluation, this expression, as written, can never throw a NullPointerException.

Short circuiting can also be used as a performance optimization. If evaluating one operand is extremely expensive, evaluating another first can save the execution time needed to evaluate an operand whose value does not influence the final result of the whole expression.

Upvotes: 5

Eran
Eran

Reputation: 394146

Even when you use explicit parentheses as in

if (expr1 || (expr2 && expr3))

expr1 is evaluated first, since the operands of operators are evaluated from left to right. Since || is a short circuited operator, the second operand ((expr2 && expr3) in your case) will only be evaluated if expr1 is false.

When you remove the parentheses, the operator precedence only comes into play if expr1 is false. In that case the && operand will be evaluated before the || operator and its value will be the second operand of the || operator.

Upvotes: 3

SamTebbs33
SamTebbs33

Reputation: 5647

The first line prints true because of short-circuiting the || operator.

a1 < a2 is true and so the rest of the boolean expression doesn't need to be evaluated and true is returned.

expr2 should be evaluated before expr1

is incorrect, as operator precedence affects the structure of the expression, rather then the evaluation order (in most cases). If you were to re-order the expression so that it was (expr2 && expr3) || expr1, then yes, expr2 would be evaluated before expr1

Upvotes: 6

Related Questions