Arithmetic Overflow in C#
Arithmetic overflow happens when you perform an operation with a data type and the result of this operation exceeds the size of a storage for this datatype. For example, when you add two large 32-bit integers together and the result cannot fit into Int32 and overflows.
int first = Int32.MaxValue;
int second = first + 1;
Console.WriteLine(first);
Console.WriteLine(second);
// prints 2147483647
// pritns -2147483648
C# deals with these situations with special keywords checked and uncheck and you can use them in a single line or as a block statements. Let’s add them to our previous example and see what happens.
checked
{
int first = Int32.MaxValue;
int second = first + 1;
}
With a standard configuration, this code throws an OverflowException saying that an arithmetic operation resulted in an overflow. So, adding this keyword allows you to make sure that any computation with primitive types won’t create unexpected results.
Internally CLR creates add instruction for unchecked additions and it has a special one called add.ovf for checking for the overflow. Obviously, the first one without the check performs better.
One important thing to note here is that CLR supports overflow check only for its primitives types and if you examine them carefully, decimal is not there as well as BigInt. Let’s run the following code.
unchecked
{
decimal first = Decimal.MaxValue;
decimal second = first + 1;
}
Even though the code is in an unchecked block and we are expecting a smooth run, it throws an OverflowException, because decimal isn’t a primitive type and doesn’t have a special CLR instruction.
I mentioned that unchecked operations are usually, therefore the default C# project settings don’t check for an overflow and you must state it explicitly. You can do it in project’s Properties => Build => Advanced => Check for arithmetic overflow/underflow. It will turn the compiler’s checked switch on.
So far it sounded like the overflow is a bad thing and you should avoid it, but it isn’t always true. There are some use cases when you definitely want to take an advantage of it. One of them is hash code generation within the GetHashCode method.
public override int GetHashCode()
{
unchecked
{
// Hash generation here
}
}
So, what are the best practices with an overflow? First of all, you should use \checked switch for development and, because it adds little extra overhead, turn it off in production.
Secondly, state explicitly checked and unchecked blocks of code in your solution where it matters and you expect input values to cause an overflow issue.
The last advice also applies to the first one and if you don’t care about overflow at all, it’s not necessary to switch the compiler even for development.