Tuesday, April 10, 2012

Handling Arithmetic Overflows in C#

In this post we will be discussing rather unknown feature of C#. This is related to the behavior of integer arithmetic in the event of overflow.

Compile Time Overflow Checks:
C# compiler checks for overflow of assignments and expressions involving only numerical literals. Let's see the follows:

If we build the project, we get the same error in the error list.

With decimal, the same overflow issue is reported as follows:

The situation gets interesting when the expression results are being assigned to a variable of a type which clearly can hold the value as follows:

Here "ticks" can clearly hold the result of the expression as it is a long type but since the members involved in the expression are default Int32, that is why, the compiler thinks that the result can not be computed without an overflow. In order to fix that we can clearly specify the types of members of expression as follows:

That is why widening conversions are implicit but we have to clearly state narrowing conversions. This could have easily worked if, in this case, we just had specified one of the operands involved as of long type. In a case like following, the types of the operands are promoted as the operand type which can hold largest value (if such implicit promotion) are allowed and then the operator (multiplication) is applied. This is also the type of result of such expression. In the following case, we are just updating one of the involved operands to be explicitly of type long. This fixes the compiler concern of possible overflow because of the operands type promotions.

Please remember that there are no such compile or run-time overflow checks for floating point numbers. As in the following example, this seems to be an overflow by assigning a value which is greater than the maximum value that the type can hold, but it compiles fine and there are also no run-time issues.

Overflows during Program execution:
Different numeric types have different behaviors at run-time if one of the operand is non-literal. Float never cause any issue when the expression is apparently resulting in an overflow. Decimal always cause overflow exception. These behaviors cannot be controlled.

Running the above code causes the following run-time exception:

Let's see if the behavior of integral types is the same. Let's write the following code and run it.

First, please don't pull your hair who have understood what seems wrong here. For those who missed, look at the above code again and see if this is a case of overflow. Isn't it? But why the exception is not generated? The above is really a case of overflow but it runs fine but the following is displayed on the console:

Basically this is the default behavior. Integral overflows don't result in overflow exception. Now we are going to discuss how we can change the behavior so that the integral overflows also result in exceptions.

Compiler Option:
C# compiler support /checked compiler option [http://technet.microsoft.com/en-us/query/h25wtyxf]. If we compile our code with /checked+, it would cause overflow exception for any overflow involving integer types. /checked- is the default behavior where overflow exceptions are not generated. Don't worry, you wouldn't have to go back to using command prompt to compile your code using csc.exe directly. Visual Studio IDE supports setting this. Just open the property window of a C# project and go to Build tab. Clicking Advanced button should open the following window. Here, through the specified check box, we can control the runtime behavior of integral overflows. If checked, an exception is caused at runtime.

Setting this option would affect code generation in such a way that an Overflow exception is generated in case of overflows. Let's check the above checkbox and run the code again.

Updating the above setting modifies the build configuration. CheckForOverflowUnderflow is updated as true for the configuration.

If we want to update this behavior for release as well then we can update the release configuration in the same way. We can also create a new configuration using Configuration Manager based on an already existing build configuration. [http://msdn.microsoft.com/en-us/library/kwybya3w.aspx]

Checked / Unchecked Keyword:
We can even control the behavior for each statement or a sub-statement scope. C# also supports introducing a smaller checked context for a single statement or statement block. Let's un-check the "check for arithmetic overflow / underflow" from project settings and update the code as follows:

Here we have defined the arithmetic expression under checked context. In this scope, any integral arithmetic overflow would result in OverflowException.

The counter keyword is "unchecked". If the project is set to check for integral overflow then we can define a scope with unchecked integral overflow as follows:

When we execute the above code then there is no reported overflow exception and the control just moves to the next line of code as follows:

Please remember that checked keyword doesn't affect code generation of nested calls. Like in the following code:

When we run the above code, although the summation seems to be causing an overflow and it is enclosed in checked scope, still the generated code would not include overflow exception support for this case.

It must also be noted that checked / unchecked contexts are only applicable to ++,--, - (unary), +, -, *, / operators. It is also applicable to explicit numeric conversion. So other operations,e.g. Shift, would not cause Overflow exception even if they execute in checked context no matter shift is used for multiplication.

No comments: