Search code examples
javaoperator-precedencepost-incrementpre-increment

Java: pre-,postfix operator precedences


I have two similar questions about operator precedences in Java.

First one:

int X = 10;
System.out.println(X++ * ++X * X++); //it prints 1440 

According to Oracle tutorial:
postfix (expr++, expr--) operators have higher precedence than prefix (++expr, --expr)

So, I suppose that evaluation order:

1) first postfix operator: X++ 
   1.a) X++ "replaced" by 10
   1.b) X incremented by one: 10+1=11
   At this step it should look like:  System.out.println(10 * ++X * X++), X = 11;

2) second POSTfix operator: X++ 
   2.a) X++ "replaced" by 11
   2.b) X incremented by one: 11+1=12
   At this step it should look like:  System.out.println(10 * ++X * 11), X = 12;

3) prefix operator: ++X
   3.a) X incremented by one: 12+1=13
   3.b) ++X "replaced" by 13
   At this step it should look like:  System.out.println(10 * 13 * 11), X = 13;

4) evaluating 10*13 = 130, 130*11 = 1430.

But Java seems to ignore PRE/POST ordering and puts them on one level. So the real order:

 X++ -> ++X -> X++ 

what causes the answer to be (10 * 12 * 12) = 1440.

Second one:

Example from this question:

    int a=1, b=2;             
    a = b + a++;

Part of accepted answer: "By the time of assignment, ++ has already incremented the value of a to 2 (because of precedence), so = overwrites that incremented value."

OK, let's look step-by-step:

 1) replacing "b" with 2
 2) replacing "a++" with 1
 3) incrementing "a" by 1 -> at this point a==2
 4) evaluating 2+1 = 3
 5) overwriting incremented value of "a" with 3

Seems everything is fine. But let's make a little change in that code (replace "=" with "+=")

    a += b + a++;

steps 1-4 should be same as above. so, after step 4 we have something like that:

    a += 3;

where a==2

And then I think: OK, a = 2+3, so a should be 5. BUT the answer is only 4

I'm really confused. I already spent couple of hours but still can't understand where I am wrong.

P.S. I know, that I shouldn't use this "style" in real applications. I just want to understand what is wrong in my thoughts.


Solution

  • The confusion stems from the fact that the operands are evaluated from left to right. This is done first, before any attention is paid to operator precedence/order of operations.

    This behavior is specified in JLS 15.7.2. Evaluate Operands before Operation

    So X++ * ++X * X++ is first evaluated as 10 * 12 * 12 which yields, as you saw, 1440.

    To convince yourself of this, consider the following:

    X = 10; System.out.println(X++ * ++X);
    X = 10; System.out.println(++X * X++);
    

    If X++ were done first, then ++X second, then multiplication, both should print the same number.

    But they do not:

    X = 10; System.out.println(X++ * ++X); // 120
    X = 10; System.out.println(++X * X++); // 121
    

    So how does this make sense? Well if we realize that operands are evaluated from left to right, then it makes perfect sense.

    X = 10; System.out.println(X++ * ++X); // 120 (10 * 12)
    X = 10; System.out.println(++X * X++); // 121 (11 * 11)
    

    The first line looks like

    X++       * ++X
    10 (X=11) * (X=12) 12
    10        * 12 = 120
    

    and the second

    ++X       * X++
    (X=11) 11 * 11 (X=12)
    11        * 11 = 121
    

    So why are prefix and postfix increment/decrement operators in the table?

    It is true that increment and decrement must be performed before multiplication. But what that is saying is that:

    Y = A * B++
    
    // Should be interpreted as
    Y = A * (B++)
    
    // and not
    Y = (A * B)++
    

    Just as

    Y = A + B * C
    
    // Should be interpreted as
    Y = A + (B * C)
    
    // and not
    Y = (A + B) * C
    

    It remains that the order of the evaluation of the operands occurs left-to-right.


    If you're still not conviced:

    Consider the following program:

    class Test
    {
        public static int a(){ System.out.println("a"); return 2; }
        public static int b(){ System.out.println("b"); return 3; }
        public static int c(){ System.out.println("c"); return 4; }
    
        public static void main(String[] args)
        {
            System.out.println(a() + b() * c());
            // Lets make it even more explicit
            System.out.println(a() + (b() * c()));
        }
    }
    

    If the arguments were evaluated at the time they were needed, either b or c would come first, the other next, and lastly a. However, the program outputs:

    a
    b
    c
    14
    a
    b
    c
    14
    

    Because, regardless of the order that they're needed and used in the equation, they're still evaluated left to right.

    Helpful reading: