# Order of evaluation with logical operators

nick woodward
Ranch Hand
Posts: 370
11
I've been reading (embarrassingly) my own previous thread on the topic, here, the one i was linked to, with great explanations here, and have read up on associativity to make sure I wasn't missing anything. I clearly am!

Oh, and I've also been messing with Roel's magical program on the topic (which i now understand). But there is one thing I still can't get straight in my head:

line 1:
&& has precedence here, but i'm pretty sure precedence doesn't matter. order of operations does (left to right) in combination with left associativity.
so (a=false) is evaluated, (b=true) is short-circuited, and (c=true) evaluates to true (causing the entire expression to evaluate to true).
effectively (a=false) && (b=true) count as a single operand for || (c=true). or ((a=false) && (b=true)) || (c=true)

line 2:
This is what throws me. It is evaluating (a=false) and then short-circuiting both (b=true) and (c=true). It is effectively (a=false) && ( (b=true) | (c=true) ) But why? I'm really at a loss, which is a bit embarrassing given that I 'claimed' to understand it last time only a few short months ago. It isn't precedence (&& is above || as well as |) and it isn't left associativity (as far as I understand it!)

Again, apologies for asking the same question again, especially after all the effort that went into the last thread, but this has had me going in circles for way too long now!!! I can't see it, even having read the thread(s). I must be missing something. I'm starting to consider 'a brain' may be the answer..!

Regards,

Nick

Winston Gutkowski
Bartender
Posts: 10571
64
nick woodward wrote:line 2:
This is what throws me. It is evaluating (a=false) and then short-circuiting both (b=true) and (c=true)...

What is the precedence of '|'? Is it greater or less than or equal to '&&'?

From your answer you should be able to work out what's happening (and it's not what you describe).

BTW, you also have '=' in these examples, but since they're all inside parentheses they will always be evaluated first (if at all).

Winston

nick woodward
Ranch Hand
Posts: 370
11
Winston Gutkowski wrote:
nick woodward wrote:line 2:
This is what throws me. It is evaluating (a=false) and then short-circuiting both (b=true) and (c=true)...

What is the precedence of '|'? Is it greater or less than or equal to '&&'?

From your answer you should be able to work out what's happening (and it's not what you describe).

BTW, you also have '=' in these examples, but since they're all inside parentheses they will always be evaluated first (if at all).

Winston

It's less than isn't it? I've clearly just got my head in a complete mess again. I've been through all the examples in the previous threads (14 of them written out in front of me) and seem to understand them. I then look at this one and it throws me.

I must be making an assumption or have an underlying misunderstanding of what is going on.

I'll try and write out what is going on again with both examples. hang on!

