Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Arithmetic Overflow in .NET - Some Nitty Gritties

4.78/5 (15 votes)
8 May 2016CPOL8 min read 41.2K   119  
Let's get into few nitty gritties involved with integer data type and what can go wrong behind the scenes when its arithmetic limits are exceeded

Introduction

This article will take you through a small journey related to basics of arithmetic overflow and underflow related to integer and long data types of .NET Framework. Knowledge of these few key details related to arithmetic overflow and underflow can save you a great deal of frustration and silent failures. Let's get started.

Prerequisites

A basic knowledge of C# language and .NET Framework from Microsoft is desired while reading this article.

A Note for Attached Code

You will need the following software to run the attached code on your computer:

  1. Visual Studio 2012 or above

Background

The idea for this article struck my mind when I was solving a very trivial problem given by a very good programmer friend of mine at my office. The problem was to print the successor of highest number which can be contained by integer (alias Int32) data type in .NET Framework. What followed through were a series of surprises.

Basic Fundamentals

We will be dealing with only Integer data type in this article, but it is equally applicable to long data type as well without any deviation.

Note: Throughout my article, I've used int, Integer and Int32 interchangeably. int is a keyword facility provided by C# compiler to integer data type as it is a primitive data type. So writing int, Int32 and System.Int32 in code is equivalent. For System.Int64 data type, the keyword is long.

Let's quickly answer few basic questions which might be popping up in your mind right now:

What is an integer data type? Int32 or int or System.Int32 is a data type supported by .NET Framework to deal with computation of numbers. It can only contain integral values like 01, 2, -1,-2,-3. No fractional numbers can be stored in a variable of integer data type. Even if you attempt to do so, the fractional part will get automatically truncated. Internally, it has memory storage size of 32 bits as is evident from its name Int32. The highest possible value which a variable of type Int32 can contain is 2147483647 and the lowest possible value is -2147483648.

What is arithmetic overflow? If you try assigning an integral value to a variable of type integer which is outside the range which spans between -2147483648 and 2147483647, it results in an arithmetic overflow error. Whenever the value being assigned to an integer variable is greater than 2147483647 or is less than -2147483648, then the resulting value along with its sign (+/-) can not be accomodated in the 32 bit memory space assigned to the variable which results in memory overflow.

.NET Framework class library has a built-in exception class named System.ArithmeticException to catch this overflow condition arising in code.

Here is a code snippet from the attached source code which generates ArithemticException. Here, the exception gets raised because of arithmetic overflow happening towards negative limit of Int32 data type.

Code Snippet # 1

C#
        //Trivial code has been removed for brevity and clarity.
        //Please download attached code for complete reference.        
        private static void CreateArithmeticOverflowForInteger()
        {
            try
            {
                var myNumber = int.MinValue;
                var predecessorNumber = myNumber - 1;
            }
            catch (ArithmeticException ex)
            {
                Console.WriteLine(ex.ToString());
            }

        }

Using the Code

Getting back to my original problem statement of printing the successor of highest number which can be contained by integer data type, here is the program I wrote:

Code Snippet # 2

C#
//Trivial code has been removed for brevity and clarity.
//Please download attached code for complete reference.

 static void Main(string[] args)
 {
     PrintSuccessor(int.MaxValue);
 }

 private static void PrintSuccessor(int number)
 {
     Console.WriteLine(number + 1);
 }

Here is what got printed on console: -2147483648

Highest value which can be stored by Int32 data type is 2147483647. The number which got printed here is -2147483648. What's going on in here? There is something wrong for sure as mathematically successor of 2147483647 (the highest value which can be stored in Int32 data type) is 2147483648. Also, if you noticed this value which got printed on console is actually the lowest possible integer which can be stored in Int32 data type. Not to worry, all this is happening because of a C# project setting which is not enabled by default and results in this aberration. Let's check that.

Project Setting to Escape Arithmetic Exceptions

Open solution explorer. Right click on the project file "ArithmeticCalculationsNumericDataTypes". Click properties in context menu. This will open the project properties window. You can also open this window by simply pressing alt + Enter after selecting project file in solution explorer. Now go to build tab in project properties window. Click Advanced button. Verify the value of "Check for arithmetic overflow/underflow" check box. Currently, it will be in unchecked state as shown below:

C# Project Settings for arithmetic overflow

Now this particular setting being unchecked is the root cause of the aberrant behavior you are observing currently. This project setting instructs the compiler to generate MSIL which will ignore the arithmetic overflow exception. The moment you increase the value of an integer variable which goes past the positive limit of Int32 data type, the value does a roll-over and gets set to negative limit of Int32 data type which is -2147483648. No run time exceptions will be raised at all. Just a silent failure which no one can notice. :) I sent this source code to my friend only to face embarrassment as it was producing wrong result.

