1

I have a very simple calculation where I calculate the distance between each individual cell to center in a 2D space. I know that the O(n) solution is redundant and I derived the formula for O(1) solution. But what I am trying to understand is: Why do these two analogous calculations give me two different results?

Here is the expected (correct) result in Python (Both versions give the same result):

result = 0;
n = 499993;
center = (n+1)//2;
for ii in range(1,center):
        result += (ii*ii*8);

print(result);

which outputs:

41664916690999888

And here are two versions in C++ with two completely different wrong results:

1)

#include <iostream>
using namespace std;
int main()
{
    unsigned long long result = 0;
    int n = 499993;
    int center = (n+1)/2;
    for(int ii = 1; ii < center; ++ii)
    {
        result += (ii*ii);
    }
    cout << result*8 << endl;
}

Output:

154435732281936
#include <iostream>
using namespace std;
int main()
{
    unsigned long long result = 0;
    int n = 499993;
    int center = (n+1)/2;
    for(int ii = 1; ii < center; ++ii)
    {
        result += (ii*ii*8);
    }
    cout << result << endl;
}

Output:

6229295798864 

What is the reason of this behavior?

For compiler I am using GCC with only -g flag

Online compiler for CPP that produces the same result: https://www.onlinegdb.com/online_c++_compiler

Thanks in advance!

9
  • 6
    I expect that at some point ii*ii will be too large for a 32 bit int. Remember since its int * int the calculation is done as an int not unsigned long long int. Commented Apr 12, 2021 at 12:53
  • 4
    Are you checking for integer overflow? Commented Apr 12, 2021 at 12:53
  • 3
    since ii * ii both operands are int so the calculation is done as an int. This is not related to registers. Commented Apr 12, 2021 at 12:57
  • 3
    In Python, integral values are unbounded. In C++, all integral types are bounded, and can overflow. Overflowing a signed type gives undefined behaviour - it doesn't magically ensure the large value can be represented. Commented Apr 12, 2021 at 12:58
  • 3
    For your second case, using result += (8LL*ii*ii); should fix things (forcing the calculation to be performed as long long). In your first case, you would need to declare ii as a long long, or cast one of the operands of the *. Commented Apr 12, 2021 at 13:02

1 Answer 1

3

Typical int (signed 32bit) can store only upto 2,147,483,647 (2**31 - 1).

The calculation of ii*ii will exceed this limit when ii becomes larger than 46340.

You are using unsigned long long for result, so casting to that before calculation will improve the behavior.

#include <iostream>
using namespace std;
int main()
{
    unsigned long long result = 0;
    int n = 499993;
    int center = (n+1)/2;
    for(int ii = 1; ii < center; ++ii)
    {
        result += (static_cast<unsigned long long>(ii)*ii); // add casting
    }
    cout << result*8 << endl;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Basically c++ compiler calculates first like so unsigned long long result = <some result of ii*ii> which means it calculates ii * ii first and since ii is int it keeps the result in int then it makes the conversion to unsigned long long at the operator =

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.