0

What's the difference between --x of x-1? I have a method which calculates the sum of the first n numbers I try with 100 and the result is the following:

int sumaRec(int _suma){
    if (_suma > 0)
    {
        return _suma + sumaRec(--_suma);
    }
    else
        return 0;
}

When i put --_suma the result is 4950 and when i put _suma -1 the result is 5050

Can anyone explain why? Please.

user2485710
  • 9,451
  • 13
  • 58
  • 102
Adrián Romero
  • 620
  • 7
  • 17
  • 1
    What do you think each does? – chris May 11 '14 at 04:14
  • http://en.cppreference.com/w/cpp/language/operator_incdec , note that `--` is an operator that can possibly be overloaded . – user2485710 May 11 '14 at 04:16
  • 6
    Using the same value twice in an expression containing a modifying operator is undefined behavior. – Mark Ransom May 11 '14 at 04:16
  • 3
    well at this point we should also say that names that begin with `_` an underscore are really bad practice and they are reserved by the standard. – user2485710 May 11 '14 at 04:18
  • --_suma subtract before _suma can be used – Adrián Romero May 11 '14 at 04:19
  • Correct...and how many times is _suma read on the same line? :p – Mike S May 11 '14 at 04:20
  • @user2485710 i do not overload the -- operator, and thanks for the observation of _ use – Adrián Romero May 11 '14 at 04:22
  • @MikeS Two times, the the first time the call is return = 99 + sumRec(99) ? – Adrián Romero May 11 '14 at 04:26
  • Mark, that actually raises a question: Are both accesses to _suma actually considered to be part of the same expression here, or did Bob just misunderstand the order of operations? It's not good practice in any case, but I essentially read it as a nested expression (_suma + sumaRec(--_suma)), where the inner expression sumaRec(--_suma) logically has to be evaluated before the addition. According to the standard, does the compiler even have the freedom to read the leftmost _suma into a register before computing sumaRec(--_suma)? That is, is it truly undefined in this case? – Mike S May 11 '14 at 04:26
  • 2
    This should help? http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points – Brandon May 11 '14 at 04:30
  • 1
    @MikeS the whole thing is one expression made up of subexpressions. The more pedantic way to express the issue is that the modification and accesses occur between the same two sequence points – Ryan Haining May 11 '14 at 04:31
  • 1
    Bob, yes, that's what seems to be going on here. Based on an intuitive order of operations, I would expect that "99 + sumRec(99)" is the only correct outcome of the sumRec(100), but if Mark Ransom is interpreting the C++ standard right (and he probably is!), it's actually "undefined behavior." That is, it may work differently depending on the compiler, or it might conjure nasal demons. Either way, it's not a good idea to use the same value twice in a line where one of the uses modifies it. – Mike S May 11 '14 at 04:32
  • 4
    It's still undefined. The C++11 standard guarantees that `--_suma` is evaluated before `sumaRec` is called and that the two operands of the `+` operator are evaluated before their results are used by the operator, but the evaluation of `_suma` and `sumaRec(--_suma)` is unsequenced, so the compiler is free to evaluate `_suma` first, `--_suma` second, `sumaRec` third, or `--_suma` first, `sumaRec` second, `_suma` third, or `--_suma` first, `_suma` second, and `sumaRec` third; or indeed conjure nasal demons, chomp up your hard drive or whatever. – T.C. May 11 '14 at 04:37
  • This makes things all the more confusing: http://www.eelis.net/C++/analogliterals.xhtml – Brandon May 11 '14 at 04:47

3 Answers3

6

This is to elaborate on the order of evaluation issue.

Here's what the C++11 standard says about order of evaluation:

  • Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated. (§1.9 [intro.execution]/p15)
  • The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. (§1.9 [intro.execution]/p15)
  • Evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. (§1.9 [intro.execution]/p15)
  • When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. (§1.9 [intro.execution]/p16)
  • Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function. (§1.9 [intro.execution]/p16)

If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. (§1.9 [intro.execution]/p15)

Now we can apply these rules to this expression: _suma + sumaRec(--_suma).

  1. The evaluations of the two operands of the + operator is unsequenced. The compiler is free to evaluate _suma first, then sumaRec(--_suma), or the reverse.
  2. The value computation and side effects of --_suma is sequenced before the call to sumaRec.
  3. Everything is indeterminately sequenced with respect to the sumaRec call itself; i.e., the compiler could evaluate _suma either before or after the execution of the statements in the sumaRec function, but not during it.
  4. The value computations, but not the side effect, of the two operands of + are sequenced before computation of the result of the + operator.

