Overflow and underflow are both errors resulting from a shortage of space. On the most basic level, they manifest in data types like integers and floating points.
Unlike the physical world, the number stored in a computer exists in a discrete number of digits. When we make a calculation that results in an extra digit, we cannot simply append that to our result, so we get an overflow or underflow error.
Overflow errors come up when working with integers and floating points, and underflow errors are generally just associated with floating points.
Overflow indicates that we have done a calculation that resulted in a number larger than the largest number we can represent. Let’s look at an example involving unsigned integers.
Lets assume we have an integer stored in byte. The greatest number we can store in one byte is , so let’s take that. This is . Now, suppose we add to it to get . The result is , which is . The result has bits, whereas the integers we are working with consist of only .
What does a computer then do in this scenario? A computer will discard the most-significant bit (MSB) and keep the rest.
This is essentially equal to .
Here, is the result, is the number of bits available, and is the modulo operator.
In the following C++ code, we demonstrate an overflow error by adding 1
in the maximum possible value for a one-byte unsigned char (255
). Since the result 256
is larger than the maximum representable value for an unsigned char
, it causes an overflow error.
#include <iostream>using namespace std;int main() {// Define a variable to store the value (1 byte)unsigned char value = 255; // Maximum value for 1 byte// Add 2 to the valuevalue = value + 1;// Print the resultcout << "The result after adding 2: " << static_cast<int>(value) << endl;return 0;}
In the output, instead of resulting in the expected value (256
), overflow causes the value to wrap around to the lowest value, which is 0
, when adding 1
.
Underflow is a bit trickier to understand because it has to do with precision in floating points. Again, due to the discrete nature of storage in computers, we cannot store an arbitrarily small number either. The floating-point convention comes up with techniques to represent fractional numbers. When we use these in calculations that result in a smaller number than our least value, we again exceed our designated space. Without going into details of floating-point representation, we can see how this problem would manifest by considering a decimal example.
Example: Suppose we are given designated boxes to write decimal numbers in. We have one box on the left of the decimal point and three boxes on the right. So, we can easily represent . Now, we want to perform a calculation, . The answer to this is , but we simply do not have this many places available to us. So we discard the least-significant bits and store , which is quite obviously an erroneous answer.
In the following C++ code, we demonstrate an underflow error by subtracting 1
from the minimum possible value for a one-byte unsigned char (0
). Since the result -1
is smaller than the minimum representable value for an unsigned char
, it causes an underflow error.
#include <iostream>using namespace std;int main() {// Define a variable to store the value (1 byte)unsigned char value = 0; // Minimum value for 1 byte// Subtract 2 from the value (underflow)value = value - 1;// Print the resultcout << "The result after subtracting 2: " << static_cast<int>(value) << endl;return 0;}
In the output, underflow results in the value wrapping around to the maximum value representable by an unsigned char (255
) when subtracting 1
rather than producing a negative number.