How does expression evaluation order differ between C++ and Java? - java

I've had my brain wrinkled from trying to understand the examples on this page:
http://answers.yahoo.com/question/index?qid=20091103170907AAxXYG9
More specifically this code:
int j = 4;
cout << j++ << j << ++j << endl;
gives an output: 566
Now this makes sense to me if the expression is evaluated right to left, however in Java a similar expression:
int j = 4;
System.out.print("" + (j++) + (j) + (++j));
gives an output of: 456
Which is more intuitive because this indicates it's been evaluated left to right. Researching this across various sites, it seems that with C++ the behaviour differs between compilers, but I'm still not convinced I understand. What's the explanation for this difference in evaluation between Java and C++? Thanks SO.

When an operation has side effects, C++ relies on sequence points rule to decide when side effects (such as increments, combined assignments, etc.) have to take effect. Logical and-then/or-else (&& and ||) operators, ternary ? question mark operators, and commas create sequence points; +, -, << and so on do not.
In contrast, Java completes side effects before proceeding with further evaluation.
When you use an expression with side effects multiple times in the absence of sequence points, the resulting behavior is undefined in C++. Any result is possible, including one that does not make logical sense.

Java guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right. The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated. Java also 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. See Java language specification §15.7 for more details.
C++, on the other hand, happily let's you get away with undefined behavior if the expression is ambiguous because language itself doesn't guarantee any order of evaluation of sub-expressions. See Sequence Point for more details.

In C++ the order of evalulations of subexpressions isn't left-to-right nor right-to-left. It is undefined.

Related

Does Java have an operator precedence table? [duplicate]

Misunderstanding Java operator precedence is a source of frequently asked questions and subtle errors. I was intrigued to learn that even the Java Language Specification says, "It is recommended that code not rely crucially on this specification." JLS §15.7 Preferring clear to clever, are there any useful guidelines in this area?
As noted here, this problem should be studied in the context of Evaluation Order, detailed here. Here are a number of resources on the topic:
JLS Operators
JLS Precedence
JLS Evaluation Order
What are the rules for evaluation order in Java?
Java Glossary
Princeton
Oracle Tutorial
Conversions and Promotions
Usenet discussion
Additions or corrections welcome.
As far as the "Real World" is concerned, it's probably fair to say:
enough programmers know that multiplication/division take precedence over addition/subtraction, as is mathematically the convention
hardly any programmers can remember any of the other rules of precedence
So, apart from the specific case of */ vs +-, I'd really just use brackets to explicitly define the precedence intended.
Another related source of bugs is how rounding errors accumulate. Not an operator precedence order issue per se, but a source of surprise when you get a different result after rearranging operands in an arithmetically-equivalent way. Here's a sun.com version of David Goldberg's What Every Computer Scientist Should Know About Floating-Point Arithmetic.
The quote (from the Java Language Specification §15.7) should be read in the context of Evaluation Order. As discussed here, that section concerns evaluation order, which is not related to operator precedence (or associativity).
Precedence and associativity influence the structure of the expression tree (i.e. which operators act on which operands), while "evaluation order" merely influences the order in which the expression tree is traversed when the expression is evaluated. Evaluation order (or "traversal order") does not have any effect unless some sub-expressions have side-effects that affect the result (or side-effects) of other sub-expressions.
For example, if x==1 initially, the expression ++x/++x would evaluate as 2/3 (which evaluates to 0) since Java has left-to-right evaluation order. Had evaluation order in Java been right-to-left, x would have been incremented twice before the numerator is evaluated, and the expression would have evaluated as 3/2 (which evaluates to 1). Had evaluation order been undefined, the expression could have evaluated to either of these results.
The quote in question, together with its context, ...
The Java programming language guarantees that the operands of
operators appear to be evaluated in a specific evaluation order,
namely, from left to right.
It is recommended that code not rely crucially on this specification.
Code is usually clearer when each expression contains at most one side
effect, as its outermost operation
...discourages the reader from depending on the left-to-rightness of Java's evaluation order (as in the example above). It does not encourage unnecessary parentheses.
Edit: Resource: Java operator precedence table that also serves as an index into sections of the JLS containing the syntactic grammar from which each precedence level is inferred.
Also, don't forget the logical && and || are shortcut operators, avoid something like:
sideeffect1() || sideeffect2()
If sideeffect1() is evaluating to true, sideeffect2() isn't going to be executed. The same goes for && and false. This is not entirely related to precendence, but in these extreme cases the assiociativity can also be an important aspect that normally is really irrelevant (at least as far as i am concerned)
The JLS does not give an explicit operator precedence table; it is implied when the JLS describes various operators. For example, the grammar for ShiftExpression is this:
ShiftExpression:
AdditiveExpression
ShiftExpression << AdditiveExpression
ShiftExpression >> AdditiveExpression
ShiftExpression >>> AdditiveExpression
This means that additive operators (+ and -) have a higher precedence than the left-associative shift operators (<<, >> and >>>).
It seems to me that the truth of this is that 'most programmers' think 'most other programmers' don't know or can't remember operator precedence, so they indulge in what is indulgently called 'defensive programming' by 'inserting the missing parentheses', just to 'clarify' that. Whether remembering this third-grade stuff is a real problem is another question. It just as arguable that all this is a complete waste of time and if anything makes things worse. My own view is that redundant syntax is to be avoided wherever possible, and that computer programmers should know the language they are programming in, and also perhaps raise their expectations of their colleagues.

Why operator precedence being ignored here? [duplicate]

I am reading some Java text and got the following code:
int[] a = {4,4};
int b = 1;
a[b] = b = 0;
In the text, the author did not give a clear explanation and the effect of the last line is: a[1] = 0;
I am not so sure that I understand: how did the evaluation happen?
Let me say this very clearly, because people misunderstand this all the time:
Order of evaluation of subexpressions is independent of both associativity and precedence. Associativity and precedence determine in what order the operators are executed but do not determine in what order the subexpressions are evaluated. Your question is about the order in which subexpressions are evaluated.
Consider A() + B() + C() * D(). Multiplication is higher precedence than addition, and addition is left-associative, so this is equivalent to (A() + B()) + (C() * D()) But knowing that only tells you that the first addition will happen before the second addition, and that the multiplication will happen before the second addition. It does not tell you in what order A(), B(), C() and D() will be called! (It also does not tell you whether the multiplication happens before or after the first addition.) It would be perfectly possible to obey the rules of precedence and associativity by compiling this as:
d = D() // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last
All the rules of precedence and associativity are followed there -- the first addition happens before the second addition, and the multiplication happens before the second addition. Clearly we can do the calls to A(), B(), C() and D() in any order and still obey the rules of precedence and associativity!
We need a rule unrelated to the rules of precedence and associativity to explain the order in which the subexpressions are evaluated. The relevant rule in Java (and C#) is "subexpressions are evaluated left to right". Since A() appears to the left of C(), A() is evaluated first, regardless of the fact that C() is involved in a multiplication and A() is involved only in an addition.
So now you have enough information to answer your question. In a[b] = b = 0 the rules of associativity say that this is a[b] = (b = 0); but that does not mean that the b=0 runs first! The rules of precedence say that indexing is higher precedence than assignment, but that does not mean that the indexer runs before the rightmost assignment.
(UPDATE: An earlier version of this answer had some small and practically unimportant omissions in the section which follows which I have corrected. I've also written a blog article describing why these rules are sensible in Java and C# here: https://ericlippert.com/2019/01/18/indexer-error-cases/)
Precedence and associativity only tell us that the assignment of zero to b must happen before the assignment to a[b], because the assignment of zero computes the value that is assigned in the indexing operation. Precedence and associativity alone say nothing about whether the a[b] is evaluated before or after the b=0.
Again, this is just the same as: A()[B()] = C() -- All we know is that the indexing has to happen before the assignment. We don't know whether A(), B(), or C() runs first based on precedence and associativity. We need another rule to tell us that.
The rule is, again, "when you have a choice about what to do first, always go left to right". However, there is an interesting wrinkle in this specific scenario. Is the side effect of a thrown exception caused by a null collection or out-of-range index considered part of the computation of the left side of the assignment, or part of the computation of the assignment itself? Java chooses the latter. (Of course, this is a distinction that only matters if the code is already wrong, because correct code does not dereference null or pass a bad index in the first place.)
So what happens?
The a[b] is to the left of the b=0, so the a[b] runs first, resulting in a[1]. However, checking the validity of this indexing operation is delayed.
Then the b=0 happens.
Then the verification that a is valid and a[1] is in range happens
The assignment of the value to a[1] happens last.
So, though in this specific case there are some subtleties to consider for those rare error cases that should not be occurring in correct code in the first place, in general you can reason: things to the left happen before things to the right. That's the rule you're looking for. Talk of precedence and associativity is both confusing and irrelevant.
People get this stuff wrong all the time, even people who should know better. I have edited far too many programming books that stated the rules incorrectly, so it is no surprise that lots of people have completely incorrect beliefs about the relationship between precedence/associativity, and evaluation order -- namely, that in reality there is no such relationship; they are independent.
If this topic interests you, see my articles on the subject for further reading:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
They are about C#, but most of this stuff applies equally well to Java.
Eric Lippert's masterful answer is nonetheless not properly helpful because it is talking about a different language. This is Java, where the Java Language Specification is the definitive description of the semantics. In particular, §15.26.1 is relevant because that describes the evaluation order for the = operator (we all know that it is right-associative, yes?). Cutting it down a little to the bits that we care about in this question:
If the left-hand operand expression is an array access expression (§15.13), then many steps are required:
First, the array reference subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the index subexpression (of the left-hand operand array access expression) and the right-hand operand are not evaluated and no assignment occurs.
Otherwise, the index subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and the right-hand operand is not evaluated and no assignment occurs.
Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
[… it then goes on to describe the actual meaning of the assignment itself, which we can ignore here for brevity …]
In short, Java has a very closely defined evaluation order that is pretty much exactly left-to-right within the arguments to any operator or method call. Array assignments are one of the more complex cases, but even there it's still L2R. (The JLS does recommend that you don't write code that needs these sorts of complex semantic constraints, and so do I: you can get into more than enough trouble with just one assignment per statement!)
C and C++ are definitely different to Java in this area: their language definitions leave evaluation order undefined deliberately to enable more optimizations. C# is like Java apparently, but I don't know its literature well enough to be able to point to the formal definition. (This really varies by language though, Ruby is strictly L2R, as is Tcl — though that lacks an assignment operator per se for reasons not relevant here — and Python is L2R but R2L in respect of assignment, which I find odd but there you go.)
a[b] = b = 0;
1) array indexing operator has higher precedence then assignment operator (see this answer):
(a[b]) = b = 0;
2) According to 15.26. Assignment Operators of JLS
There are 12 assignment operators; all are syntactically right-associative (they group right-to-left). Thus, a=b=c means a=(b=c), which assigns the value of c to b and then assigns the value of b to a.
(a[b]) = (b=0);
3) According to 15.7. Evaluation Order of JLS
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
and
The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.
So:
a) (a[b]) evaluated first to a[1]
b) then (b=0) evaluated to 0
c) (a[1] = 0) evaluated last
Your code is equivalent to:
int[] a = {4,4};
int b = 1;
c = b;
b = 0;
a[c] = b;
which explains the result.
Consider another more in-depth example below.
As a General Rule of Thumb:
It's best to have a table of the Order of Precedence Rules and Associativity available to read when solving these questions e.g. http://introcs.cs.princeton.edu/java/11precedence/
Here is a good example:
System.out.println(3+100/10*2-13);
Question: What's the Output of the above Line?
Answer: Apply the Rules of Precedence and Associativity
Step 1: According to rules of precedence: / and * operators take priority over + - operators. Therefore the starting point to execute this equation will the narrowed to:
100/10*2
Step 2: According to the rules and precedence: / and * are equal in precedence.
As / and * operators are equal in precedence, we need to look at the associativity between those operators.
According to the ASSOCIATIVITY RULES of these two particular operators,
we start executing the equation from the LEFT TO RIGHT i.e. 100/10 gets executed first:
100/10*2
=100/10
=10*2
=20
Step 3: The equation is now in the following state of execution:
=3+20-13
According to the rules and precedence: + and - are equal in precedence.
We now need to look at the associativity between the operators + and - operators. According to the associativity of these two particular operators,
we start executing the equation from the LEFT to RIGHT i.e. 3+20 gets executed first:
=3+20
=23
=23-13
=10
10 is the correct output when compiled
Again, it is important to have a table of the Order of Precedence Rules and Associativity with you when solving these questions e.g. http://introcs.cs.princeton.edu/java/11precedence/

