ZetCode

C# integer types

last modified October 16, 2026

In this article we work with integer types in C#.

Integers are whole numbers without a fractional part, such as -2, 0, or 42. They are used for counting, indexing, identifiers, and many other purposes. C# has several integral numeric types that differ in their size and in whether they can hold negative values.

C# integral types

The following table lists the integral types of C#.

SizeSignedUnsigned
8-bitsbyteSystem.SBytemin -128
max 127
byteSystem.Bytemin 0
max 255
16-bitshortSystem.Int16min -32,768
max 32,767
ushortSystem.UInt16min 0
max 65,535
32-bitintSystem.Int32min -2,147,483,648
max 2,147,483,647
uintSystem.UInt32min 0
max 4,294,967,295
64-bitlongSystem.Int64min -9,223,372,036,854,775,808
max 9,223,372,036,854,775,807
ulongSystem.UInt64min 0
max 18,446,744,073,709,551,615
nativenintSystem.IntPtr32 or 64 bits,
depends on the platform
nuintSystem.UIntPtr32 or 64 bits,
depends on the platform

The int type is the default choice for integers. We use long when the values may exceed about two billion. The unsigned types and the small types are used mostly for interoperability, binary data, and memory-sensitive code. The native-sized nint and nuint types are used in low-level code.

For even larger values, .NET provides the 128-bit Int128 and UInt128 types and the arbitrary-precision BigInteger type.

C# integer ranges

Each integral type has the MinValue and MaxValue constants.

Program.cs
Console.WriteLine($"sbyte: {sbyte.MinValue} to {sbyte.MaxValue}");
Console.WriteLine($"byte: {byte.MinValue} to {byte.MaxValue}");
Console.WriteLine($"short: {short.MinValue} to {short.MaxValue}");
Console.WriteLine($"int: {int.MinValue} to {int.MaxValue}");
Console.WriteLine($"long: {long.MinValue} to {long.MaxValue}");
Console.WriteLine($"ulong: {ulong.MinValue} to {ulong.MaxValue}");
Console.WriteLine($"Int128: {Int128.MaxValue}");

Console.WriteLine($"nint size: {nint.Size} bytes");
Console.WriteLine($"int size: {sizeof(int)} bytes");

We print the ranges of some integral types and the sizes of nint and int.

Console.WriteLine($"nint size: {nint.Size} bytes");

On a 64-bit platform, the nint type has 8 bytes.

$ dotnet run
sbyte: -128 to 127
byte: 0 to 255
short: -32768 to 32767
int: -2147483648 to 2147483647
long: -9223372036854775808 to 9223372036854775807
ulong: 0 to 18446744073709551615
Int128: 170141183460469231731687303715884105727
nint size: 8 bytes
int size: 4 bytes

C# integer literals

Integer literals can be written in decimal, hexadecimal, or binary notation. The underscore character can be used as a digit separator to improve readability.

Program.cs
int a = 1_000_000;
int b = 0xFF;
int c = 0b_1010_1010;

var d = 42;
var e = 42u;
var f = 42L;
var g = 42UL;
var h = 3_000_000_000;

Console.WriteLine($"{a} {b} {c}");
Console.WriteLine($"{d.GetType()} {e.GetType()} {f.GetType()} {g.GetType()}");
Console.WriteLine(h.GetType());

The 0x prefix denotes a hexadecimal literal and the 0b prefix a binary literal.

var e = 42u;
var f = 42L;
var g = 42UL;

The u, L, and UL suffixes produce uint, long, and ulong literals. The lowercase l suffix is allowed, but it is easily confused with the digit 1.

var h = 3_000_000_000;

A literal without a suffix has the first of the int, uint, long, and ulong types in which its value fits. This value is too big for int, so it is a uint.

$ dotnet run
1000000 255 170
System.Int32 System.UInt32 System.Int64 System.UInt64
System.UInt32

C# integer division and remainder

When both operands are integers, the / operator performs integer division; the fractional part of the result is discarded. The % operator returns the remainder.

Program.cs
Console.WriteLine(7 / 2);
Console.WriteLine(-7 / 2);
Console.WriteLine(7 % 3);
Console.WriteLine(-7 % 3);

Console.WriteLine(7 / 2.0);
Console.WriteLine((double) 7 / 2);

var (q, r) = int.DivRem(17, 5);
Console.WriteLine($"17 = 5 * {q} + {r}");

int m = ((-7 % 3) + 3) % 3;
Console.WriteLine(m);

The example demonstrates integer division and the remainder operator.

Console.WriteLine(-7 / 2);

Integer division truncates towards zero; therefore, the result is -3, not -4.

Console.WriteLine(-7 % 3);