nick woodward
Ranch Hand
Posts: 370
11
so line 2 isn't short circuiting b and c? I'm fairly sure it does, doesn't it? Roel's program seems to say it does (unless i've misunderstood the output or have ruined it via copy/paste)

Output:

(1) b1:false b2:false b3:false

b1:false b2:false b3:false result: false

this makes me think b | c is not evaluated and is treated as one operand (otherwise surely |(c=true) would be evaluate to true?)

Winston Gutkowski
Bartender
Posts: 10571
64
nick woodward wrote:It's less than isn't it?

Well, here's the Bible. Have a look.

But whatever you do, don't guess.

Winston

Henry Wong
author
Marshal
Posts: 22089
88
nick woodward wrote:so line 2 isn't short circuiting b and c? i'm not sure how else line 1 evaluates to true, and line 2 to false!

The bitwise OR operator has higher precedence than the logical AND operator, so yes, b and c assignments has been short circuited.

Henry

Winston Gutkowski
Bartender
Posts: 10571
64
Henry Wong wrote:The bitwise OR operator has higher precedence than the logical AND operator, so yes, b and c assignments has been short circuited.

Hate to disagree with an esteemed colleague, but explain that statement to me in detail.

Winston

nick woodward
Ranch Hand
Posts: 370
11
don't do that! don't disagree! | having greater precedence than && would help/make sense to me!

having said that, i'm annoyed with the princeton link:

http://introcs.cs.princeton.edu/java/11precedence/
which says | has a lower precedence than &&.

doesn't seem to match the precedence from the bible, which seems to say the opposite!

*edit: i know it should be about evaluation order, not precedence, but if that was the case, why does it appear to treat b | c as one single operand to the &&?

Winston Gutkowski
Bartender
Posts: 10571
64
nick woodward wrote:this makes me think b | c is not evaluated and is treated as one operand (otherwise surely |(c=true) would be evaluate to true?)

Well, my take on it is that it by precedence rules it ought to be evaluated, since | has higher precedence; but it's possible that the compiler is smart enough to work out that since (a=false) is false anything to the right of '&&' is unreachable.

Either way the result is the same, but the way I'd assume it to work if these were variables with unknown values:
1. evaluate b | c = d
2. evaluate a && d
or, if you prefer, evaluation would be equivalent to
(b | c) && a

Winston

Winston Gutkowski
Bartender
Posts: 10571
64
nick woodward wrote:don't do that! don't disagree!

I'm not disagreeing about the result; merely Henry's use of the term "short circuit".

having said that, i'm annoyed with the princeton link:
http://introcs.cs.princeton.edu/java/11precedence/
which says | has a lower precedence than &&.

That's not how I read it. In fact '|' is level 11 and '&&' is level 12.

Winston

nick woodward
Ranch Hand
Posts: 370
11
Winston Gutkowski wrote:
nick woodward wrote:don't do that! don't disagree!

I'm not disagreeing about the result; merely Henry's use of the term "short circuit".

having said that, i'm annoyed with the princeton link:
http://introcs.cs.princeton.edu/java/11precedence/
which says | has a lower precedence than &&.

That's not how I read it. In fact '|' is #11 and '&&' is #12.

Winston

but then assignment is #15. or multiplication 4 vs addition 5? it seems to be at odds with the link you gave. maybe i am reading it wrong....

anyway, as you've pointed out, it seems i do have a massive underlying misconception - i thought | had a lower precedence than &&.

let me have another look with that in mind!

much appreciated, this has been completely messing with me!

Nick

nick woodward
Ranch Hand
Posts: 370
11
yup, that makes complete sense, thanks!

what a massive waste of time this afternoon was!!!

Winston Gutkowski
Bartender
Posts: 10571
64
Winston Gutkowski wrote:Either way the result is the same, but the way I'd assume it to work if these were variables with unknown values:..

Which brings up an interesting point. In your line 1 expression - and assuming that true and false were t and f and set to true and false somewhere else - how many assignments would take place?

I reckon the answer ought to be 3.

Winston

Winston Gutkowski
Bartender
Posts: 10571
64
• 1
nick woodward wrote:yup, that makes complete sense, thanks!

No probs. Fun stuff.

what a massive waste of time this afternoon was!!!

Aww. Don't say that. At least now you know the right order for '|' and '&&'.

Winston

Roel De Nijs
Sheriff
Posts: 10662
144
• 1
nick woodward wrote:line 1:
&& has precedence here, but i'm pretty sure precedence doesn't matter. order of operations does (left to right) in combination with left associativity.
so (a=false) is evaluated, (b=true) is short-circuited, and (c=true) evaluates to true (causing the entire expression to evaluate to true).
effectively (a=false) && (b=true) count as a single operand for || (c=true). or ((a=false) && (b=true)) || (c=true)

You are spot-on! The expression (a=false) && (b=true) || (c=true) is equivalent to ((a=false) && (b=true)) || (c=true), so (b=true) will never be evaluated because the left operand evaluates to false and therefore the short-circuit operator && will not spend time to evaluate the right operand as it will never be true. If you would change the expression like this (a=false) && ((b=true) || (c=true)), only the first operand will be evaluated. All three different scenarios are illustrated in this code snippet (and confirming what we think).Output:
(a) a:false b:false c:false
(c) a:false b:false c:false
a:false b:false c:true res:true

(a) a:false b:false c:false
(c) a:false b:false c:false
a:false b:false c:true res:true

(a) a:false b:false c:false
a:false b:false c:false res:false

So the output clearly illustrates that the first and second expression are evaluated exactly the same!

Now let's replace the short-circuit operator || with the bitwise inclusive OR operator | and see what happensOutput:
(a) a:false b:false c:false
a:false b:false c:false res:false

(a) a:false b:false c:false
(c) a:false b:false c:false
a:false b:false c:true res:true

(a) a:false b:false c:false
a:false b:false c:false res:false

Again this output shows clearly that the first and thrid expression are evaluated exactly the same! Meaning it's evaluated like a=false is the left operand and (b=true) | (c=true) is the right operand. Because the left operand evaluates to false, the short-circuit operator && doesn't waste any CPU cycles to evaluate the right operand.

nick woodward wrote:Again, apologies for asking the same question again, especially after all the effort that went into the last thread, but this has had me going in circles for way too long now!!! I can't see it, even having read the thread(s). I must be missing something. I'm starting to consider 'a brain' may be the answer..!

Honestly, I would not worry about these expressions. I have taken 4 certification exams (3 SCJP & 1 OCAJP) and I have never seen an actual exam question where a short-circuit operator was combined with a bitwise logical operator. In fact, I even think I've never seen an exam question with an expression having more than one short-circuit operator. I'm pretty sure if you know the short-circuit operators, the bitwise logical operators and the difference between them; you'll be in pole position to answer all exam questions correctly. Expect to see something like thisSo if you know what will happen with the above code snippets, you don't have to worry and definitely don't need to request another brain

Hope it helps!
Kind regards,
Roel

nick woodward
Ranch Hand
Posts: 370
11
cheers Roel, will go over those shortly, but they look good to me! i haven't had a silly error like this waste my time in a while! glad to hear the exam isn't quite as technical!

Winston - i meant waste because I have 18 days till my exam! otherwise i wouldn't mind - after all, that's why i couldn't let it go, despite knowing what Roel reiterated in this and the old thread, that it was unlikely to be this indepth.

Not wanting to be distracted too much further..... (i suppose i've got over 2 weeks.....) but i'd disagree with one of your previous posts....

I would say that, in my humble but likely completely incorrect opinion, b|c is not evaluated, because evaluation is left to right regardless of precedence, and the compiler comes across the short-circuit &&. By the same logic I'd say that in line 1 only 2 assignments take place. how would it be 3? but as i said, i would not be surprise if i was wrong!

also - was that Princeton link and table of precedence wrong? still looks it to me

Roel De Nijs
Sheriff
Posts: 10662
144
• 1
nick woodward wrote:I would say that, in my humble but likely completely incorrect opinion, b|c is not evaluated, because evaluation is left to right regardless of precedence, and the compiler comes across the short-circuit &&. By the same logic I'd say that in line 1 only 2 assignments take place. how would it be 3? but as i said, i would not be surprise if i was wrong!

In line1 of the original code snippet you have two assignments: false is assigned to a and true is assigned to c. The assignment of b is not executed, which is proven by the output (otherwise its value had been true as well). You could of course argue that the result of the whole expression is assigned to the boolean parameter of the println method

nick woodward wrote:also - was that Princeton link and table of precedence wrong? still looks it to me

Honestly I don't see any difference between the Princeton table and the Oracle table, except the first one has more operators. Where do you see a difference between both tables? Here's a summary of both tables.
PrincetonOracle
[] . () ++ --expr++ expr--
++ -- + - ! ~++expr --expr +expr -expr ~ !
() new(not mentioned)
* / %* / %
+ - ++ -
<< >> >>><< >> >>>
< <= > >= instanceof< > <= >= instanceof
== !=== !=
&&
^^
||
&&&&
||||
?:? :
= += -= *= /= %= &= ^= |= <<= >>= >>>== += -= *= /= %= &= ^= |= <<= >>= >>>=

Hope it helps!
Kind regards,
Roel

nick woodward
Ranch Hand
Posts: 370
11
Roel De Nijs wrote:
nick woodward wrote:I would say that, in my humble but likely completely incorrect opinion, b|c is not evaluated, because evaluation is left to right regardless of precedence, and the compiler comes across the short-circuit &&. By the same logic I'd say that in line 1 only 2 assignments take place. how would it be 3? but as i said, i would not be surprise if i was wrong!

In line1 of the original code snippet you have two assignments: false is assigned to a and true is assigned to c. The assignment of b is not executed, which is proven by the output (otherwise its value had been true as well). You could of course argue that the result of the whole expression is assigned to the boolean parameter of the println method

nick woodward wrote:also - was that Princeton link and table of precedence wrong? still looks it to me

Honestly I don't see any difference between the Princeton table and the Oracle table, except the first one has more operators. Where do you see a difference between both tables? Here's a summary of both tables.
PrincetonOracle
[] . () ++ --expr++ expr--
++ -- + - ! ~++expr --expr +expr -expr ~ !
() new(not mentioned)
* / %* / %
+ - ++ -
<< >> >>><< >> >>>
< <= > >= instanceof< > <= >= instanceof
== !=== !=
&&
^^
||
&&&&
||||
?:? :
= += -= *= /= %= &= ^= |= <<= >>= >>>== += -= *= /= %= &= ^= |= <<= >>= >>>=

Hope it helps!
Kind regards,
Roel

Re: the assignments - yeah, the first part was in relation to line 2. I think what I wrote agrees with what you were saying. I was trying to say that in line 2 I disagreed with winston that b|c were evaluated and therefore weren't assigned (in that particular part of the code ). I agree that b in line 1 is not evaluated and therefore is not assigned.

*** ah my brain must be fried. on the princeton table I was looking at '+' (concatenation) and wondering why addition came above multiplication. concatenation not appearing on the oracle list is where i think the confusion came in.....

and just to prove my brain is fried: answers to the code snippets (well the 3rd one):

1st go: x = 5, y = 11, z = 8, i = 6.
2nd go: lets not talk about the second go.
3rd go: x = 4, y = 11, z = 8, i = 6.

attempt 3 is my final answer, and i'm fairly confident, but this was actually a topic WAY up on my list of things to do. I'm TERRIBLE at doing these at any sort of speed, mainly because I don't have a good strategy for writing down the cycles and the values - i often lose track (clearly). maybe it's just practice, but I'm fairly sure poor technique has something to do with it too. any suggestions or good thread links? should probably make it a large section in your book Roel. (i'll go search the forum now :P)

thanks again,

Nick

Roel De Nijs
Sheriff
Posts: 10662
144
• 1
nick woodward wrote:*** ah my brain must be fried. on the princeton table I was looking at '+' (concatenation) and wondering why addition came above multiplication. concatenation not appearing on the oracle list is where i think the confusion came in.....

True! The Princeton table makes a distinction between additive operators and the concatenation operator whereas the Oracle table doesn't

nick woodward wrote:attempt 3 is my final answer, and i'm fairly confident, but this was actually a topic WAY up on my list of things to do. I'm TERRIBLE at doing these at any sort of speed, mainly because I don't have a good strategy for writing down the cycles and the values - i often lose track (clearly). maybe it's just practice, but I'm fairly sure poor technique has something to do with it too. any suggestions or good thread links? should probably make it a large section in your book Roel. (i'll go search the forum now :P)

