Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C# & .NET (C# 14, .NET 10 LTS)  Module 2 – Types, variables and operators

Integers, overflow and checked arithmetic

Pick the C# integer type that fits your range, see int.MaxValue + 1 wrap to a negative number, and stop it with checked arithmetic before data goes wrong.

  • Beginner
  • 15 minutes
  • Examples run with .NET 10.0.401 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 6 of 15 minutes free
Free: Each integer type holds a fixed range of whole numbers · A result that does not fit wraps around, unless the code is checked. With a pass: 2 in-depth parts and 3 interview answers, about 9 minutes.

What you will learn

  • Pick the integer type, from byte to Int128, whose range fits the values a program stores
  • Predict the wrapped result of an overflowing operation and catch it with checked arithmetic
  • Decide where a checked context applies, for an expression, a block or a whole project
  • Use integer division, the remainder and the shift operators correctly on negative numbers
  • Add money as a long count of paise without a silent overflow

Before you start

What you’ll learn · Before you start

What you will learn

  • Pick the integer type, from byte to Int128, whose range fits the values a program stores
  • Predict the wrapped result of an overflowing operation and catch it with checked arithmetic
  • Decide where a checked context applies, for an expression, a block or a whole project
  • Use integer division, the remainder and the shift operators correctly on negative numbers
  • Add money as a long count of paise without a silent overflow

Before you start

On this page

In C#, int.MaxValue + 1 is -2,147,483,648: the sum wraps around to the smallest int, and the program carries on without an error. Thirty orders of 1,000,000.00 each, counted in hundredths in an int, add up to -1,294,967,296 the same way.

C# integers come in fixed sizes, from the 8 bits of a byte to the 128 bits of Int128, and each has a smallest and a largest value. You will pick a type from the range its values reach, watch what happens one step past the edge, and turn that silent wraparound into an exception with checked arithmetic.

Each integer type holds a fixed range of whole numbers

An integer type stores a whole number in a fixed number of bits, and the bits decide its range. This program prints the smallest and largest value of each type, then the type that the compiler gives to some literals:

ranges.cs: the integer types and some literals C# · ranges/ranges.cs
using System.Globalization;
using System.Runtime.CompilerServices;

// Every number prints with the invariant culture, the same on every computer.
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

Console.WriteLine("Type    Bits  Smallest to largest");
Row("sbyte", 8, sbyte.MinValue, sbyte.MaxValue);
Row("byte", 8, byte.MinValue, byte.MaxValue);
Row("short", 16, short.MinValue, short.MaxValue);
Row("ushort", 16, ushort.MinValue, ushort.MaxValue);
Row("int", 32, int.MinValue, int.MaxValue);
Row("uint", 32, uint.MinValue, uint.MaxValue);
Row("long", 64, long.MinValue, long.MaxValue);
Row("ulong", 64, ulong.MinValue, ulong.MaxValue);
Row("nint", nint.Size * 8, nint.MinValue, nint.MaxValue);
Console.WriteLine($"Int128 has 128 bits; its largest value is {Int128.MaxValue:N0}");

Console.WriteLine();
Console.WriteLine("Literal          Value          Type");
Literal(0x7FFF_FFFF);
Literal(0b1111_1111);
Literal(1_00_000);
Literal(3_000_000_000);
Literal(3_000_000_000L);
Literal(100U);
Literal(100UL);

static void Row<T>(string name, int bits, T min, T max)
    where T : IFormattable =>
    Console.WriteLine($"{name,-7} {bits,4}  {min:N0} to {max:N0}");

// CallerArgumentExpression hands the method the literal exactly as it is written.
static void Literal<T>(T value, [CallerArgumentExpression(nameof(value))] string text = "") =>
    Console.WriteLine($"{text,-16} {value,-14} {typeof(T).Name}");

Output

Type    Bits  Smallest to largest
sbyte      8  -128 to 127
byte       8  0 to 255
short     16  -32,768 to 32,767
ushort    16  0 to 65,535
int       32  -2,147,483,648 to 2,147,483,647
uint      32  0 to 4,294,967,295
long      64  -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
ulong     64  0 to 18,446,744,073,709,551,615
nint      64  -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
Int128 has 128 bits; its largest value is 170,141,183,460,469,231,731,687,303,715,884,105,727

Literal          Value          Type
0x7FFF_FFFF      2147483647     Int32
0b1111_1111      255            Int32
1_00_000         100000         Int32
3_000_000_000    3000000000     UInt32
3_000_000_000L   3000000000     Int64
100U             100            UInt32
100UL            100            UInt64

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run ranges.cs

