Reputation: 9
The task is to count the number of primes numbers below an input parameter N. I am testing this code with different input number ranging from small to large. It is very strange, I only encountered a runtime error if the input value is larger and equal than 46342. If the input value is smaller than 46342, the code runs perfectly fine without any runtime error.
int countPrimes(int n) {
if (n <= 2) return 0;
vector<bool> array(n,true);
int cnt = 0;
for (int i = 2 ; i < n ; i++)
{
for (int j = i ; i * j < n ; j++)
{
int product = i * j;
if (array[product])
{
array[product] = false;
cnt++;
}
}
}
return n - 2 - cnt;
}
If the input is bigger and equal than 46342, I will see "Runtime Error ", if the input is smaller than 46342, the code runs fine and the result is correct.
Upvotes: 0
Views: 933
Reputation: 12779
As noted in the other answers, what the OP is experiencing is the combination of two issues, integer overflow and out of bounds access of a vector, both causes of undefined behavior.
The line
int product = i * j;
Declares an int
variable initialized with the product of i
and j
which are of type int
, which, quoting https://en.cppreference.com/w/cpp/language/types
If no length modifiers are present, it's guaranteed to have a width of at least 16 bits. However, on 32/64 bit systems it is almost exclusively guaranteed to have width of at least 32 bits.
Apparently, in OP's system an int
has a 32-bit width, so that a value of i
greater then 46341 makes the operation 46342 * 46342 = 2147580964 overflow the range of representable numbers (INT_MAX likely beeing 2147483647).
In case of overflow, the behavior is undefined, but in most architectures, where integers are represented in 2's complement, a negative number can be obtained, which leads to the other cause of undefined behavior and probably the "Runtime error" mentioned in the question:
if (array[product]) // <- 'product' can be negative
// 'array.at(product)' would have caught this error
{
array[product] = false;
//…
An easy way to prevent this issue is using a type big enough to safely perform the operation, like:
long long int product = static_cast<long long int>(i) * j;
An alternative is to rewrite the logic to get rid of the troublesome part:
#include <iostream>
#include <vector>
#include <cassert>
// Counts the number of prime numbers less than or equal to 'n'
long int count_primes(long int n)
{
if (n < 2)
return 0;
// Initialize the sieve. The first two elements (0 and 1) are ignored.
std::vector<bool> sieve(n + 1, true);
long int count = 0;
for (long int i = 2; i <= n; ++i)
{
// If a prime is found, delete all its multiples.
if ( sieve[i] )
{
// Update the counter, once only.
++count;
// Instead of performing a possible overflowing operation, you can
// use a bigger type, capable of store the result, or a different logic:
for (long int j = i + i; j <= n; j += i)
{
sieve[j] = false;
}
}
}
return count;
}
int main(void)
{
// Source: https://en.wikipedia.org/wiki/Prime-counting_function
assert(count_primes(1) == 0);
assert(count_primes(2) == 1);
assert(count_primes(10) == 4);
assert(count_primes(101) == 26);
assert(count_primes(1000) == 168);
assert(count_primes(10000) == 1229);
assert(count_primes(100000) == 9592);
assert(count_primes(1000000) == 78498);
std::cout << "So far, so good.\n";
}
Upvotes: 0
Reputation: 17533
The maximum of an int
is the 31th power of 2, which is equal to 2147483648.
The square root of this is 46340.95, so the value you're working with (by performing i*j
) goes above that limit, causing the runtime exception.
Upvotes: 0
Reputation: 73384
This:
int product = i * j;
will overflow, making product
equal to a negative number.
Then, you will attempt to access array
(which is actually a vector, so naming it array
is not a good choice) with a negative index, which will make your program crash.
You need to use types that can support larger numerical values. Now you used int
(C++ Integer Limits). Use unsigned int
for n
, i
, j
, and product
.
Upvotes: 2