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#.
| Size | Signed | Unsigned | ||
|---|---|---|---|---|
| 8-bit | sbyteSystem.SByte | min -128 max 127 | byteSystem.Byte | min 0 max 255 |
| 16-bit | shortSystem.Int16 | min -32, max 32, | ushortSystem.UInt16 | min 0 max 65, |
| 32-bit | intSystem.Int32 | min -2, max 2, | uintSystem.UInt32 | min 0 max 4, |
| 64-bit | longSystem.Int64 | min -9, max 9, | ulongSystem.UInt64 | min 0 max 18, |
| native | nintSystem.IntPtr | 32 or 64 bits, depends on the platform | nuintSystem.UIntPtr | 32 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.
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.
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.
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.
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.
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.
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.
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
In this article we have worked with integer types in C#.
Author
List all C# tutorials.