In particular, because the side effect (storing the decremented value) on a scalar object (_suma) is unsequenced with respect to a value computation using the value of the same scalar object (computation of the value of the first operand of +), the behavior is undefined.

A conforming compiler can do any of the following:

  1. evaluate _suma first, --_suma second, sumaRec third
  2. evaluate --_suma first, sumaRec second, _suma third
  3. evaluate --_suma first, _suma second, and sumaRec third
  4. perform the value computation of --_suma first, the value computation of _suma second, the side effect of --_suma (storing the decremented value) third, and sumaRec fourth
  5. conjure nasal demons, chomp up your hard drive, or anything else it wants to do. When the behavior is undefined, all bets are off.

It is worth emphasizing that operator precedence and order of evaluation are entirely different things. Operator precedence means that an expression like f() + g() * h() is interpreted by the compiler as f() + (g() * h()) and not (f() + g()) * h(), but there is no guarantee whatsoever that f(), g(), and h() will be evaluated in any particular order. In fact, if this expression appears twice in the same code, the compiler is not even required to be consistent: the evaluation order can be f(), g(), h() in one and g(), f(), h() in another.

Edit: To note that GCC, as expected, emits a warning for this code:

g++ -march=native -std=c++11 -Wall -Wextra -pedantic main.cpp && ./a.out
main.cpp: In function 'int sumaRec(int)':
main.cpp:9:39: warning: operation on '_suma' may be undefined [-Wsequence-point]
         return _suma + sumaRec(--_suma);
                                       ^

It's always a good idea to compile with full warnings enabled.

T.C.
  • 133,968
  • 17
  • 288
  • 421
3

the basic difference is that, when you do x-1, the value of x remains the same. But when you do --x, the value of x is decremented. So,

Let,

x=4;
y=x-1;

Now, in this case, x is 4 and y is 3. Now, for same x=4,

y = --x;

now x and y both are 3.

Aswin Murugesh
  • 10,831
  • 10
  • 40
  • 69
1

The prefix decrement is modifying the variable you are using not only to send it to the function but also in your sequence calculation. That's why you end up with a wrong calculation.

I suggest you put the subtraction in a temp variable and just pass that variable to your function.

stripthesoul
  • 362
  • 1
  • 8
  • [http://msdn.microsoft.com/en-us/library/dy3d35h8.aspx] I've been reading about this for a bit, and thats what I thought initially until I read the below statement in the link I posted above: "When a prefix operator is applied to a function argument, the value of the argument is not guaranteed to be incremented or decremented before it is passed to the function. See section 1.9.17 in the C++ standard for more information." – stripthesoul May 11 '14 at 04:36
  • 1
    That's quite off. Even in the older version of the standard, there's a sequence point after argument evaluation and before the execution of the function body. – T.C. May 11 '14 at 04:45
  • I edited the post to have a link to the msdn article so that I dont get blamed for any information that would sound wrong. – stripthesoul May 11 '14 at 04:51
  • the article is very poorly worded if there is a way that it's right at all. It references "1.9.17" of the standard, which doesn't exist as of the c++11 draft – Ryan Haining May 11 '14 at 04:52
  • 1
    @T.C. or perhaps it's a reference to an MSVC error we should all be aware of – Ryan Haining May 11 '14 at 04:52
  • 3
    To clarify this, in `f(--a)`, if `f` is a function,the value received by `f` must be `a-1`, and the decrement of `a` must be complete when the function is entered (because there is a sequence point upon entering a function). [This is for pre-C++11; T.C.'s post covers the new sequencing rules] – M.M May 11 '14 at 05:26
  • update: the offending microsoft page is no longer there – Ryan Haining Jun 24 '14 at 00:05
  • update again, it's [here](http://msdn.microsoft.com/EN-US/LIBRARY/dy3d35h8(v=vs.100).ASPX) now. there's a community addon showing at the bottom now bringing attention to the issue. it says it's from 2010 though, I wonder why it didn't show before. – Ryan Haining Jun 26 '14 at 18:24
  • 1
    if you look at the versions of the page for newer versions of visual studio, it no longer has the offending text. This makes me think that older vs compilers were broken and they've since fixed the std violation, but not patched the old compilers. – Ryan Haining Jun 26 '14 at 18:30