views:

185

answers:

5

I made a program that returns the sum of all primes under 2 million. I really have no idea what's going on with this one, I get 142891895587 as my answer when the correct answer is 142913828922. It seems like its missing a few primes in there. I'm pretty sure the getPrime function works as it is supposed to. I used it a couple times before and worked correctly than. The code is as follows:

vector<int> getPrimes(int number);

int main()
{

    unsigned long int sum = 0;
    vector<int> primes = getPrimes(2000000);

    for(int i = 0; i < primes.size(); i++)
    {
        sum += primes[i];
    }

    cout << sum;

    return 0;
}


vector<int> getPrimes(int number)
{

    vector<bool> sieve(number+1,false);
    vector<int> primes;
    sieve[0] = true;
    sieve[1] = true;

    for(int i = 2; i <= number; i++)
    {
        if(sieve[i]==false)
        {
            primes.push_back(i);
            unsigned long int temp = i*i;
            while(temp <= number)
            {
                sieve[temp] = true;
                temp = temp + i;
            }
        }
    }
    return primes;
}
+2  A: 

You may be running into datatype limits: http://en.wikipedia.org/wiki/Long_integer.

Zian Choy
If he does, then it'll **wrap**, which clearly it hasn't.
Aviral Dasgupta
@aviraldg: Yes, the addition `sum += primes[i]` will wrap properly, but that's not the problem. The statement `temp = i*i` is overflowing, which is causing the sieve to be set up incorrectly.
Adam Rosenfield
+1  A: 

This line is the problem:

unsigned long int temp = i*i;
Dan
And the problem would be...
George Edison
Yes, it is, but why?
Potatoswatter
@Suma: all composite numbers below `i*i` are products of primes below `i`.
Potatoswatter
+10  A: 

The expression i*i overflows because i is an int. It is truncated before being assigned to temp. To avoid the overflow, cast it: static_cast<unsigned long>( i ) * i.

Even better, terminate iteration before that condition occurs: for(int i = 2; i*i <= number; i++).

Tested fixed.

Incidentally, you're kinda (un)lucky that this doesn't produce extra primes as well as missing some: the int value is signed, and could be negative upon overflow, and by my reading of §4.7/2, that would cause the inner loop to skip.

Potatoswatter
(-: w00t! 10K! :-)
Potatoswatter
Quick, somebody downvote two of his posts! ;-) (I kid; Congrats!)
James McNellis
@James: actually, it would be interesting to know if that happens…
Potatoswatter
uh, quick, somebody downvote 12 of my posts…
Potatoswatter
Congratz! [15c]
Matthieu M.
The results of signed integer overflow are undefined / implementation-defined. There are also a large number of signed / unsigned comparison warnings. - I just replaced ints with `unsigned long long` (yes, non-standard till C++0x, but probably quite universally available), and got the desired result (with existing code 1157974819 which is way off). - Integer overflows are a PITA in such challenges: I've been always planning to implement an integer wrapper that detects overflows, otherwise it is hard to determine if it is the algorithm or just the overflow that is producing wrong results.
visitor
@visitor: a number of existing implementations of "safe integer libraries" are pointed out in section 5.7 of "Secure Coding in C and C++" (Seacord). This set of slides is based on chapter 5 of the book, take a look starting on slide 84 (page 42): http://www.cert.org/archive/pdf/sd-bestpractices-integers060914.pdf Slides have less detail than the book.
Dan
A: 

I'll give you a hint. Take a closer look at the initial value you give to temp. What's the first value you exclude from sieve? Are there any other smaller multiples of i that should also be excluded? What different initial value could you use to make sure all the right numbers get skipped?

There are some techniques you can use to help figure this out yourself. One is to try to get your program working using a lower limit. Instead of 2 million, try, say, 30. It's small enough that you can calculate the correct answer quickly by hand, and even walk through your program on paper one line at a time. That will let you discover where things start to go wrong.

Another option is to use a debugger to walk through your program line-by-line. Debuggers are powerful tools, although they're not always easy to learn.

Instead of using a debugger to trace your program, you could print out messages as your program progressed. Say, have it print out each number in the result of getPrimes instead of just printing the sum. (That's another reason you'd want to try a lower limit first — to avoid being overwhelmed by the volume of output.)

Rob Kennedy
All good suggestions, in general, but in this case, none of them would have helped. Using `i*i` instead of `i+i` is a valid optimization. @Potatoswatter points out the real reason why `i*i` doesn't work but `i+i` would.
Dan
A: 

Your platform must have 64-bit longs. This line:

unsigned long int temp = i * i;

does not compute correctly because i is declared int and the multiplication result is also int (32-bit). Force the multiplication to be long:

unsigned long int temp = (unsigned long int) i * i;

On my system, long is 32-bit, so I had to change both temp and sum to be unsigned long long.

Mark Tolonen