Your 3rd attempt is absolutely spot-on!

I always use some kind of table to keep track of the status of each variable with each operation. For the above (free ) mock question, it would look something like this

Hope it helps!
Kind regards,
Roel

nick woodward
Ranch Hand
Posts: 370
11
yeah, i think that's a good structure to use, mine was a bit more haphazard. stolen!

the thing is, these sorts of questions are generally far more time consuming (for me) than their relative worth, so i'm thinking of skipping them and doing them all at the end. but i guess a massive amount of practice won't hurt

thanks for all the help, and you too winston, much appreciated.

Nick

Winston Gutkowski
Bartender
Posts: 10571
64
nick woodward wrote:I would say that, in my humble but likely completely incorrect opinion, b|c is not evaluated, because evaluation is left to right regardless of precedence, and the compiler comes across the short-circuit &&. By the same logic I'd say that in line 1 only 2 assignments take place. how would it be 3? but as i said, i would not be surprise if i was wrong!

The problem is that you have two factors at work here. Modern compilers are very smart, so it's quite possible that it can look at an expression like
(a=false)
and say "you know what, I KNOW that evaluates to false, so I also know that anything to the right of '&&' is irrelevant, regardless of any stupid precedence rules".

but what if the code was something like this?:Now the compiler has no way of knowing what the value of 'a' actually is, so it would be breaking precedence protocol NOT to evaluate b|c before it evaluates the 'a &&' bit.
Similarly, '()' has higher precedence than any of the other operators in those expressions, so in theory it ought to evaluate them first.

