18

Assume that I want to get sum of all squares from M to N. I googled a bit and found this formula:

(1^2 + 2^2 + 3^2 + ... + N^2) = (N * (N + 1) * (2N + 1)) / 6

so I write this code:

static void Main(string[] args)
{
    const int from = 10;
    const int to = 50000;
    Console.WriteLine(SumSquares(from, to));
    Console.WriteLine(SumSquares2(from, to));
}

static long SumSquares(int m, int n)
{
    checked
    {
        long x = m - 1;
        long y = n;
        return (((y*(y + 1)*(2*y + 1)) - (x*(x + 1)*(2*x + 1)))/6);
    }
}

static long SumSquares2(int m, int n)
{
    long sum = 0;

    for (int i = m; i <= n; ++i)
    {
        sum += i * i;
    }
    return sum;
}

it works fine until 40k, but when N becomes 50k it fails. Output for 50k:

41667916674715
25948336371355
Press any key to continue . . .

I think it's an overflow or something, so I added checked keyword and tried to change long to double, but I got the same result. How can it be explained? How to get correct result without loops?

Eborbob
  • 1,905
  • 1
  • 15
  • 30
Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151
  • 5
    Have you tried using BigInteger instead of long? –  Sep 28 '15 at 14:24
  • You didn't add `checked` to the `SumSquares2()` method, and it's that one which is overflowing. – Matthew Watson Sep 28 '15 at 14:34
  • Your code is not checking for errors and exceptions. So when there is one, you don't know it either. – RBarryYoung Sep 28 '15 at 14:44
  • `46341 * 46341` overflows the int capacity. – njzk2 Sep 28 '15 at 21:43
  • I encourage you to enable checked arithmetic in the project settings, at minimum in the debug build, but preferably also in the release build. That reduces performance a bit, but for most applications that's a small price to pay for detecting otherwise silent bugs. – CodesInChaos Sep 29 '15 at 07:47

3 Answers3

31

Your second method is overflowing because you are using an int in the loop. Change it to a long as follows (and also add checked):

static long SumSquares2(int m, int n)
{
    checked
    {
        long sum = 0;

        for (long i = m; i <= n; ++i)
        {
            sum += i*i;
        }
        return sum;
    }
}

What was going wrong is that i*i was being calculated internally as an int data type even though the result was being cast to a long data type (i.e. the variable sum), and so it overflowed.

Community
  • 1
  • 1
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 2
    I am wondering how this was substantially different from the answer I had already submitted telling him to change every int to long or BigInteger. –  Sep 28 '15 at 15:01
  • 1
    @GiliusMaximus There are differences such as you're talking about using BigInt and I'm not. Also, to use `long` as the result type, you don't need to change the parameters `m` and `n` to `long` as you suggest. Note that you often get similar answers to questions posted; that's the nature of StackOverflow. I was typing in my answer while you were posting yours, therefore I didn't see it before I posted mine. I judged the answers different enough to leave this here. (And note that I did upvote your answer, since it's useful.) – Matthew Watson Sep 28 '15 at 15:26
  • 2
    More than fair enough. Upvoting yours as well as it did add to my understanding. –  Sep 28 '15 at 15:30
  • Thanks. My problem was that I was thinking that second method is ideal when first may fail. I didn't assume that it could be vice versa. – Alex Zhukovskiy Sep 28 '15 at 16:07
12

While you are using long for the result, you are still using int for the operators. I would define M and N as long or even BigInteger, and the same for the result. If you do not, you are probably doing int arithmetic still, even though your result is of type long.

I tried your code, and got the results you got. But then I changed every int to long and got the two numbers to match, up to an N of 1600000.

Using BigInteger, I am up to 160000000 and still working ok (result for m=10 and n=160000000 is 13653333461333333359999715, both ways).

To use BigInteger, you will need to add a reference to the System.Numerics dll to your project, and you will need to have a statement at the top of your code including that library.

using System.Numerics;

namespace ConsoleFiddle
{
  class Program
  {
    static void Main(string[] args)
    {
        BigInteger from = 10;
        BigInteger to = 160000000;
        Console.WriteLine(SumSquares(from, to));
        Console.WriteLine(SumSquares2(from, to));
        Console.ReadKey();
    }
    static BigInteger SumSquares(BigInteger m, BigInteger n)
    {
        checked
        {
            BigInteger x = m - 1;
            BigInteger y = n;
            return (((y * (y + 1) * (2 * y + 1)) - (x * (x + 1) * (2 * x + 1))) / 6);
        }
    }
    static BigInteger SumSquares2(BigInteger m, BigInteger n)
    {
      checked
      {
        BigInteger sum = 0;
        for (BigInteger i = m; i <= n; ++i)
        {
            sum += i * i;
        }
        return sum;
      }
    }

For an M of 4000000000000000000 (4 x 10^18), and an N of 4000000000100000000. This code still works and gives an immediate result with the first method (1600000016040000000400333333338333333350000000). With the second method it takes it a little while (100 million loop iterations) but gives the same result.

3

Most probably you are experiencing integer overflow, as the range of long is limited. Probably you have disabled exceptions for integer overflow, so no exception is thrown. The exceptions for integer overflow can be disabled and enabled in the project properties in Visual Studio, if I'm not mistaken.

Codor
  • 17,447
  • 9
  • 29
  • 56