The signed types, sbyte, short, int and long, spend one bit on the sign, so int stops at 2,147,483,647 while uint, with no sign, reaches 4,294,967,295. nint has the size of a pointer in the process that runs the program, 64 bits here. A literal can be hexadecimal (0x) or binary (0b), an underscore is only a separator for the reader, so 1_00_000 is 100,000, and a suffix picks the type: L for long, U for uint, UL for ulong.

Choose the type by the largest value the program can ever store:

  • int for counts and indexes, the everyday choice: Microsoft’s conventions prefer it to the unsigned types.
  • long when values can pass 2.1 billion, such as file sizes in bytes or money counted in its smallest unit.
  • byte for the raw bytes of files and network messages.
  • Int128, new in .NET 7, only for values past long’s 9.2 × 10¹⁸.

Checkpoint

Check: Integer types and their ranges

  1. One question A counter must reach 5,000,000,000. Which is the smallest of these types that is sure to hold it?

    Choose one answer.

    Show the answer: One question

    Answer: long

    Pick the type from the largest value the program can ever store. ranges.cs printed the limits: uint ends at 4,294,967,295 and long at 9,223,372,036,854,775,807.

A result that does not fit wraps around, unless the code is checked

When a result is too big for its type, C# keeps the low bits that fit and drops the rest. Step through 127 + 1 in an sbyte, whose 8 bits make the arithmetic easy to follow:

Interactive · stage by stage What happens to the bits when 127 + 1 is stored in an sbyte Step through one addition in 8 bits. The arrow aside is the path a checked context takes.
What happens to the bits when 127 + 1 is stored in an sbyte, step 5 of 5. Showing sbyte 127, Add 1, Bits 1000 0000, Read back: -128, OverflowException. In focus: OverflowException.in a checked contextsbyte 127bits 0111 1111Add 1the carry runs to the leftBits 1000 0000the top bit is the signRead back: -128the smallest sbyteOverflowExceptionnothing is stored128 does not fit

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The largest sbyte. An sbyte has 8 bits, and the top one is the sign bit. 127, the largest sbyte, is a 0 for the sign followed by seven 1s.
  2. Add 1. Adding 1 turns each of the seven 1s into a 0 and carries 1 to the left, the way 999 + 1 carries in decimal, until the carry reaches the sign bit.
  3. The carry flips the sign. The result is 1000 0000. Unchecked arithmetic keeps whatever 8 bits come out, so the carry lands in the sign bit and no error is raised.
  4. Read back as -128. Read as a signed number, 1000 0000 is -128: the sum has wrapped around to the smallest sbyte. wrap.cs printed exactly that, and an int does the same at 2,147,483,647.
  5. In a checked context. In a checked context the runtime tests whether the true sum, 128, fits in an sbyte. It does not, so it throws OverflowException and the variable keeps its old value.

Signed integers use two's complement: The way .NET stores signed integers in binary: the top bit counts as minus 2 to the power of the bits below it, so 1000 0000 in an sbyte is -128, and one past the largest value reads back as the smallest., which is why one past the largest value reads back as the smallest. An int does the same at its edge:

wrap.cs: one step past the edge of four types C# · wrap/wrap.cs
using System.Globalization;

// Every number prints with the invariant culture, the same on every computer.
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

// One past the largest value of a type wraps around to its smallest, with no error.
int max = int.MaxValue;
int next = max + 1;
Console.WriteLine($"int.MaxValue + 1 = {next:N0}");

sbyte small = 127;
small++;
Console.WriteLine($"127 in an sbyte, plus 1 = {small}");

byte full = 255;
full++;
Console.WriteLine($"255 in a byte, plus 1 = {full}");

uint none = 0;
none--;
Console.WriteLine($"0 in a uint, minus 1 = {none:N0}");

// 30 orders of 1,000,000.00 each, counted in hundredths (cents or paise).
int order = 100_000_000;
int totalInt = 0;
long totalLong = 0;
for (int i = 0; i < 30; i++)
{
    totalInt += order;
    totalLong += order;
}
Console.WriteLine($"30 orders, int total:  {totalInt:N0}");
Console.WriteLine($"30 orders, long total: {totalLong:N0}");

// checked turns the same overflow into an exception.
try
{
    int checkedNext = checked(max + 1);
    Console.WriteLine(checkedNext);
}
catch (OverflowException e)
{
    Console.WriteLine($"checked(max + 1): {e.GetType().Name}: {e.Message}");
}

