Why don't we check for overflow?
In my last note, I talked about an example of an abstraction with significant flaws that has stuck around because it is generally considered to be "good enough." I also said I would give other examples of such abstractions. Here is one such example.What happens when you add two integers and the result is too large to fit in an integer of that type? In both C and C++, the official answer is that the result is undefined; the implementation is allowed to do as it pleases. What many programmers fail to realize is that the notion of "undefined" means that the implementation is not even required to behave consistently.
For example, I once encountered a complaint about a program that contained a statement similar to
x = y - z; if (x == 0) { /* ... */ }
in which x, y, and z were integer variables. The programmer complained that there seemed to be some circumstances in which it was possible for x to be zero even though the code in the braces was not executed.
When we investigated, we found that the compiler was generating code that was smarter than the user wanted. This particular machine had a condition code that was set automatically after every arithmetic operation. The code had four possible values: positive, negative, zero, or overflow. When y and z had values that caused their difference to be zero after overflow, the condition code would be set to overflow.
Now consider the code that it is logical for a compiler to generate for this statement. It can subtract z from y, store the result in x, and then execute the code in braces only if the condition code indicates a zero result. If there is an overflow, the code in braces will not be executed.
What happens if the result after the overflow happens to be zero? It is possible to argue that the result is zero, so the code in braces should be executed. However, doing so requires the compiler to generate an additional test on the result of the addition--a test that must be executed regardless of whether there is the possibility of overflow.
This case raised what has turned out to be an interesting and difficult question ever since: When a programming language chooses not to define the outcome of an operation, is it acceptable for that outcome to be something that appears logically impossible within the "normal" language framework? Or should C and C++ have said that whatever the result of an overflow it, it must be a number that then has all the properties of an ordinary number?
Whichever way you answer this question has its problems. For example, if the result of an integer overflow were required to be an ordinary number, what would happen if someone wanted to implement C++ in a way that checked for integer overflow? On the other hand, failure to check for overflow enables not only anomalies such as the one we have just seen, but also various kinds of security problems.
What ultimately decided the argument was the fact that many computers do not have a fast way of testing for integer overflow. Part of the trouble is that by their very nature, overflow tests must change the ordinary flow of control through the program. As processors become more parallel, control-flow changes become more expensive.
So in languages such as C and C++, where performance is important, we are still stuck with the old compromise that integer arithmetic produces accurate results except when it doesn't, and if we care about getting accurate results, it is our responsibility as programmers to ensure that we limit our inputs to what the processor can handle.