However '&&' is a bit of a special animal, and modern compilers are allowed to do all sorts of things, like reordering instructions, providing the final outcome is logically equivalent; so it's still perfectly possible that after evaluating (a=fals) and then encountering the '&&' it can skip back to the logic at the top.
The only real way to know what it does do is to run javap on it and look at the bytecode it generates.

Good luck with your exam, BTW. Sounds like you'll probably smoke it.

Winston

Roel De Nijs
Sheriff
Posts: 10662
144
Winston Gutkowski wrote:The problem is that you have two factors at work here. Modern compilers are very smart, so it's quite possible that it can look at an expression like
(a=false)
and say "you know what, I KNOW that evaluates to false, so I also know that anything to the right of '&&' is irrelevant, regardless of any stupid precedence rules".

If I understand correctly you are suggesting that the compiler might adjust the compiled code because the first operand always evaluates to false. Honestly I don't see how this can explain how 3 assignments will happen, because if it would happen then subexpressions would be removed from the expression as they will never be evaluated due to the first operand of the short-circuit operator && always evaluates to false

Winston Gutkowski wrote:but what if the code was something like this?:

The compiler is only involved in compiling your code and thus only cares about valid syntax and your code not violating any rules (e.g. casting between incompatible types), so it could maybe make a difference in the byte code. But that's completely irrelevant for the OCAJP (and OCPJP) certification exam(s) and therefore in this forum we don't care about the compiled code. We have already plenty to care about while preparing for the OCAJP exam (sometimes even more than we can chew), so we are very happy we can ignore something
As the compiler is not involved when executing a program, this makes no difference at all while executing. And that's clearly illustrated by this adjusted code snippet using the test method insteadOutput:
(a) a:false b:false c:false
(c) a:false b:false c:false
a:false b:false c:true res:true