Output

int.MaxValue + 1 = -2,147,483,648
127 in an sbyte, plus 1 = -128
255 in a byte, plus 1 = 0
0 in a uint, minus 1 = 4,294,967,295
30 orders, int total:  -1,294,967,296
30 orders, long total: 3,000,000,000
checked(max + 1): OverflowException: Arithmetic operation resulted in an overflow.

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run wrap.cs

The order totals are the realistic case: 30 orders of 100,000,000 hundredths make 3,000,000,000, which no int can hold, so the int total came out negative while the long total is right. Microsoft’s documentation describes the same edge in Environment.TickCount, the milliseconds since the computer started: it climbs for about 24.9 days to int.MaxValue, then jumps to int.MinValue.

This silent wrap is integer overflow: A result too large or too small for its integer type. By default C# keeps the low bits that fit, so the value wraps around without an error; in a checked context the operation throws OverflowException instead., and it is the default, because integer arithmetic in C# is unchecked unless you ask otherwise. Checked arithmetic is the other choice: inside checked(…) the same addition threw OverflowException, as the last line shows, so a wrong total stops the program instead of reaching a report. Four tools decide what happens at the edge of a type:

  • checked, as a block or an expression, turns integer overflow inside it into an OverflowException.
  • unchecked asks for wrapping on purpose, for example in a hash function.
  • The project setting CheckForOverflowUnderflow makes checked arithmetic the default for every line of a project.
  • A wider type, such as long for int, moves the edge beyond any value the program will meet.

Integer division and the remainder have edges of their own, with negative numbers and with zero.

Programmer Calculator (Bitwise) Choose signed 32-bit, type 2147483647 + 1 and see the result wrap to -2,147,483,648.

Key idea

Integer arithmetic in C# wraps around without a word when a result does not fit, so give each value a type wide enough for it, and use checked arithmetic wherever a wrapped result would be wrong.

Checkpoint

Check: Wraparound and checked arithmetic

  1. Question 1 of 2 In the stepped figure, what does 127 + 1 read back as when it is stored in an sbyte?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: -128

    Unchecked arithmetic keeps the bits that fit. For a signed type, the carry out of the largest value flips the sign bit, so the result reads as the smallest value: -128 for sbyte, -2,147,483,648 for int.

  2. Question 2 of 2 int max = int.MaxValue; int next = checked(max + 1); What happens on the second line?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It throws OverflowException when the line runs

    checked turns the silent wrap into an exception at run time, on the line whose result does not fit, so a wrong value never reaches the next line.

Checked and unchecked contexts, reproduced in code Pro: opens with a Pro, Premium or Ultimate pass

A checked context covers less code than many people expect, and conversions follow rules of their own. The runs below map its edges line by line, switch checking on for a whole project and show the instruction it compiles to.

In this part:

  • What a checked context covers, and what it leaves out
  • Turning checking on for a whole project, with a session
  • What checked changes in the compiled code

4 runnable examples (C#, Python) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Division, remainders, shifts and money in long, with the numbers Pro: opens with a Pro, Premium or Ultimate pass

Integer division, the remainder and the shifts follow simple rules that turn into bugs once a number is negative. The runs below reproduce three such bugs, then add money as whole paise with a sum that cannot wrap.

In this part:

  • Which way integer division rounds, and the sign of a remainder
  • Right shifts of a negative number, and shift counts past 31
  • Money as a long count of paise, with a sum that names the item that overflowed

3 runnable examples (C#) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise: totals that cannot wrap

Make the order total stop instead of wrapping

Copy wrap.cs to your computer, put the loop inside checked { … } and make Console.WriteLine(i + 1); the loop’s first line. Before you run it, work out on which order the int total throws, then run it with dotnet run wrap.cs: the last number it prints is that order.

Then try the questions on ranges and wraparound, with the idea you are less sure of first:

Practice that adapts to you

Practise this lesson

9 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 9 What is int.MaxValue?

    Choose one answer.

    Show the answer: Question 1 of 9

    Answer: 2,147,483,647

    ranges.cs printed every type's limits; int, the everyday integer, ends at 2,147,483,647.

  2. Question 2 of 9 Why does uint reach 4,294,967,295 while int stops at 2,147,483,647, with 32 bits each?

    Choose one answer.

    Show the answer: Question 2 of 9

    Answer: int spends one bit on the sign, so half its values are negative

    Signed and unsigned types of one size have the same number of values. The signed one splits them around zero: -2,147,483,648 to 2,147,483,647.

  3. Question 3 of 9 What does the literal 1_00_000 stand for?

    Choose one answer.

    Show the answer: Question 3 of 9

    Answer: The int 100000

    Digit separators let you group digits as you read them, thousands or lakhs. They change neither the value nor the type.

  4. Question 4 of 9 A service counts milliseconds since it started and runs for months. Which type should hold the count?

    Choose one answer.

    Show the answer: Question 4 of 9

    Answer: long

    Size a counter for the longest run it can have. Microsoft's documentation of Environment.TickCount describes the jump to int.MinValue after about 24.9 days; Environment.TickCount64 exists for that reason.

  5. Question 5 of 9 byte full = 255; full++; What does full hold now?

    Choose one answer.

    Show the answer: Question 5 of 9

    Answer: 0

    Unchecked arithmetic keeps the low bits: 256 is 1 0000 0000 in binary, and its low 8 bits are all zeros.

  6. Question 6 of 9 In a new C# project, with no checked keyword anywhere, what happens when an int addition overflows?

    Choose one answer.

    Show the answer: Question 6 of 9

    Answer: The result wraps around and the program goes on

    Integer arithmetic is unchecked unless the code or the project asks otherwise, so the program goes on with the wrapped value. Ask for checked arithmetic where a wrong number would do harm.

  7. Question 7 of 9 wrap.cs adds orders of 100,000,000 hundredths to an int. If the loop runs checked, on which order (1 to 30) does it throw?

    Type a number.

    A hint

    Which multiple of 100,000,000 is the first above 2,147,483,647?

    Show the answer: Question 7 of 9

    Answer: 22 order

    After 21 orders the total is 2,100,000,000, still below 2,147,483,647. The 22nd order would make 2,200,000,000, which does not fit, so checked throws OverflowException there.

  8. Question 8 of 9 Which of these makes checked arithmetic the default for every line of a project?

    Choose one answer.

    Show the answer: Question 8 of 9

    Answer: <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> in the project file

    Four tools decide what happens at the edge: checked, unchecked, the project setting and a wider type. Only the setting covers a whole project.

  9. Question 9 of 9 A daily sales total in paise, kept in an int, went negative. Which change fixes it for good?

    Choose one answer.

    Show the answer: Question 9 of 9

    Answer: Keep the total in a long and add with checked arithmetic

    Fix the range first, with a type wide enough for every value, then make any overflow loud with checked.

Interview questions

Fresher · What happens when an int overflows in C#?

By default nothing visible: integer arithmetic in C# is unchecked, so the result keeps only the low 32 bits and wraps around. int.MaxValue + 1 gives int.MinValue, -2,147,483,648, without an exception, and 30 orders of 100,000,000 hundredths summed in an int come out negative. In a checked context, a checked block or expression, or a project built with CheckForOverflowUnderflow, the same operation throws OverflowException. The defence is a type wide enough for every value, such as long for money in paise, plus checked arithmetic where a wrapped value would be wrong.

  1. Fresher to mid

    A nightly job adds up a merchant's sales in paise in an int, and one morning the report shows a negative total for the day. Explain what happened, how you would find which days are wrong, and how you would fix the code and the stored totals.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    After an overflow bug reached production, your team proposes turning on CheckForOverflowUnderflow for the whole solution. What exactly would it change, what can break or slow down, and where would you still write unchecked?

    Model answer, Pro. First step and follow-ups

  3. Fresher to mid

    A hand-written hash table computes bucket = key.GetHashCode() % buckets.Length and now and then crashes with IndexOutOfRangeException. Explain why it happens only sometimes, and write the bucket calculation correctly.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Each C# integer type has a fixed size and range: int stops at 2,147,483,647, long at about 9.2 × 10¹⁸.
  • A literal’s value and suffix decide its type, and an underscore in a literal only helps the reader.
  • By default a result that does not fit wraps around without an error, as int.MaxValue + 1 became int.MinValue.
  • Checked arithmetic, a checked block or expression, turns that overflow into an OverflowException.

Terms in this lesson

integer overflow
A result too large or too small for its integer type. By default C# keeps the low bits that fit, so the value wraps around without an error; in a checked context the operation throws OverflowException instead.
two's complement
The way .NET stores signed integers in binary: the top bit counts as minus 2 to the power of the bits below it, so 1000 0000 in an sbyte is -128, and one past the largest value reads back as the smallest.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.