-2

This is regarding another question: Java Recursion Bug? I am going crazy.

I understand the solution there. But, why does C++ behaves differently than Java in this case?

Can anybody please give exact pointers (no pun) to C++/Java specifications? I know that java assigns 0 to sum before each call, while C++ does it differently. But what's the specification which allows this?

Edit: Adding code from link

public class Test {

public static int sum=0;

public static int fun(int n) {

    if (n == 1)
        return 1;
    else
        sum += fun(n - 1);  // this statement leads to weird output
    // { // the following block has right output
    //     int tmp = fun(n - 1);
    //     sum += tmp;
    // }

    return sum;
}

public static void main(String[] arg) {
    System.out.print(fun(5));
}
}

The output is 1 which should be 8. Relative C/C++ code is as follows:

#include<stdio.h>
int sum=0;
int fun(int n) {

    if (n == 1)
        return 1;
    else
        sum += fun(n - 1);

    return sum;
}

int main()
{
printf("%d",fun(5));

return 0;
}

Output in C++ is 8.

Community
  • 1
  • 1
sakura
  • 392
  • 1
  • 4
  • 12
  • Please show the code you are asking about. Perferably Java and C++ and the unexpected result. – jeremyjjbrown Feb 25 '14 at 01:54
  • The problem with the answer is that it says that `sum += fn(n)` expands to `sum = sum + fn(n)` when it doesn't (at least in C++) and those have different semantics, since in the second, it's unspecified whether `sum` or the function call is evaluated first. – chris Feb 25 '14 at 01:56
  • Java: [JLS 15.26.2](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.26.2) says that it's equivalent to `sum = (int)(sum + fun(n-1))`, and [JLS 15.7.1](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1) specifies that the left operand of `+` is evaluated first. I'm pretty sure C++ leaves the evaluation order undefined, but I haven't found the reference. – ajb Feb 25 '14 at 02:00
  • @ajb, It's unspecified (not undefined) per C++11 § 8.3.6 [dcl.fct.default]/9. – chris Feb 25 '14 at 02:02
  • 1
    @chris even if you change that to sum = sum + fn(n) does not change the result in C/C++ – sakura Feb 25 '14 at 02:03
  • 1
    @sakura, Not for you maybe, but [it could](http://coliru.stacked-crooked.com/a/b0b72776d6a1f9cd) depending on what the compiler wants to do. The part that is bothersome is that `sum` is being changed in the function call, so if that happens first, it will be updated when adding the result of that call to `sum`, and it won't be updated if not. – chris Feb 25 '14 at 02:04
  • @sakura Java doesn't 'assign 0 to sum before each call'. – user207421 Feb 25 '14 at 02:07
  • 1
    @chris yes, C++ does say that `sum += fn(n)` is equivalent to `sum = sum + fn(n)`, in 5.17(7) of [this](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf). – ajb Feb 25 '14 at 02:11
  • @ajb, Except that `sum` is only evaluated once, but thanks for that. It doesn't say *when* `sum` is evaluated, so I'm guessing I'm wrong and it suffers from the same problem. – chris Feb 25 '14 at 02:13
  • @chris Saying `sum` is evaluated only once is irrelevant here. In `sum = sum + fn(n)`, `sum` is evaluated once as an _lvalue_ which doesn't do anything interesting. It would be relevant in a case like `func(x)->y += something`, as opposed to `func(x)->y = func(x)->y + something`. Now `func` is called just once with `+=` but twice in the second case. – ajb Feb 25 '14 at 02:15
  • 1
    Different languages = different semantics. – Captain Obvlious Feb 25 '14 at 02:22
  • @sakura Here is a recent [C++ Standard](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf). Here is a recent [Java Language Specification](http://docs.oracle.com/javase/specs/jls/se7/html/index.html). You will be able to find everything you need in those. – Jason C Feb 25 '14 at 02:32
  • 1
    possible duplicate of [Java Recursion Bug? I am going crazy](http://stackoverflow.com/questions/11813692/java-recursion-bug-i-am-going-crazy) – Shafik Yaghmour Feb 25 '14 at 02:39
  • By the way, 1.9.15 in the C++ Standard is the relevant part: `Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.` It then goes on to give relevant examples. – Jason C Feb 25 '14 at 02:42
  • 2
    @JasonC I provided an answer in the original question, since this is a dup. Unfortunately the best way to get a better answer to a poorly answered or neglected question is a bounty but that is not available to the OP yet. – Shafik Yaghmour Feb 25 '14 at 02:47
  • @ShafikYaghmour I see it; I also agree that was the right thing to do. – Jason C Feb 25 '14 at 02:54
  • 2
    @ShafikYaghmour I am relatively new to SO, didn't know about bounty concept, also my (little) reputation suffered due to this question :( – sakura Feb 25 '14 at 04:03
  • @sakura I did not downvote, I realize this may not be obvious to relatively low rep users. This meta thread: [How do I get attention for old, unanswered questions?](http://meta.stackexchange.com/questions/7046/how-do-i-get-attention-for-old-unanswered-questions) explains your options. – Shafik Yaghmour Feb 25 '14 at 04:06
  • @ShafikYaghmour done! Getting used to SO! – sakura Feb 26 '14 at 22:37

3 Answers3

3

OK, combining everyone's comments:

This line:

sum += fun(n - 1);  // this statement leads to weird output

expands to

sum = sum + fun(n - 1);

in both C++ and Java. See JLS 15.26.2, C++11 draft section 5.17(7).

In Java, the language specifies that sum must be evaluated first, before the function is called. See JLS 15.7.1. In order to get this right (in case fun modifies sum, as it does), the code must read sum and save it somewhere, before it calls fun. After fun returns, the code then adds the saved version of sum to the result of fun. Since sum is never modified until after all the fun calls have been started, the result is that all the saved versions of sum are 0, and the result is 1.

In C++, the order in which the operands to + are evaluated is unspecified. (See 1.9.15 of the C++11 draft.) Because of this, the function result could be 1 or 8 depending on how the compiler decides to implement it. 8 is probably more likely, since the compiler will probably generate code that doesn't require sum to be saved in a temporary, and on some processors could generate an instruction that adds directly into sum without reading it first. But 1 would not be an incorrect result, since the evaluation order is unspecified and the result can change depending on the evaluation order. Moral: Don't write code like this.

ajb
  • 31,309
  • 3
  • 58
  • 84
  • 1
    1.9.15 in C++ is what you are looking for: `Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.` It then goes on to give a relevant example. – Jason C Feb 25 '14 at 02:42
  • 1
    The better choice here would have been to provide an answer to the original question like I did since this is basically a dup. – Shafik Yaghmour Feb 25 '14 at 02:50
  • sum += ... is working as expected with Visual Studio 2005 with modified code. I posted example code in an answer below. With VS2005, I have to declare the single instance of a class static variable outside of any class or function. – rcgldr Feb 25 '14 at 03:19
1

from jls http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1

15.7.1. Evaluate Left-Hand Operand First

The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.

If the operator is a compound-assignment operator (§15.26.2), then evaluation of the left-hand operand includes both remembering the variable that the left-hand operand denotes and fetching and saving that variable's value for use in the implied binary operation.

If evaluation of the left-hand operand of a binary operator completes abruptly, no part of the right-hand operand appears to have been evaluated.

for

sum += fun(n - 1);

sum is evaluated first and the value 0 is saved any change to the value of sum after this is ignored.

fun is then evaluated and is equal to 1

giving the result

sum = 0 + 1 

recursively this is the same as

sum = sum + sum + sum + sum + 1

or

sum = 0 + 0 + 0 + 0 + 1
Jason C
  • 38,729
  • 14
  • 126
  • 182
BevynQ
  • 8,089
  • 4
  • 25
  • 37
  • Here's a [recent C++ standard](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf) if you want to go to town on that, too. Compound assignment operators are down in 5.17. – Jason C Feb 25 '14 at 02:27
  • I should have expected it would be a working draft. – BevynQ Feb 25 '14 at 02:35
  • All the working drafts are free to download. The official releases must be purchased. The differences are usually trivial. In any case, the relevant info is in 1.9 (Program Execution). – Jason C Feb 25 '14 at 02:41
0

update - declaring sum to be volatile would prevent using a cached copy of sum in an expression, but this may not solve the order of evaluation issue.

If Java is always left to right then changing the statement to sum = fun(n-1) + sum should work, but the alternate code mentioned in the original example may be the only solution. volatile isn't needed in this example, but I left it there to show how it's implemented.

#include <iostream>

class Test{
public:
static volatile int sum;
static int fun(int n) {
    if (n == 1)
        return 1;
    int tmp = fun(n - 1);
    sum += tmp;
    return sum;
}
};

volatile int Test::sum = 0; // declare and initialize sum

int main(){
    std::cout << Test::fun(5) << std::endl;
    return(0);
}
rcgldr
  • 27,407
  • 3
  • 36
  • 61
  • I don't like the term "as expected" since it implies that this gives the "correct" result. As some of us have tried to explain, there are two possible correct results in C++ (maybe more, in theory); your modification doesn't have any effect on the issue. I do not want people to write code like this, period. I don't want to be driving an automatic car where the code looks like this because someone who didn't understand evaluation order "expected" it to work. – ajb Feb 25 '14 at 06:37
  • The issue isn't +=, precedence is right to left: [operator precedence](http://en.cppreference.com/w/cpp/language/operator_precedence). The issue is a conflict with how the original poster's compiler is handling a static member of a class: [static class member](http://en.cppreference.com/w/cpp/language/static). The static member should only get initialized one time at program start, but in the example it appears that it's getting repeatedly reinitialized. – rcgldr Feb 25 '14 at 08:49
  • Here is another C++ article with an example showing that a static member is only initialized one time, and can be updated afterwards, without getting reinitialized: [static class members](http://www.learncpp.com/cpp-tutorial/811-static-member-variables). – rcgldr Feb 25 '14 at 11:47
  • 1
    No, the static variable is **not** getting repeatedly reinitialized. Please read my answer carefully. Because Java requires the left argument of `+` be evaluated first (and because it treats `a+=b` identically to `a=a+b`), each `fun` invocation must read `sum` (the left argument) first, **store it in a temporary**, call `fun`, and then use the **temporary** to do the addition even though `sum` has changed. It is not rereading, or reinitializing, `sum`. C++'s rules allow compilers to behave the same way but does not require it. – ajb Feb 25 '14 at 15:37
  • OK, I see the issue with using a temporary copy of sum as an option, in which case the output would be 1. As you mentioned earlier, the compiler may opt to avoid the overhead of using a temporary copy of sum and evaluate fun() first. I'm curious what GCC would do with the example I posted. – rcgldr Feb 25 '14 at 18:30
  • GCC (4.8.1, MinGW-W64) outputs 8. – ajb Feb 25 '14 at 18:35
  • One way to "fix" the issue would be to declare sum as volatile, which would require that sum be used instead of a temporary whenever it is referenced (C++ or Java). – rcgldr Feb 25 '14 at 19:21
  • 1
    No, that would not fix it. `volatile` is used to force re-reads of variables that could be written by another thread (or by an external source), to prevent an optimizer from thinking it's OK to use the previous value. This is *not* an optimization issue. It's a semantics issue. The semantics of Java *require* that the output is 1. – ajb Feb 25 '14 at 19:31
  • So with Java, sum = fun(n-1) + sum should result in an output of 8. With C / C++, since the order of evaluation is not defined, it seems the alternative mentioned in the original post would be the only solution. – rcgldr Feb 25 '14 at 20:27
  • Right. Of course, the real solution is not to write code like this. Writing recursive routines that do their work using outside variables like this is a newbie mistake made by those who are just learning recursion. – ajb Feb 25 '14 at 21:15