The problem is overflow, which can be solved by changing some of the types in your code (I'm going to pretend
i is predeclared here):
int sum = 0;
int upLimit = 2000000;
int i = 2;
int j = i;
int is commonly a 32-bit integer, this isn't required. And we really care about the size (and the range it implies) of our integer types. So let's start by switching to the fixed width integer types defined in
cstdint*. We should also switch to the unsigned variety. Since our code only uses positive integers, this effectively doubles our overflow-safe range:
uint32_t sum = 0;
uint32_t upLimit = 2000000;
uint32_t i = 2;
uint32_t j = i;
This helps, but will still break eventually. So let's establish an upper bound on the maximum value that we'd like to store in
sum. There's some really interesting math in this area, but for now, let's pretend we don't know anything about primes. We only know that we'll be summing distinct 32-bit numbers. So that's the sum of at most 2^32 values. And the largest possible value is 2^32. Which means that
sum can never grow larger than 2^32 * 2^32 = 2^64. And we have a type that is guaranteed to be able to contain that:
uint64_t sum = 0;
No more overflow.
If you're interested,
cstdint also contains type definitions like
uint_fast32_t, which allow the compiler to use a larger type if that would allow it to generate more efficient code. See for example cstdint on CppReference. Usual caveats regarding premature optimization apply, of course.
One last tip, not related to overflow: a
bool takes up 1 bytes per value, while a
std::vector<bool> is allowed to be more space-efficient. And space-efficiency (and more generally cache-friendliness) will be paramount if you want to take this beyond a few million.
*) These definitions require C++11. If that's not available, many compilers offer similar types in C++98 mode, although portability might suffer. Alternatively, if your compiler supports C99, you might be able to get working fixed width definitions from