Unhandled Overflow
Happened earlier this morning, I stumbled over a piece of code that looks something like this (can’t remember from where):
var x = 1;
var y = int.MaxValue + x;
Scanning the code, I was actually expecting an exception being thrown by the CLR. Well, apparently not.
The result of y on the above assignment is -2,147,483,648, or in binary 00000000.
For those uninitiated about how addition works inside a computer, here’s a quick take:
- All things in a computer is expressed in a binary format. So for example, 3 in decimal equals to 11 (one-one, not eleven) in binary.
- When you add, do a usual addition, but if the result is 2, write 0 and add 1 to the next digit. For example, 3 + 2 (in decimal) equals to 11 + 10 in binary. Take least significant digit (the rightmost one), add, and move towards the left. The result would be 101 in binary.
- int in C# contains 32 digit of binary, a.k.a. 32 bit. Thus, the maximum value is 1111 1111 1111 1111 1111 1111 1111 1111 binary or 4,294,967,295. But since we need to take account negative numbers as well, someone devises a clever idea, anything with 1 on the most significant digit (the right-most digit), means positive, otherwise negative. Thus, 0 decimal starts from 1000 0000 0000 0000 0000 0000 0000 0000 binary.
- Now take int.MaxValue, and add 1 to it. The result would be 1 0000 0000 0000 0000 0000 0000 0000 0000 (1 followed with 32-zeroes). This will cause an overflow, which means more data than the memory can handle (which is 33 digit versus 32 digit). The processor ditches the rightmost digit, thus creating a variable that contains all zero. All zero = -2,147,483,648 decimal.
Now the worrying part is, .NET handled this too gracefully, and instead of throwing exception, it just removes the most significant digit. A coworker said, this was part of an Intel specification. Well, that means extra work for us! So if you aware that you are doing calculations on a large number, please check the result before using it. If you’re using int, you can use long as the result and check if it is within boundary of int.MaxValue (or int.MinValue depending on your situation).
Here’s the piece of code that somehow illustrates that idea:
function int SaveAddition(int a, int b) {
long result = a + b;
if (result > int.MaxValue || result < int.MinValue) throw new ArgumentOutOfRangeException();
return (int)result;
}
Addendum:
Apparently there’s an additional keyword that requires these checks to be done explicitly. The keyword is checked, and more info can be found at MSDN. Phew!