Arrow (->) operator precedence/priority is lowest, or priority of assignment/combined assignment is lowest?

JLS:
The lowest precedence operator is the arrow of a lambda expression
(->), followed by the assignment operators.
Followed in which direction (increasing priority, decreasing priority)? - "followed" means assignment has higher priority or lower priority (with respect to arrow operator)? I guess, in increasing, because "lowest" (for arrow) means absolutely lowest.
As I understand, arrow (->) should be at the very bottom of this Princeton operator precedence table (that is below all assignment operators), thus arrow (->) having 0 (zero) priority Level (as per that table).
Am I correct in my understanding?
ExamTray seems to say that arrow priority is at least same as assignment... Plus clarified that arrow associativity is Left->To->Right (unlike assignment). I didn't find any JLS quote for arrow associativity.
I always used to think that assignment priority is principally lowest for a reason.
Note the sentence preceding the quoted JLS text:
Precedence among operators is managed by a hierarchy of grammar productions.
The grammar of the Java language determines which constructs are possible and implicitly, the operator precedence.
Even the princeton table you’ve linked states:
There is no explicit operator precedence table in the Java Language Specification. Different tables on the web and in textbooks disagree in some minor ways.
So, the grammar of the Java language doesn’t allow lambda expressions to the left of an assignment operator and likewise, doesn’t allow assignments to the left of the ->. So there’s no ambiguity between these operators possible and the precedence rule, though explicitly stated in the JLS, becomes meaningless.
This allows to compile, e.g. such a gem, without ambiguity:
static Consumer<String> C;
static String S;
public static void main(String[] args)
{
Runnable r;
r = () -> C = s -> S = s;
}
First, let's explain the practical issue here.
Assuming you have a definition like
IntUnaryOperator op;
The following is syntactically accepted, and works as expected:
op = x -> x;
That is, we have an identity function on int assigned to the op variable. But if = had a higher priority, we'd expect Java to interpret this as
(op = x) -> x;
Which is not syntactically valid, thus should be a compile error. Hence, assignment does not, in practice, have higher precedence than the arrow.
But the following is also OK (assume t is a class/instance variable of type int):
op = x -> t = x;
This compiles, and the function, if applied, assigns the value of the operand to t and also returns it.
This means that the arrow doesn't have higher precedence than the assignment t = x. Otherwise it would have been interpreted as
op = ( x -> t ) = x
and clearly, this is not what happens.
So it seems that the operations have equal precedence. What's more, that they are right-associative. This is implied from the grammar at JLS chapter 19:
Expression:
LambdaExpression
AssignmentExpression
LambdaExpression:
LambdaParameters -> LambdaBody
...
LambdaBody:
Expression
Block
So the right side of the lambda body gets us back to Expression, which means we can either have a (higher priority) lambda inside it, or a (higher priority) assignment in it. What I mean by "higher priority" is that the deeper you go through the production rules, the earlier the expression will be evaluated.
The same is true for the assignment operator:
AssignmentExpression:
ConditionalExpression
Assignment
Assignment:
LeftHandSide AssignmentOperator Expression
Once again, the right side of the assignment throws us back to Expression, so we can have a lambda expression or an assignment there.
So rather than relying on the JLS text, the grammar gives us a well defined description of the situation.

