I was happily playing with my shiny new prime-number-checker, trying out how loops work in C++.
When I started entering stupidly big numbers, something strange happened.
./main
Enter a number: 5784320578432578493207508493
Congratulations, it's prime!
Except, that is not actually a prime number. I can’t have typed a prime number by randomly banging on the keypad. What’s going on?
I didn’t code any input sanitization in my prime-number-checker, so let’s check what my program actually gets. I am now removing all the boring logic, and compiling this simple code:
// Main.cpp
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
int x;
cout << "Enter a number: ";
cin >> x;
cout << "You entered " << x << endl;
return 0;
}
Let’s play.
./main
Enter a number: 2147483647 <-- int32 maximum positive value
You entered 2147483647
./main
Enter a number: 2147483648 <-- int32 maximum positive value +1
You entered 2147483647
./main
Enter a number: 99999999999999999999999999999999999999999999999999 <-- a lot
You entered 2147483647
With this, the first mystery is solved: I was always checking the same value, no matter what big number I was entering. And it happened to be prime (yes: (2^32)/2-1
is indeed prime!).
But now I have another problem: why do I always get the same number? The variable x
is a 32-bit integer and it can’t hold those big numbers. But still, I was expecting the integer overflow to wrap around the maximum value.
For example: 2147483647
is the maximum value for a signed 32-bit integer. I expected the input 2147483648
to be stored in x
as the smallest possible value: -2147483648
.
Instead, I get 2147483647
every time.
Let’s try something different.
./main
Enter a number: I am not a number, I am a free man!
You entered 0
Woops. OK there’s some magic going on here, and it is not about integer representation. It’s std::cin
doing the magic!
We need some differential diagnosis. I am now removing std::cin
and hardcoding the values.
// Main.cpp
#include <iostream>
using std::cout;
using std::endl;
int main()
{
// By the way: "int" has its constructor overloaded, too!
int x(2147483647);
cout << "2147483647 = " << x << endl;
x = x + 1;
cout << "2147483647+1 = " << x << endl;
return 0;
}
./main
2147483647 = 2147483647
2147483647+1 = -2147483648
OK, that’s better! Finally, a good old wrapping integer overflow.