17

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.

Community
  • 1
  • 1
D.R.
  • 175
  • 2
  • 9
  • 1
    No, expressions are evaluated left to right and * has higher precedence. So you need to swap 2 and 3 in your first analysis. And a+= is replaced with a=a+. – assylias Sep 24 '13 at 20:22
  • @assylias: `*` has higher precedence than what? I think that part is misleading. But it is true that expressions are *evaluated* left to right and that that explains everything (see my answer). – jedwards Sep 24 '13 at 21:43
  • Sorry lower, than pre/post increment - meaning that the operands of the multiplication are evaluated first, from left to right. – assylias Sep 24 '13 at 21:54

5 Answers5

17

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:

Community
  • 1
  • 1
jedwards
  • 29,432
  • 3
  • 65
  • 92
  • oh, I found document from Princeton and coded almost the same code. So, we always move from left to right. And **ONLY** if we have two operators share same operand - we look at the Precedence Order Table. – D.R. Sep 24 '13 at 22:06
  • 2
    Not really. Think of it as a two step process. First we *evaluate* the operands, from left to right. Then we use the operators, as determined by the table, to compute the result. – jedwards Sep 24 '13 at 22:09
  • This may help: [JLS 15.7.2. Evaluate Operands before Operation](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html) – jedwards Sep 24 '13 at 22:12
  • nope. "The Java programming language guarantees that **every operand of an operator** appears to be fully evaluated before any part of the operation itself is performed." `int arr[] = null;int tmp = 5 * 3 / 0 / arr[0];` - I have `/ by zero` exception, not `NullPointerException`. That is, we have operator `*` - we know both operands - we evaluate it, now we have `15 / 0 / arr[0]`. Next operator `/` - we know both operands, evaluate them and boom - `/ by zero` exception. – D.R. Sep 24 '13 at 22:33
  • 1
    Nope what? And why are you quoting from the link, I read it, *I pasted it*. – jedwards Sep 24 '13 at 22:37
  • I understood your comment as "first we evaluate ALL operands, then we use the operators". If you didn't mean that - my "nope" is wrong, I'm sorry. – D.R. Sep 24 '13 at 22:44
  • One thing I still have trouble understanding is that if operands are evaluated from left to right, "does it matter" that `x++` has a higher precedence than `++x`. In other words, can I concoct an expression where the result would be different if the precedences were reversed? – DavidS Sep 21 '15 at 23:52
  • @jedwards - This is the best and amazing explanation I have ever read. Cleared the confusion. Thanks a lot! – user3055964 Mar 25 '21 at 05:25
2

In short,

Precedence is like preparing the expression to be calculated by putting parentheses. Evaluation comes next from left to right considering each pair of parentheses as a separate operation.

For example,if i=2 then i+i++ becomes i+(i++) after precedence and evaluates to 2+2 = 4. However, i+++i becomes (i++)+i and evaluates to 2+3 = 5.

Same to i+(i=5) which evaluates to 2+5 = 7.

In fact the postfix operators do have higher precedence than prefix operators. For example, i+++++i becomes ((i++)++)+i after precedence which gives a compile error (the second postfix operator needs a variable to operate on but a value is found instead!). If both postfix and prefix operators had had equal precedence then the expression would have become (i++)+(++i) and evaluates to 2+4 = 6.

If you need more explanation you can compile and run the following code and inspect the examples printed at the output.

public class TestPrecedence {
    public static void main(String[] str) {
        int i = 0;
        System.out.println("\n");
        i = 2; System.out.println("i = " + i + "\n");
        i = 2; System.out.println("i++ = " + i++ + "\n");
        i = 2; System.out.println("++i = " + ++i + "\n");
        i = 2; System.out.println("i++i = (i++)i TestPrecedence.java:8: error: ')' expected\n"+
                                  "              i++i\n"+
                                  "                 ^\n");
        i = 2; System.out.println("i+-i = i+(-i) = " + (i+-i) + "\n");
        i = 2; System.out.println("++i++ = ++(i++) TestPrecedence.java:12: error: unexpected type\n"+
                                  "                ++i++ \n"+
                                  "                   ^\n"+
                                  "                required: variable\n"+
                                  "                found:    value\n");
        i = 2; System.out.println("i+++++i = ((i++)++)+i TestPrecedence.java:17: error: unexpected type\n"+
                                  "                      i+++++i\n"+
                                  "                       ^\n"+
                                  "                      required: variable\n"+
                                  "                      found:    value\n");
        i = 2; System.out.println("i++ + ++i = " + (i++ + ++i) + "\n");
        i = 2; System.out.println("i+(i=3) = " + (i+(i=3)) + " evaluates left to right\n");
        i = 2; System.out.println("i+i++ precedence yields i+(i++) evaluates to 2+2 = " + (i+i++) + "\n");
        i = 2; System.out.println("i+++i precedence yields (i++)+i evaluates to 2+3 = " + (i+++i) + "\n");
        System.out.println("\n");
    }
}
Steve Campbell
  • 3,385
  • 1
  • 31
  • 43
Ahmad Taan
  • 21
  • 3
1

The reason why its 1440 is because

  1. x is set to 10 i.e 1st term is FIXED(overall equation 10 *)

  2. x is incremented by 1,x =11 now

  3. x is pre-incremented by 1 x=12 and second term FIXED now (overall equation 10 * 12 *)

  4. now x is set to 12 and third term FIXED(overall equation 10 * 12 *12)

  5. x is increment now but is in this case not used for evaluation,

in short a term is FIXED when variable occurs which in this case is X

2nd case: I'm not sure but I guess can be broken as,

  1. a=b+a
  2. a++

which I think is what is happening.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
  • It's clear. But look at the [table](http://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html). **prefix** has lower precedence than **postfix**. So why is it evaluated _before_ last postfix? That's the main problem for me. – D.R. Sep 24 '13 at 20:52
  • this may be wrong,but just a guess,is because of left to right evaluation in java..say its 2 *3 *5 in java, i think it straightaway doesnt multiply it to 30 it does 2*3=6 1st and den multiplies it by 5 – Kaushik Sivakumar Sep 24 '13 at 21:00
0

First step

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) prefix 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 * 12 * X++), X = 12;

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

This prints 10 * 12 * 12 = 1440

This is the bytecode for the first step

 1. bipush 10
 2. istore_0
 3. getstatic java/lang/System/out Ljava/io/PrintStream;
 4. iload_0
 5. iinc 0 1
 6. iinc 0 1
 7. iload_0
 8. imul
 9. iload_0
10. iinc 0 1
11. imul
12. invokevirtual java/io/PrintStream/println(I)V
13. return

This do the following:

 1. Push 10 to the stack
 2. Pop 10 from the stack to X variable
 3. Push X variable value (10) to the stack
 5. Increment local variable X (11)
 6. Increment local variable X (12)
 7. Push X variable value (12) to the stack
 8. Multiply top and subtop (10*12) Now Top = 120 
 9. Push X variable value (12) to the stack
10. Increment local variable X (13)
11. Multiply top and subtop (120*12) Now Top = 1440

Notice than the last increment (10.) is done after the push of X to the stack

Omar Mainegra
  • 4,006
  • 22
  • 30
0

For the second one ->

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

Compiler will convert it to

 a = a + b + a++;

Then apply your logic and you will find the reason why a is 4.

dganesh2002
  • 1,917
  • 1
  • 26
  • 29