4

According to the Oracle operator precedence specification, an operation such as:

x-- // Returns x, then subtracts 1 from x.

should take precedence over:

--x // Subtracts 1 from x, then returns x.

So, given the small snippet below, why does:

int x = 5;
int y = --x * 5 + x--;
System.out.println(x+" vs "+y);

print 3 vs 24 instead of 3 vs 20 ?

Elaboration

Assuming the given order of operator precedence, one could break down the line #2 of the snippet to the following pseudo-blocks (previously evaluated values put in square brackets):

  1. Evaluate x--

  2. Evaluate --x

  3. Evaluate [--x] * 5

  4. Evaluate [--x * 5] + [x--]

  5. Evaluate y = [--x * 5 + x--]

Which would then resolve as follows:

After 1 returns 5 sets x to 4

After 2 sets x to 3 returns 3

After 3 multiplies 3 by 5 and returns 15

After 4 adds 15 to 5 and returns 20

After 5 sets y to 20

How come the returned value is 24 not 20.

P.S. (You get 24 if you evaluate --x before x-- but it should not be the case due to operator precedence).

Am I blind or just bad at math or what?

Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
Ceiling Gecko
  • 3,104
  • 2
  • 24
  • 33
  • possible duplicate of [Java: Prefix/postfix of increment/decrement operators?](http://stackoverflow.com/questions/5413548/java-prefix-postfix-of-increment-decrement-operators) – Aaron Kurtzhals Aug 27 '15 at 17:04
  • @AaronKurtzhals Not actually a duplicate of that. In this one, OP knows how the operators work isolated; the question is about the order in which they are evaluated when part of the same expression. – Reinstate Monica -- notmaynard Aug 27 '15 at 17:09
  • Its just the order of evaluation and not too much to do with operator precedence per se. – Rahul Tripathi Aug 27 '15 at 17:25

5 Answers5

5

The operands of operators are always evaluated left-to-right, regardless of the order of operations dictated by precedence.

Here is what happens:

  1. Evaluate --x. Sets x to 4, yields 4.
  2. Evaluate (--x) * 5. x is 4 at this point, so this evaluates 4 * 5 and yields 20.
  3. Evaluate x--. Yields 4, sets x to 3.
  4. Evaluate ((--x) * 5) + x--;. Adds 20 to 4 to yield 24.
  5. Print "3 vs 24".

The JLS, Section 15.7, states:

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

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • Did you want to exclude the assignment operator? You broke down every other operation in the example, and even though it's not relevant here, it could be if x was also the target of the assignment. – Rainbolt Aug 27 '15 at 17:08
1

Operator precedence has nothing to do with the order in which the operands are evaluated. Operands are always evaluated from left to right.

Operator precedence determines how expressions are evaluated. For example, the expression 4 * 5 + 6 could mean (4 * 5) + 6 or 4 * (5 + 6). Because * has a higher operator precedence, the correct one is the first.

Both prefix and postfix decrement and increment have a high operator precedence, so --x * 5 means (--x) * 5 and not --(x * 5). Obviously, the latter expression would yield a compiler error because x * 5 is not a variable. This is the reason prefix and postfix decrement and increment have a high operator precedence: it makes no sense to evaluate --x * 5 as --(x * 5).

In your case, therefore, the expression is equivalent to ((--x) * 5) + (x--). This expression can then be evaluated, where the operands are evaluated strictly from left to right.

Hoopje
  • 12,677
  • 8
  • 34
  • 50
  • This answer sort of has the right idea, but you're not exactly explaining the specific case involving pre- and post-increment operators particularly well. Also, `--(x * 5)` is not valid Java code. – Bernhard Barker Aug 27 '15 at 17:33
0

The operands are evaluated from left to right which you think it as right to left. So --x is getting set to 4 then --x*5 which is 4*5 as 20. And then --x*5 + x-- makes it 24. And finally x-- is evaluated as 3.

From the Java Docs:

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

Here is a good detailed article on the Java precedence.

Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
0

So, the Java compiler finds this expression:

int x = 5;
int y = --x * 5 + x--;

and must decide the order of the operations to generate bytecodes. There are four operators in a row: --, *, + and --, and not a single parenthesis, so the good compiler checks its precendence table and puts the parenthesis that you, lazy programmer, omitted:

((--OP1) * OP2) + (OP3--) 

Please note that at this stage operands are opaque entities: it doesn't matter if they are variables (that imply a read from memory), constants, method calls, implicit unboxings or whatever - the parenthesisation process doesn't need to know.

Finally, when emitting bytecodes, the compiler has another rule to obey: operands must be evaluated left-to-right, so that operations that read from and write to memory (variable decrement/increment and method calls for example) have a guaranteed order. So it must be the case that in the sequence of instructions:

OP1
...
OP2
...
OP3

Let's go object oriented with IntegerVariable to see a more intuitive example:

public class IntegerVariable {

    private int val;

    public IntegerVariable(int val) {
        this.val = val;
    }

    public int getAndDecrement() {
        return val--;
    }

    public int decrementAndGet() {
        return --val;
    }

    public int get() {
        return val;
    }

    public static IntegerVariable Int(int val) {
        return new IntegerVariable(val);
    }

    public static void main(String... args) {
        IntegerVariable x = Int(5);
        int y = x.decrementAndGet() * Int(5).get() + x.getAndDecrement();
    }
}

If you set breakpoints at get(), decrementAndGet() and getAndDecrement() you can clearly see the difference between

  • evaluation order, which is made debuggable by using virtual calls. Evaluation order is left-to-right, and you are guaranteed that the operand is fully evaluated before operation takes place
  • operator precedence, which basically just puts the parenthesis that you omitted: y = ((--x) * 5) + (x--)
Raffaele
  • 20,627
  • 6
  • 47
  • 86
  • This might sort of answer the question, but it's far from immediately obvious how or why (you should at least add a bit of an explanation), and "this is one way you could get to the answer" is not the same as "this is how Java gets to the answer (and where it's documented)". – Bernhard Barker Aug 27 '15 at 17:22
-1

x-- happens after instruction execution. so it was like 4*5+4; and after the execution of this statement, x again decremented to 3.

Thats why 3 and 24

Ritesh
  • 1,809
  • 1
  • 14
  • 16
  • I'm not sure if you're misunderstanding what's happening or just not explaining it well, but going by this answer, I'd think `x-- + x--` is `10`, when it's in fact `9` (for `x = 5`). Check the other answers. – Bernhard Barker Aug 27 '15 at 17:18
  • Choice of the words by me was bad – Ritesh Aug 27 '15 at 17:52