An irritating limitation: Once set in project properties, I've no way to change this behavior of arithmetic overflow at run time. If this setting is checked in project properties, it will always throw exception when arithmetic overflow occurs. If this setting is unchecked in project properties, it will always eat the exception when arithmetic overflow occurs and silently roll over to lower limit or upper limit of the data type as the case may be. What is the way out? C# compiler designers are smart people. :) Let's see what they have in store for us.

Checked and Unchecked Programming Constructs

To modify/control the behavior of arithmetic overflow at run time, C# provides two programming constructs namely checked and unchecked keywords. Let's see them in action. Let's say at run time, you decide to enable arithmetic overflow exception. Here is a quick snippet which shows how it works:

C#
//Trivial code has been removed for brevity and clarity.
//Please download attached code for complete reference.
private static void CheckedProgrammingConstruct()
{
    checked
    {
        var myNumber = int.MaxValue;
        //Exception will be raised here as we are inside checked block
        var successorNumber = myNumber + 1;
    }
}

So you can see clearly that in the last line, arithmetic overflow will happen. It will result in arithmetic exception as it is crossing the upper limit of Int32 data type irrespective of the state of the arithmetic overflow setting in your project properties since this code block is enclosed inside checked block.

Let us now see unchecked programming construct:

C#
//Trivial code has been removed for brevity and clarity.
//Please download attached code for complete reference.
private static void UncheckedProgrammingConstruct()
{
    unchecked
    {
        var myNumber = int.MaxValue;
        //Exception will NOT be raised here as we are inside unchecked block
        var successorNumber = myNumber + 1;
    }
}

So as you can see again in the last line of code, there is a likelihood of arithmetic overflow, but it will NOT result in any kind of arithmetic exception even if it is crossing the upper limit of Int32 data type. It will silently jump to the negative limit of Int32 data type irrespective of the state of the arithmetic overflow setting in your project properties since this code block is enclosed inside unchecked block.

Who Solves My Successor Problem?

The only way to get the right result is to use the Int64 (long) data type instead of Int32 (int) data type. Here is the code which solves my friend's original problem:

C#
//Trivial code has been removed for brevity and clarity.
//Please download attached code for complete reference.

static void Main(string[] args)
{
    PrintSuccessorWithoutError(int.MaxValue);
}

private static void PrintSuccessorWithoutError(long number)
{
    Console.WriteLine(number + 1);
}

Do FCL Classes Respect Your Project Settings?

This is the MOST important take away for you which is why I wrote this article as this finding had left me in a bit of amazement and gave me food for thought. Let's say you have kept the "Check for arithmetic overflow/underflow" setting unchecked in your project properties. Now how the following code snippet will behave. List<int> is a framework class library (FCL) class which comes with .NET Framework. Here, we store two integers in a list and then ask the Sum API of List<int> class to sum them up. I've chosen two numbers in such a way that the summation process will result in an arithmetic overflow.

C#
//Trivial code has been removed for brevity and clarity.
//Please download attached code for complete reference.
private static void BehaviorOfFclClasses()
{
    var listOfNumbers = new List<int>();
    listOfNumbers.Add(Int32.MaxValue);
    listOfNumbers.Add(1);
    //This sum operation is done by the FCL class internally and returned back to you.
    var totalSumOfListItems = listOfNumbers.Sum();
}

This code results in arithmetic overflow exception irrespective of arithmetic overflow settings in project properties. This really goes in programmer's favor. The thing is it can get really chaotic if framework class libraries start eating arithmetic overflow exception under the carpet based on your project settings. If it starts happening, then they will return results like sum in this case which will be incorrect without throwing exception. So FCL classes always use checked keyword in their implementation wherever an arithmetic operation in involved. So don't feel dumbfounded next time if you see FCL classes not respecting your project settings for arithmetic overflow. :) They are doing it in your best interest.

So you are all set to dive into the world of arithmetic operations.

Recommendations

  1. Always follow the default project setting for arithmetic flow (Unchecked state). Enabling arithmetic overflow checks across the application in one shot using the project setting will have negative impact on performance. If you have specific cases where you feel you have to enable it, then do it through checked keyword in specific piece of code only at run time.

Points of Interest

  • I've an Int32 variable. Let's say I assign it the lowest possible value of Int32 data type. Now you have to think what arithmetic operation on the variable will cause the lower limit of Int32 data type to jump to upper limit of Int32 data type if the project setting I mentioned in this article is unchecked.
  • How about putting an FCL class API which does arithmetic calculation enclosed inside unchecked keyword. Observe its behavior. Will it throw an arithmetic exception or eat it silently?
C#
 unchecked

{
     var listOfNumbers = new List<int>();
     listOfNumbers.Add(Int32.MaxValue);
     listOfNumbers.Add(1);
     //This sum operation is done by the FCL class internally and returned back to you.
     var totalSumOfListItems = listOfNumbers.Sum();
 }

History

  • May 5, 2016 - First release
  • May 9, 2016 - Updated the article to remove the concept of underflow. The concept of underflow is not related to integer data type. Integers always overflow both on their positive and negative limits.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)