(a) a:false b:false c:false
a:false b:false c:false res:false

Hope it helps!
Kind regards,
Roel

PS. I verified if using the compile-time constants in a statement like System.out.println((a = false) && (b = true) | (c = true)); makes any difference in the (de)compiled code, but it clearly doesn't. When the byte code is decompiled, the statement looks exactly the same (System.out.println((a = false) && (b = true) | (c = true));).

Roel De Nijs
Sheriff
Posts: 10662
144
nick woodward wrote:the thing is, these sorts of questions are generally far more time consuming (for me) than their relative worth, so i'm thinking of skipping them and doing them all at the end. but i guess a massive amount of practice won't hurt

True! I follow the same approach... unless I can quickly spot a compiler error You are likely to spot a compiler error disguised inside such complex loop logic (e.g. using a variable outside its scope)

nick woodward
Ranch Hand
Posts: 370
11
but surely if that was the case Winston then the && would have very little benefit in terms of efficiency other than in the case that the assignments to a b and c were explicitly stated or known.

anyway, it sounds like a distinction between 'evaluation' and 'evaluation order'. even if it's not, i'm going to pretend that it is, because otherwise I'll get confused and be making a similar thread again in a weeks time ;)

cheers for the kind words too, lets hope so!

Roel - I've got to massively improve my speed in general so that I get time at the end, but that's the idea/hope!

Regards,

Nick

Roel De Nijs
Sheriff
Posts: 10662
144
• 1
nick woodward wrote:Roel - I've got to massively improve my speed in general so that I get time at the end, but that's the idea/hope!

Regarding time management I have a (valuable) tip. There is no negative scoring on the exam, so you can guess for free. When you encounter a question you want to skip, you have two possibilities: either mark it for review or leave it unanswered. With either of these possibilities the question will be shown at the end of the exam as "unanswered". But if you think you might run out of time, it's probably better to mark the question for review and answer the question by selecting (guessing) the correct answers. If you have time at the end you can still change your answer. If you run out of time, you might have a (slight) chance to have answered the question correctly.

Winston Gutkowski
Bartender
Posts: 10571
64
nick woodward wrote:but surely if that was the case Winston then the && would have very little benefit in terms of efficiency other than in the case that the assignments to a b and c were explicitly stated or known.

Not at all. However, how much benefit it has is likely to have a lot to do with what the compiler actually does - and Roel's probably right here: that's not what the OCJP examiners are really interested in.

However, if you want my full answer it's this:
The compiler has TWO functions:
1. To ensure that your syntax is correct.
2. To generate the bytecode to run your code.
and it's in that last requirement that modern compilers have a lot of latitude.

They are allowed to reorder, normalise, and even (FWIU) omit instructions if they deem them to be redundant or inefficient - and that includes assignments, and in particular expressions that involve literals - which is just one reason why the old adage about "premature optimisation" is probably more important in a language like Java than in one like C.

If you look at it simply as a purist, you have three expressions enclosed in brackets, which has the highest precedence of all the operators in the expression; therefore, those 3 expressions should be evaluated first. Next you have the one sandwiching '|', which has the next highest priority, so it should be evaluated next. And finally you have the '&&'. The '='s are irrelevant because they have already been overridden by the enclosing '()'.
And if you look at it that way, you will get the correct result for the entire expression.

All I've been musing about is whether or not the compiler actually generates bytecode that does it that way, given the huge impact of '&&' in terms of reduction.

