Ceiling Gecko
Ceiling Gecko

Reputation: 3186

Why is prefix evaluated before postfix in an expression containing both?

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?

Upvotes: 4

Views: 647

Answers (5)

Hoopje
Hoopje

Reputation: 12932

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.

Upvotes: 1

Raffaele
Raffaele

Reputation: 20885

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--)

Upvotes: 0

Rahul Tripathi
Rahul Tripathi

Reputation: 172458

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.

Upvotes: 0

rgettman
rgettman

Reputation: 178263

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.

Upvotes: 5

Ritesh
Ritesh

Reputation: 1847

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

Upvotes: -1

Related Questions