The result of the remainder operator has the sign of the dividend.

Console.WriteLine((double) 7 / 2);

To get a floating point result, at least one of the operands must be a floating point value. Note that (double) (7 / 2) would produce 3, because the division is done before the cast.

var (q, r) = int.DivRem(17, 5);

The DivRem method returns both the quotient and the remainder as a tuple.

int m = ((-7 % 3) + 3) % 3;

This is a common way to compute a mathematical modulo, which is never negative for a positive divisor.

$ dotnet run
3
-3
1
-1
3.5
3.5
17 = 5 * 3 + 2
2

Integer division by zero throws a DivideByZeroException.

C# integer overflow

When the result of an arithmetic operation does not fit into the type, an overflow occurs. By default, C# does not check for overflow; the result silently wraps around.

Program.cs
int x = int.MaxValue;
x++;
Console.WriteLine(x);

byte b = 250;
b += 10;
Console.WriteLine(b);

int big = 50_000;
long wrong = big * big;
long right = (long) big * big;

Console.WriteLine(wrong);
Console.WriteLine(right);

In all three cases, the result does not fit into the type of the operation.

int x = int.MaxValue;
x++;

Incrementing the largest int value gives the smallest one.

long wrong = big * big;
long right = (long) big * big;

A common bug: the multiplication is done with int values and only the wrapped result is converted to long. To get the correct result, we must convert one of the operands to long before the multiplication.

$ dotnet run
-2147483648
4
-1794967296
2500000000

C# checked keyword

In a checked context, an integer overflow throws an OverflowException.

Program.cs
int x = int.MaxValue;

try
{
    int y = checked(x + 1);
    Console.WriteLine(y);
}
catch (OverflowException e)
{
    Console.WriteLine(e.Message);
}

checked
{
    try
    {
        int total = 0;

        foreach (var val in new[] { 2_000_000_000, 2_000_000_000 })
        {
            total += val;
        }
    }
    catch (OverflowException)
    {
        Console.WriteLine("The total is too big");
    }
}

Console.WriteLine(unchecked(x + 1));

We use a checked expression and a checked block.

int y = checked(x + 1);

The checked expression checks the overflow in a single expression.

Console.WriteLine(unchecked(x + 1));

The unchecked keyword explicitly disables the overflow check.

$ dotnet run
Arithmetic operation resulted in an overflow.
The total is too big
-2147483648

The overflow checking can be enabled for the whole project with the CheckForOverflowUnderflow property in the project file.

<PropertyGroup>
  <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
</PropertyGroup>

C# integer conversions

A conversion to a type with a larger range is implicit. A conversion to a type with a smaller range requires an explicit cast, because data may be lost.

Program.cs
int a = 300;
long b = a;

byte c = (byte) a;

int big = 70_000;
short d = (short) big;

double e = 7.9;
int f = (int) e;
int g = (int) Math.Round(e);

Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(d);
Console.WriteLine(f);
Console.WriteLine(g);

try
{
    byte h = Convert.ToByte(a);
    Console.WriteLine(h);
}
catch (OverflowException e2)
{
    Console.WriteLine(e2.Message);
}

We convert values between integral types and between floating point and integral types.

byte c = (byte) a;

The value 300 does not fit into a byte. The cast keeps only the lowest eight bits; the result is 300 - 256 = 44.

The same happens with 70,000, which does not fit into a short; the result is 70,000 - 65,536 = 4,464. Note that a cast of a constant, such as (short) 70_000, is reported by the compiler as the CS0221 error, because the compiler can see that the value does not fit.

int f = (int) e;

Casting a floating point value to an integer truncates the fractional part. To round the value, we use Math.Round.

byte h = Convert.ToByte(a);

Unlike a cast, the methods of the Convert class check the range and throw an OverflowException.

$ dotnet run
300
44
4464
7
8
Value was either too large or too small for an unsigned byte.

C# parsing integers

The int.Parse method converts a string to an integer and throws an exception for invalid input. The int.TryParse method returns a boolean value instead.

Program.cs
string[] vals = ["42", "-17", "3.5", "abc", "99999999999", " 12 "];

foreach (var val in vals)
{
    if (int.TryParse(val, out int n))
    {
        Console.WriteLine($"'{val}' -> {n}");
    }
    else
    {
        Console.WriteLine($"'{val}' -> invalid");
    }
}

We try to parse several strings.

$ dotnet run
'42' -> 42
'-17' -> -17
'3.5' -> invalid
'abc' -> invalid
'99999999999' -> invalid
' 12 ' -> 12

Floating point numbers and values out of range are rejected. Leading and trailing white space is allowed.

Source

Integral numeric types

In this article we have worked with integer types in C#.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.