Skip to content
Theory · Operators · lesson 9 of 10

Overflow

The dashboard of an old car has a mechanical odometer: six wheels with digits. After 999999 kilometers the wheels roll over, and the odometer shows 000000 again. It has no seventh digit, and there is nowhere to write a larger number.

The int type works in a similar way. It has a fixed number of digits, so it holds numbers only from int.MinValue to int.MaxValue—you already printed these limits in the theme on types. What happens if you add one to the largest value?

C#
int max = int.MaxValue;
Console.WriteLine(max); // 2147483647
Console.WriteLine(max + 1); // -2147483648
int min = int.MinValue;
Console.WriteLine(min - 1); // 2147483647

Let's go through the program line by line:

  1. max holds the largest int value, and the program prints 2147483647.
  2. max + 1 prints not 2147483648 but -2147483648: the count jumped to the smallest value.
  3. min - 1 jumps the other way, to the largest value.

Here the odometer picture parts ways with int. After its maximum the odometer shows zero, while int shows its smallest negative number. Half of the int values are negative, and past the top edge the count goes on from the bottom one.

Unchecked by default

When a result does not fit in its type, it is called an overflow. By default C# does not check integer operations for overflow: the extra binary digits are dropped, and the value silently jumps over the edge. There is no error and no exception, and the program goes on computing with a wrong number.

Interviewers like to ask this: what happens when you add one to int.MaxValue? The answer: at run time you get int.MinValue, with no error and no exception.

When the numbers go past two billion, use long: its maximum has 19 digits. But a long overflows in exactly the same way, only much later.

Checking with checked

A silent overflow is dangerous: the program produces a wrong result and shows no sign of it. To make C# check an operation, wrap it in checked(...). An overflow then throws an OverflowException, and the program stops at that line:

C#
int max = int.MaxValue;
Console.WriteLine(checked(max + 1)); // ✗
TEXT
Unhandled exception. System.OverflowException: Arithmetic operation resulted in an overflow.

You can also put checked before curly braces: checked { max++; } checks every integer operation inside them.

Overflow at compile time

When an expression consists only of literals and constants such as int.MaxValue, the compiler computes the result before the program runs and notices the overflow itself. This line does not compile:

C#
Console.WriteLine(int.MaxValue + 1); // ✗
TEXT
error CS0220: The operation overflows at compile time in checked mode

For such expressions the check is on by default—that is what the words checked mode in the message say. That is why the examples in this lesson put the maximum into the variable max first: the compiler does not compute a variable's value in advance.

Common mistake

The product of two int values is stored in a long, in the hope that the bigger type will hold the result:

C#
int million = 1000000;
long wrong = million * million; // ✗
Console.WriteLine(wrong); // -727379968
long right = (long)million * million;
Console.WriteLine(right); // 1000000000000

The compiler sees no error, and the program prints -727379968. Both operands of million * million are int, so the multiplication runs in int too: the result overflows before it reaches the long. Cast one of the operands to long: (long)million * million multiplies in long and gives 1000000000000.

Key points

  • int holds numbers from int.MinValue to int.MaxValue; past an edge the value silently jumps to the other edge.
  • checked turns an overflow into an OverflowException.
  • When an expression holds only literals and constants, the compiler finds the overflow, and such code does not compile.
  • The operands set the type of the result: int * int overflows before it reaches a long.