Hope I've explained myself a bit better than I obviously have up to now.

Winston

nick woodward
Ranch Hand
Posts: 370
11
interesting stuff!

i do understand what you're saying generally, but think i'm going to not try too hard to get into it, in case I forget the rule that seems to work for me:

that evaluation order != precedence

for the time being i'm just going to assume that the evaluating you're talking about (ie having the variables and input for the expression as a whole) is different to how the expression is actually evaluated at runtime.

i'm already starting to over-think it now......

Winston Gutkowski
Bartender
Posts: 10571
64
nick woodward wrote:in case I forget the rule that seems to work for me:
that evaluation order != precedence

I hope I'm not reading that right, because unless you're told otherwise, evaluation order absolutely == precedence; and "short circuiting" (or ignoring) only takes place at the time that a short-circuiting operation is evaluated.

And I'm pretty sure that the OCJP exam will assume my "purist" interpretation for all expressions, not just your line 2.

for the time being i'm just going to assume that the evaluating you're talking about (ie having the variables and input for the expression as a whole) is different to how the expression is actually evaluated at runtime.

Yep, That's probably the way to go.

i'm already starting to over-think it now......

Welcome to Java.

Winston

Roel De Nijs
Sheriff
Posts: 10662
144
Winston Gutkowski wrote:evaluation order absolutely == precedence; and "short circuiting" (or ignoring) only takes place at the time that a short-circuiting operation is evaluated.

That's complete BS! And is already illustrated many times in this topic. Every code snippet in this topic using the | operator illustrates short-circuiting happens BEFORE precedence. If that would not have been the case, variables b and c would have been true, but both are (clearly) false as these subexpressions are NOT evaluated due to the short-circuit operator && (which has a lower precedence than the | operator).

