tags:

views:

118

answers:

7

Hi all!

I encountered something that I can't understand.

I have this code:

cout << "f1 * f1 + f2 * f1 - f1 / f2 is: "<< f1 * f1 + f2 * f1 - f1 / f2 << endl;

All the "f"s are objects, and all the operators are overloaded.

The weird this is that the first computation is of the / operator, then the second * and then the first *; after that, the operator + and at last, operator -.

So basically, the / and * worked from right to left, and the + and - operators worked from left to right.

I made another test... I checked this code:

 cout << "f1 * f1 / f2 is: " << f1 * f1 / f2 << endl;

Now, the first operator was * and only then operator /. So now, it worked from left to right.

Can someone help me understand why is there difference in the directions?

10X!

+9  A: 

This is yet again a question of the order of evaluation of function parameters - C++ does not specify such an order. Your code is equivalent to:

(f1 * f1) + (f2 * f1) - (f1 / f2)

The three multiply and divide operations can be evaluated in any order. This is perhaps cleraer for named functions:

add(f1*f2,f2*f1)).minus(f1/f2);

The bottom line is that associativity and precedence have nothing to say about the order of evaluation of function parameters and/or sub-expressions. Given the simple expression:

a + b

the C++ (and C) compiler is free to evaluate a first, then b, or b first then a, whether or not the '+' is overloaded.

anon
YES! Neil, you're apparently in the top quintile ;v) — and it's worth noting, just because there are so many wrong answers, that associativity is NOT affected by the surprising order of evaluation.
Potatoswatter
Or, `sub( add( mul( f1, f2 ), mul( f2, f1 ) ), div( f1, f2 ) )`
Potatoswatter
+2  A: 

It is unspecified in what sequence operator arguments will be calculated.

C++ Standard 5/4:

Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.

Your expression is equivalent to (* and / are operators too, but leave them as is):

operator-( operator+(f1*f1, f2*f1), f1/f2 )
Kirill V. Lyadvinsky
A: 

http://www.cppreference.com/wiki/operator_precedence strange behavior, indeed. But what i would say is that this should not matter, because a * b / c should be equal to a / c * b if you implemented them mathematical-wise

Andrey
I did not downvote, but the most probable reason is that the order of evaluation is defined and no reordering of the actual operations take place. Also consider that there are real differences: `int a = 5, b = 2, c = 10; assert( 1 == (a*b/c) ); assert( 0 == (a/c*b) )` A more complex example can be found for doubles, due to the fact that operations in computers are actually discrete, so the order of evaluation does modify the end result.
David Rodríguez - dribeas
@David Rodríguez - dribeas - i didn't undestood from your samples how order can affect result.
Andrey
@Andrey: when `a`, `b`, and `c` are `int`, `a/c = 0`, so `a/c*b = 0`.
Bill
the fact is that when working with discrete math, the order of operations affect. In the simple integer example, `a/c` is 0, but `a*b/c` is 1, so even if in real world mathematics the order or multiplication and division are irrelevant (`a*b/c = a/c*b`) in computing the results are not the same.
David Rodríguez - dribeas
A: 

This just looks like simple operator precedence at work.

In your first example, all the multiplication and division must be done before the addition and subtraction. Since the results of the multiplications and divisions are independent, it really doesn't matter what order they're performed, just that the results are used from left to right in the addition and subtraction.

In the second example, the multiplication and division are not independent and must be performed left to right.

You're always getting the correct results based upon operator precedence. THat's all that really matters. The compiler does not guarantee anything about the order of evaluation other than that operator precedence is honored.

Mike Cargal
+1  A: 

Operator precedence defines the order of operators that have different precedence, so that, e.g., * and / always evaluate before + and -. Then there is the left-to right rule when multiple operators of the same precedence are concatenated.

However, there is (with the exception of logical and ternary operators) no rule about which of an operator's arguments should be evaluated first. The compiler is free to perform the multiplicative operations in any order in pleases before passing them to the additive operators.

In fact, with the expression f() + g() + h(), the compiler is free to call the functions in reverse order, h(), then g(), then f(), then add together the result of f() and g(), and finally add the result of h(). That wouldn't be a very sensible thing to do in most cases, but it's perfectly legal.

Marcelo Cantos
Fine, down-vote me, but at least say why!
Marcelo Cantos
A: 

Neil is right . It is matter of operator associativity and precedence. The expression is evaluated as (f1 * f1) + (f2 * f1) - (f1 / f2) as per the rules suggests and after that it is left to right in second pass . lastly addition and sub. takes place.

second example is simple. * and / have same precedence so we evaluate the expression as per the associativity rules which is left to right hence the order.

Grv
Neil didn't say what you seem to think he said.
anon
A: 

User defined operators use the same precedence and associativity rule than buil-in ones.

Rule of precedence state that operators with higher precedence should be executed before those with lower precedence when adjacent in an expression (separated by a single operator).

Associativity rule state in wich order operators should be executed when an expression contains adjacent operators of same precedence.

  • In your first exemple precedence rule apply, but as associativity is only about adjacent operators the compiler choose in which order he will execute multiply and divide.

  • In Your second exemple the asociativity rule applies.

Rule of thumb to avoid problems with this kind of rules (that can be somewhat complex):

  • if unsure use parenthesis or local variables to force order.
  • avoid side effect when you call function (or user defined operators) because result could be surprising
  • when redefining operators try to be consistent with maths
kriss