In this article
- Overview
- Numeric Types
- Numeric Literals
- Numerical literal type inference
- Numeric Suffixes
- Numeric Conversion
- Arithmetic Operators
- Increment and Decrement Operators
Overview
C# has predefined numeric types
- Integral (signed & unsigned)
Numeric Types
- Of the integral types, int and long are first-class citizens and are favored by both C# and the runtime.
- The other integral types are typically used for interoperability or when space efficiency is paramount.
- The
nint
andnuint
native-sized integer types (introduced in C# 9) are most useful in helping with pointer arithmetic
Integral Types
Signed
C# type | System type | Suffix | Size | Range |
---|---|---|---|---|
sbyte | Sbyte | 8 bits | ||
short | Int16 | 16 bits | ||
int | Int32 | 32 bits | ||
long | Int64 | L | 64 bits | |
nint | IntPtr | 32/64 bits |
1 bit is reserved for the sign
Unsigned
C# type | System type | Suffix | Size | Range |
---|---|---|---|---|
byte | Byte | 8 bits | ||
ushort | UInt16 | 16 bits | ||
uint | UInt32 | U | 32 bits | |
ulong | UInt64 | UL | 64 bits | |
unint | UIntPtr | 32/64 bits |
Real Types
C# type | System type | Suffix | Size | Range |
---|---|---|---|---|
float | Single | F | 32 bits | ± |
double | Double | D | 64 bits | ± |
decimal | Decimal | M | 128 bits | ± |
float
anddouble
are called floating-point types and are typically used for scientific and graphical calculations.- The
decimal
type is typically used for financial calculations, for which base-10-accurate arithmetic and high precision are required.
From .NET 5, there is a 16-bit floating point type called
Half
. This is intended mainly for interoperating with graphics card processors and does not have native support in most CPUs. Half is not a primitive CLR type and does not have special language support in C#.
Numeric Literals
Integral Literals
- Integral-type literals can use decimal or hexadecimal (prefixed with
0x
) notation.
Example:
int x = 127;
long y = 0x7F;
- underscore
_
anywhere within a numeric literal is allowed for readability sake, For example:
int million = 1_000_000;
- You can specify the number in binary with
0b
prefix. For example:
int oneThousand = 0b0011_1110_1000; // 1000 in decimal
Summary
system | prefix | Example |
---|---|---|
Decimal | 127 | |
Binary | 0b | 0b0001 |
Hexadecimal | 0x | 0xfff |
Real Literals
Real literals can use decimal and/or exponential notation, for example:
double d = 1.5;
double million = 1E06;
Numerical literal type inference
By default, the compiler infers a numeric literal to be either double or an integral type:
- If the literal contains a decimal point or the exponential symbol (E), it is a double.
- Otherwise, the literal’s type is the first type in this list that can fit the literal’s value: int, uint, long, and ulong. For example:
Console.WriteLine ( 1.0.GetType()); // Double (double)
Console.WriteLine ( 1E06.GetType()); // Double (double)
Console.WriteLine ( 1.GetType()); // Int32 (int)
Console.WriteLine ( 0xF0000000.GetType()); // UInt32 (uint)
Console.WriteLine (0x100000000.GetType()); // Int64 (long)
Numeric Suffixes
Numeric suffixes explicitly define the type of a literal. Suffixes can be either lowercase or uppercase.
The suffixes U and L are rarely necessary because the uint, long, and ulong types can nearly always be either inferred or implicitly converted from int:
long i = 5; // Implicit lossless conversion from int literal to long
The D suffix is technically redundant in that all literals with a decimal point are
inferred to be double. And you can always add a decimal point to a numeric literal:
double x = 4.0;
The F and M suffixes are the most useful and should always be applied when specifying float or decimal literals. Without the F suffix, the following line would not compile, because 4.5 would be inferred to be of type double, which has no implicit conversion to float:
float f = 4.5F;
The same principle is true for a decimal literal:
decimal d = -1.23M; // Will not compile without the M suffix.
We describe the semantics of numeric conversions in detail in the following section.
Numeric Conversion
Converting between integral types
Integral type conversions are implicit when the destination type can represent every possible value of the source type. otherwise, an explicit conversion is required.
Converting between floating-point types
A float can be implicitly converted to a double given that a double can represent every possible value of a float. The reverse conversion must be explicit.
Converting between floating-point and integral types
- All integral types can be implicitly converted to all floating-point types.
- The reverse conversion must be explicit.
- When you cast from a floating-point number to an integral type, any fractional portion is truncated;
- no rounding is performed.
- The static class System. Convert provides methods that round while converting between various numeric types.
- implicitly converting a large integral type to a floating-point type preserves magnitude but can occasionally lose precision. This is because floating-point types always have more magnitude than integral types but can have less precision.
int i1 = 100000001;
float f = i1; // Magnitude preserved, precision lost
int i2 = (int)f; // 100000000
Decimal conversions
- All integral types can be implicitly converted to the decimal type given that a decimal can represent every possible C# integral-type value.
- All other numeric conversions to and from a decimal type must be explicit because they introduce the possibility of either a value being out of range or precision being lost.
Arithmetic Operators
The arithmetic operators are defined for all numeric types except the 8- and 16-bit integral types:
Operator | Description |
---|---|
+ | Addition |
– | Subtraction |
* | Multiplication |
/ | Division |
% | Remainder after division |
Increment and Decrement Operators
The Increment and decrement operators:
++
increment numeric types by 1.--
decrement numeric types by 1.
The operator can either follow or precede the variable, depending on whether you want its value before or after the increment/decrement;
int x = 0, y = 0;
Console.WriteLine (x++); // Outputs 0; x is now 1
Console.WriteLine (++y); // Outputs 1; y is now 1