In Java (sub)expressions are evaluated from left to right (except assignment operators which are evaluated right to left). When 2 operators share an operand the operator with the higher precedence goes first. Little example: 2 + 3 * 4 is treated as 2 + (3 * 4) (as multiplification has higher precedence than addition). But (and I can't stress this enough): evaluation order is different from precedence! So the expression 7 - 4 * 2 is evaluated as follows: * has higher precedence than -, so 7 is evalauted first (left to right rememeber), then 4 * 2 is evaluated and finally 7 - 8 is evaluated.

In this post and this one you'll find excellent explanations about precedence order, associativity, order of evaluation of (sub)expressions, and short circuiting. Please read before you confuse exam aspirants even more.

Roel De Nijs
Sheriff
Posts: 10662
144
Winston Gutkowski wrote:The compiler has TWO functions:
1. To ensure that your syntax is correct.
2. To generate the bytecode to run your code.
and it's in that last requirement that modern compilers have a lot of latitude.

And to provide a very simple and straightforward example of one of the possible compiler optimizations. When you have an expression which adds two compiler-time constants, the compiler will already perform the addition when generating the byte code. So this source codewill result in this byte codeBut once again: for the OCAJP (and OCPJP) certification exams you should not know about these compiler optimizations at all. One of the possible reasons: this behavior could be dependent on different compilers and even on different versions of the same compiler.

Winston Gutkowski wrote:Take your line 2 example:
If you look at it simply as a purist, you have three expressions enclosed in brackets, which has the highest precedence of all the operators in the expression; therefore, those 3 expressions should be evaluated first. Next you have the one sandwiching '|', which has the next highest priority, so it should be evaluated next. And finally you have the '&&'. The '='s are irrelevant because they have already been overridden by the enclosing '()'.
And if you look at it that way, you will get the correct result for the entire expression.

To be honest I don't see any advantage looking at it that way. It only adds to the confusion about this topic. It's completely different from how this expression will actually be evaluated And you might get the correct result for the entire expression (although you still need to know the precedence order), but this result is useless as all your other variables (e.g. b and c) have incorrect values. And these variables could be used in a subsequent expression (resulting in a wrong end result and a wrong answer on the exam).

Winston Gutkowski
Bartender
Posts: 10571
64
Roel De Nijs wrote:That's complete BS! And is already illustrated many times in this topic. Every code snippet in this topic using the | operator illustrates short-circuiting happens BEFORE precedence. If that would not have been the case, variables b and c would have been true, but both are (clearly) false as these subexpressions are NOT evaluated due to the short-circuit operator && (which has a lower precedence than the | operator)

And (if we're using nick's code) the '()' operators as well.

OK, so where in the JLS does it explain exactly WHEN short-circuiting takes place? My assumption is that it would be done WHEN the sub-expression that involves the operator is, but you're saying that's not the case. I say that it doesn't make ANY difference logically, and that what you're showing me is what the generated bytecode actually does.

And this (I say) is the problem. Unless there's a section in the JLS that explains short-circuiting and WHEN it's done, I'm assuming how the expression ought to be interpreted in terms of operator precedence; you're showing me what actually happens when the bytecode generated by the compiler is executed.

So: what do the OCJP examiners want you to know?

Winston

Roel De Nijs
Sheriff
Posts: 10662
144
Winston Gutkowski wrote:And I'm pretty sure that the OCJP exam will assume my "purist" interpretation for all expressions, not just your line 2.

Absolutely not!

For the simple reason your "purist" interpretation is completely wrong. And it's very easy to prove with a very simple application. Execute the following code snippet without program argumentsAccording to your "purist" interpretation, the expression args[0].equals("java") will be evaluated first because it's in between parantheses and therefore has the highest precedence. If that would be the case and the program is executed without program arguments, you expect to get an ArrayIndexOutOfBoundsException at runtime. But the program completes normally and prints "The end!". And that's because expressions in Java are evaluated from left to right, so args.length > 0 is evaluated first and because it evaluates to false, the short-circuit operator && will not execute the right operand (and thus preventing the program from throwing a runtime exception).

Winston Gutkowski
Bartender
Posts: 10571
64
Roel De Nijs wrote:According to your "purist" interpretation, the expression args[0].equals("java") will be evaluated first...

Gotcha.

My apologies nick (and all). I've been completely out to lunch on this.

Winston

Winston Gutkowski
Bartender
Posts: 10571
64
Roel De Nijs wrote:When 2 operators share an operand the operator with the higher precedence goes first. Little example: 2 + 3 * 4 is treated as 2 + (3 * 4) (as multiplification has higher precedence than addition).

I still have a question. Nick's line 2 is essentially:
false && true | true
which would seem to me to be precisely the same as 2 + 3 * 4. except that we're dealing with booleans instead of numbers.

Now you said above that 2 + 3 * 4 is evaluated as 2 + (3 * 4) because the 3 is "shared" and '*' has higher precedence. So surely nick's expression should be dealt with as
false && (true | true)

But it isn't - presumably because in "left-to-right" evaluation, 'false &&' trumps all because of short-circuiting, whereas '2 +' is just "2 plus whatever" (which might already be evaluated).

Winston

(*) Edit: That still sounds wrong, because the first expression can't be evaluated unless the '*' is processed before the '+'. I sort of understand it in terms of a shunting-yard; but it would be nice to find a definitive explanation of what Java means by "left-to-right".

Roel De Nijs
Sheriff
Posts: 10662
144
Winston Gutkowski wrote:So surely nick's expression should be dealt with as
false && (true | true)

But it isn't - presumably because in "left-to-right" evaluation, 'false &&' trumps all because of short-circuiting, whereas '2 +' is just "2 plus whatever" (which might already be evaluated).

You are correct about how the expression should be dealt with. So the expression false && true | true will be dealt as false && (true | true), because as you stated correctly the | operator has a higher precedence than the && operator (and thus it's equivalent with the 2 + 3 * 4 expression).
And the expression is effectively dealt like this! So the left operand of the && operator is false and the right operand of this operator is true | true. Now the left operand evaluates to false and therefore the && operator doesn't bother about evaluating the right operand.

Now let's assume the expression false && true | true would not be dealt like this (as you seem to suggest, but maybe I'm wrong in my understanding), then it would be a different story (with a different outcome). Because the only other possibility to evaluate this expression is (false && true) | true. Which means that in this case the left operand of the && operator is evaluated (false) and the right operand of the | operator will be evaluated as well (true). The right operand of the && operator is not evaluated. The end result of the complete expression false && true | true would be true (which is incorrect).

This is illustrated in this previous post, both for the || operator and the | operator. And the result is different because both operators have a different precedence towards the && operator (the first one lower, the latter one higher).

Hope it helps!
Kind regards,
Roel

Roel De Nijs
Sheriff
Posts: 10662
144
Winston Gutkowski wrote:(*) Edit: That still sounds wrong, because the first expression can't be evaluated unless the '*' is processed before the '+'.

The epression 2 + 3 * 4 will be evaluated as 2 + (3 * 4). And that means first the 2 will be evaluated, then 3 * 4 will be evaluated and finally the sum 2 + 12 will be evaluated. If you would like to see some kind of evaluation flow, it would look like this

And to illustrate that the multiplication is not evaluated first, I have this "nice" example This code snippet prints 51. Because it's evaluated as 6 (++i), 5 (--i) * 9 (--j) and finally 6 + 45. If the multiplication would be evaluated first, the result would be different (as --i would evaluate to 4).

Hope it helps!
Kind regards,
Roel

Henry Wong
author
Marshal
Posts: 22089
88
Roel De Nijs wrote:
Winston Gutkowski wrote:(*) Edit: That still sounds wrong, because the first expression can't be evaluated unless the '*' is processed before the '+'.

The epression 2 + 3 * 4 will be evaluated as 2 + (3 * 4). And that means first the 2 will be evaluated, then 3 * 4 will be evaluated and finally the sum 2 + 12 will be evaluated. If you would like to see some kind of evaluation flow, it would look like this

Another way to look at it is ... Just because the multiply operator is applied first, doesn't mean that it is evaluated first. While evaluation is always left to right, doesn't mean that it must be finished with the previous operation as the evaluation is moving left to right.

The left operand for the addition is parsed (evaluated) first, then the addition operator is parsed, and it is in the middle of evaluating the right operand when it reaches the multiply. The multiply is applied first, because it is needed for the right operand for the addition operation.

Evaluation is always from left to right, but that doesn't mean the parts being evaluated are finished left to right.

Henry

Winston Gutkowski
Bartender
Posts: 10571
64
Roel De Nijs wrote:And to illustrate that the multiplication is not evaluated first, I have this "nice" example ...

Perfect. Thanks a lot. And you too, Henry.

Like I say, I kind of understand it in shunting yard terms, but that wrinkle of "short-circuiting" - which is clearly checked for before any evaluation of rvalue takes place (which does make sense) - is really badly documented in the JLS.

What it says (15.7.2) is that 'The Java programming language guarantees that every operand of an operator (except the conditional operators &&, ||, and ? :) appears to be fully evaluated before any part of the operation itself is performed" (my emphasis). But it doesn't explain the "except" bit, or even link to the referenced operators, and you have to plough all the way down to 15.23 before you get the "full Monty".

It also then says (15.7.3): "The Java programming language respects the order of evaluation indicated explicitly by parentheses and implicitly by operator precedence" with no qualification whatsoever; when it's clearly not the case if short-circuiting is involved.

To me, short-circuiting changes everything, so it should be explained right at the start:
1. Evaluate lvalue.
2. Check if op allows "short-circuiting", and return the result if no more evaluation is necessary.
Otherwise:
3. Evaluate rvalue.
4. Apply op.
and forget all the other guff until you've explained that. I'd also put '? :' in a completely separate category, with back references.

Guess it's lucky I don't write logic that contains side-effects, because I was plainly totally out to lunch on this one.

Winston

Henry Wong
author
Marshal
Posts: 22089
88
Winston Gutkowski wrote:
To me, short-circuiting changes everything, so it should be explained right at the start:
1. Evaluate lvalue.
2. Check if op allows "short-circuiting", and return the result if no more evaluation is necessary.
Otherwise:
3. Evaluate rvalue.
4. Apply op.
and forget all the other guff until you've explained that. I'd also put '? :' in a completely separate category, with back references.

I am not completely sure if I agree with this... I would argue that the compiler still need to "evaluate" the rvalue. Perhaps, it is a very soft "evaluate", where it just determines where/how to skip it... ... which I guess is what the JLS meant by not "fully evaluate". And regardless, that still requires the definition of precedence, in order for the compiler to determine the point to skip to... which I guess is what the JLS meant by "respect precedence".

Henry

Winston Gutkowski
Bartender
Posts: 10571
64
Henry Wong wrote:I am not completely sure if I agree with this... I would argue that the compiler still need to "evaluate" the rvalue.

Not according to the JLS (15.23):
"The conditional-and operator '&&' is like '&', but evaluates its right-hand operand only if the value of its left-hand operand is true". (again, my emphasis)
and there are similar stipulations for '||' and '?:'.

However, I see what you mean. Presumably:
false && [some really complex expression that results in 2 (with or without brackets)]
should throw a compiler error; so some sort of evaluation is needed to make sure that the type of rvalue is correct.

Sheesh, and it all sounds so simple: "Java evaluates left to right".

Except for all those "unless"es....

Winston