Execution order of f1() + f2()*f3() expression and operator precedence in JLS

Given an expression f1() + f2()*f3() with 3 method calls, java evaluates (operands of) addition operation first:
int result = f1() + f2()*f3();
f1 working
f2 working
f3 working
I (wrongly) expected f2() to be called first, then f3(), and finally f1(). Because multiplication shall be evaluated before addition.
So, I don't understand JLS here - what am I missing?
15.7.3. Evaluation Respects Parentheses and Precedence
The Java programming language respects the order of evaluation
indicated explicitly by parentheses and implicitly by operator
precedence.
How exactly operator precedence is respected in this example?
JLS mentions some exceptions in 15.7.5. Evaluation Order for Other Expressions (method invocation expressions (§15.12.4),method reference expressions (§15.13.3)), but I cannot apply any of those exceptions to my example.
I understand that JLS guarantees that operands of a binary operation are evaluated left-to-right. But in my example in order to understand method invocation sequence it is necessary to understand which operation (and both its operands respectively!) is considered first. Am I wrong here - why?
More examples:
int result = f1() + f2()*f3() + f4() + f5()*f6() + (f7()+f8());
proudly produces:
f1 working
f2 working
f3 working
f4 working
f5 working
f6 working
f7 working
f8 working
Which is 100% left-to-right, totally regardless of operator precedence.
int result = f1() + f2() + f3()*f4();
produces:
f1 working
f2 working
f3 working
f4 working
Here the compiler had two options:
f1()+f2() - addition first
f3()*f4() - multiplication first
But the "evaluate-left-to-right rule" works here as a charm!
This link implies that in such examples method calls are always evaluated left-to-right regardless of any parenthesis and associativity rules.
You're missing the text immediately under 15.7:
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
The values of the operands of an expression are evaluated before the expression itself. In your specific case, The outermost expression is f1() + [f2() * f3()]. We must first evaluate f1(), then we must evaluate f2() * f3(). Within that, f2() is evaluated first, then f3(), then the multiplication (which returns the value for the operand of the +).
JLS §15.17.2. Evaluate Operands before Operation:
The Java programming language guarantees that every operand of an
operator (except the conditional operators &&, ||, and ? :) appears to
be fully evaluated before any part ofthe operation itself is
performed.
Thus, f1(), f2() and f3() are evaluated first (left-to-right). Then, operator precedence is applied.
After all, you could observe the execution order in the bytecode, which in my case is:
[...]
INVOKESTATIC Main.f1 ()I
INVOKESTATIC Main.f2 ()I
INVOKESTATIC Main.f3 ()I
IMUL
IADD
Here is how the Expression Tree would look like:
+
/ \
/ \
f1 *
/ \
/ \
f2 f3
Leaves are evaluated first and the operator precedence is encoded in the tree itself.
Because multiplication shall be evaluated before addition.
As you seem to mean it, that's not a reasonable description of the precedence of multiplication over addition, nor of the effect of section 15.7.3. Precedence says that your expression
f1() + f2()*f3()
is evaluated the same as
f1() + (f2() * f3())
, as opposed to the same as
(f1() + f2()) * f3()
Precedence does not speak to which operand of the + operator is evaluated first, regardless of the nature of the operands. It speaks instead to what each operand is. This does impact order of evaluation, in that it follows from precedence rules that the result of the multiplication in your original expression must be computed before the result of the addition, but that does not speak directly to the question of when the values of two factors are evaluated relative to each other or to other sub-expressions.
Different rules than the one you quoted are responsible for the order in which the two operands of a + or * operator are evaluated -- always the left operand first and the right operand second.
How exactly operator precedence is respected in this example?
The example's output does not convey information about whether precedence -- understood correctly -- is respected. The same output would be observed for both of the two parenthesized alternatives above, and precedence is about which one of those two is equivalent to the original expression.
In an order-of-operations sense, precedence says (only) that the right-hand operand of the addition is f2()*f3(), which means that product must be computed before the overall sum can be. JLS 15.7.3 isn't saying anything more than or different from that. That that sub-expression is a multiplication has no bearing on its order of evaluation relative to the other operand of the addition operation.
I understand that JLS guarantees that operands of a binary operation are evaluated left-to-right. But in my example in order to
understand method invocation sequence it is necessary to understand
which operation (and both its operands respectively!) is considered
first. Am I wrong here - why?
Yes, you are wrong, and JLS notwithstanding, your examples provide very good evidence for that.
The problem seems to be the "and both its operands respectively" bit. Your idea seems to be roughly that Java should look through the expression, find all the multiplication operations and fully evaluate them, including their operands, and only afterward go back and do the same for the addition operations. But that is not required to observe precedence, and it is not consistent with the left-to-right evaluation order of addition operations, and it would make Java code much harder for humans to write or read.
Let's consider your longer example:
int result = f1() + f2()*f3() + f4() + f5()*f6() + (f7()+f8());
Evaluation proceeds as follows:
f1() is evaluated first, because it is the left operand of the first operation, and the the operation's operands must be evaluated left-to-right
f2() is evaluated next, because it is part of the right operand to the addition (whose turn it is to be evaluated), and specifically the left operand of the multiplication
f3() is evaluated next, because the multiplication sub-expression to which it belongs is being evaluated, and it is the right operand of that multiplication. Precedence affects the order of operations here by establishing that in fact it is the product that is being evaluated. If that were not so, then instead, the result of f2() would be added to the result of f1() at this point, before f3() was computed.
the product of f2() and f3() is computed, in accordance with the precedence of multiplication over addition.
The sum of (previously evaluated) f1() and f2()*f3() is computed. This arises from a combination of precedence of * over + already discussed, and the left-to-right associativity of +, which establishes that the sum is the left operand of the next addition operation, and therefore must be evaluated before that operation's right operand.
f4() is evaluated, because it is the right operand of the addition operation being evaluated at that point.
The second sum is computed. This is again because of the left-to-right associativity of +, and the left-to-right evaluation order of the next addition operation.
f5() then f6() then their product is evaluated. This is completely analogous to the case of f2()*f3(), and thus is another exercise of operator precedence.
the result of f5()*f6(), as the right-hand operand of an addition operation, is added to the left-hand operand. This is again analogous to previous steps.
f7() is evaluated.
Because of the parentheses, f8() is evaluated next, and then its sum with the previously-determined result of evaluating f7(), and then that result is added to the preceding result to complete the computation. Without the parentheses, the order would be different: the result of f7() would be added to the preceding result, then f8() computed, then that result added.
The most useful tip in my opinion was found in this discussion
JLS is not so clear on that - section 15.7 about evaluation speaks only about operands of a single operation, leaving unclear real-life complex cases with many operations combined.

Are the &, |, ^ bitwise operators or logical operators?

Firstly I learnt that &, |, ^ are the bitwise operators, and now somebody mentioned them as logical operators with &&, ||, I am completely confused - the same operator has two names? There are already logical operators &&, ||, then why use &, |, ^?
The Java operators &, | and ^ are EITHER bitwise operators OR logical operators ... depending on the types of the operands. If the operands are integers, the operators are bitwise. If they are booleans, then the operators are logical.
And this is not just me saying this. The JLS describes these operators this way too; see JLS 15.22.
(This is just like + meaning EITHER addition OR string concatenation ... depending on the types of the operands. Or just like a "rose" meaning either a flower or a shower attachment. Or "cat" meaning either a furry animal or a UNIX command. Words mean different things in different contexts. And this is true for the symbols used in programming languages too.)
There are already logical operators &&, ||, why use &, |, ^?
In the case of the first two, it is because the operators have different semantics with regards to when / whether the operands get evaluated. The two different semantics are needed in different situations; e.g.
boolean res = str != null && str.isEmpty();
versus
boolean res = foo() & bar(); // ... if I >>need<< to call both methods.
The ^ operator has no short-circuit equivalent because it simply doesn't make sense to have one.
Having a language reference is one thing, interpreting it correctly is another.
We need to interpret things correctly.
Even if Java documented that & is both bitwise and logical, we could make an argument that & really didn't lost its logical-operator-ness mojo since time immemorial, since C. That is, & is first and foremost, an inherently logical operator(albeit a non-short-circuited one at that)
& parses lexically+logically as logical operation.
To prove the point, both of these lines behaves the same, eversince C and upto now(Java, C#, PHP, etc)
if (a == 1 && b)
if (a == 1 & b)
That is, the compiler will interpret those as these:
if ( (a == 1) && (b) )
if ( (a == 1) & (b) )
And even if both variables a and b are both integers. This...
if (a == 1 & b)
... will still be interpereted as:
if ( (a == 1) & (b) )
Hence, this will yield a compilation error on languages which doesn't facilitate integer/boolean duality, e.g. Java and C#:
if (a == 1 & b)
In fact, on the compilation error above, we could even make an argument that & didn't lost its logical(non-short-circuit) operation mojo, and we can conclude that Java continues the tradition of C making the & still a logical operation. Consequently, we could say it's the other way around, i.e. the & can be repurposed as bitwise operation (by applying parenthesis):
if ( a == (1 & b) )
So there we are, in another parallel universe, someone could ask, how to make the & expression become a bitmask operation.
How to make the following compile, I read in JLS that & is a bitwise
operation. Both a and b are integers, but it eludes me why the
following bitwise operation is a compilation error in Java:
if (a == 1 & b)
Or this kind of question:
Why the following didn't compile, I read in JLS that & is a bitwise
operation when both its operands are integers. Both a and b are
integers, but it eludes me why the following bitwise operation is a
compilation error in Java:
if (a == 1 & b)
In fact, I would not be surprised if there's already an existing stackoverflow questions similar to above questions that asked how to do that masking idiom in Java.
To make that logical operation interpretation by the language become bitwise, we have to do this (on all languages, C, Java, C#, PHP, etc):
if ( a == (1 & b) )
So to answer the question, it's not because JLS defined things such way, it's because Java(and other languages inspired by C)'s & operator is for all intents and purposes is still a logical operator, it retained C's syntax and semantics. It's the way it is since C, since time immemorial, since before I was even born.
Things just don't happen by chance, JLS 15.22 didn't happen by chance, there's a deep history around it.
In another parallel universe, where && was not introduced to the language, we will still be using & for logical operations, one might even ask a question today:
Is it true, we can use the logical operator & for bitwise operation?
& doesn't care if its operands are integers or not, booleans or not. It's still a logical operator, a non-short-circuited one. And in fact, the only way to force it to become a bitwise operator in Java(and even in C) is to put parenthesis around it. i.e.
if ( a == (1 & b) )
Think about it, if && was not introduced to C language(and any language who copied its syntax and semantics), anyone could be asking now:
how to use & for bitwise operations?
To sum it up, first and foremost Java & is inherently a logical operator(a non-short-circuited one), it doesn't care about its operands, it will do its business as usual(applying logical operation) even if both operands are integers(e.g. masking idiom). You can only force it to become bitwise operation by applying parenthesis. Java continues the C tradition
If Java's & really is a bitwise operation if its operands(integer 1 and integer variable b on example code below) are both integers, this should compile:
int b = 7;
int a = 1;
if (a == 1 & b) ...
They(& and |) were used for two purposes long time ago, logical operator and bitwise operator. If you'll check out the neonatal C (the language Java was patterned after), & and | were used as logical operator.
But since disambiguating the bitwise operations from logical operations in the same statement is very confusing, it prompted Dennis Ritchie to create a separate operator(&& and ||) for logical operator.
Check the Neonatal C section here: http://cm.bell-labs.com/who/dmr/chist.html
You can still use the bitwise operators as logical operators, its retained operator precedence is the evidence of that. Read out the history of bitwise operator's past life as logical operator on Neonatal C
Regarding the evidence, I made a blog post on comparing the logical operator and bitwise operator. It will be self-evident that the so called bitwise operators are still logical operators if you try contrasting them in an actual program: http://www.anicehumble.com/2012/05/operator-precedence-101.html
I also answered a question related to your question on What is the point of the logical operators in C?
So it's true, bitwise operators are logical operators too, albeit non-short-circuited version of short-circuited logical operators.
Regarding
There are already logical operators &&, ||, then why use &, |, ^?
The XOR can be easily answered, it's like a radio button, only one is allowed, code below returns false. Apology for the contrived code example below, the belief that drinking both beer and milk at the same time is bad was debunked already ;-)
String areYouDiabetic = "Yes";
String areYouEatingCarbohydrate = "Yes";
boolean isAllowed = areYouDiabetic == "Yes" ^ areYouEatingCarbohydrate == "Yes";
System.out.println("Allowed: " + isAllowed);
There's no short-circuit equivalent to XOR bitwise operator, as both sides of the expression are needed be evaluated.
Regarding why the need to use & and | bitwise operators as logical operators, frankly you'll be hard-pressed to find a need to use bitwise operators(a.k.a. non-short-circuit logical operators) as logical operators. A logical operation can be non-short-circuited (by using the bitwise operator, aka non-short-circuited logical operator) if you want to achieve some side effect and make your code compact(subjective), case in point:
while ( !password.isValid() & (attempts++ < MAX_ATTEMPTS) ) {
// re-prompt
}
The above can re-written as the following(removing the parenthesis), and still has exactly the same interpretation as the preceding code.
while ( !password.isValid() & attempts++ < MAX_ATTEMPTS ) {
// re-prompt
}
Removing the parenthesis and yet it still yields the same interpretation as the parenthesized one, can make the logical operator vestige of & more apparent. To run the risk of sounding superfluous, but I have to emphasize that the unparenthesized expression is not interpreted as this:
while ( ( !password.isValid() & attempts++ ) < MAX_ATTEMPTS ) {
// re-prompt
}
To sum it up, using & operator (more popularly known as bitwise operator only, but is actually both bitwise and logical(non-short-circuited)) for non-short-circuit logical operation to achieve side effect is clever(subjective), but is not encouraged, it's just one line of savings effect in exchange for readability.
Example sourced here: Reason for the exsistance of non-short-circuit logical operators
The Java type byte is signed which might be a problem for the bitwise operators. When negative bytes are extended to int or long, the sign bit is copied to all higher bits to keep the interpreted value. For example:
byte b1=(byte)0xFB; // that is -5
byte b2=2;
int i = b1 | b2<<8;
System.out.println((int)b1); // This prints -5
System.out.println(i); // This prints -5
Reason: (int)b1 is internally 0xFFFB and b2<<8 is 0x0200 so i will be 0xFFFB
Solution:
int i = (b1 & 0xFF) | (b2<<8 & 0xFF00);
System.out.println(i); // This prints 763 which is 0x2FB

Categories

Resources