C# has a number of predefined numeric types. The names and details of which can be found in the table below.

Type |
Description |
Minimum |
Maximum |
Bits |

byte | Unsigned Byte | 0 | 255 | 8 |

sbyte | Signed Byte | -128 | 127 | 8 |

short | Signed Short Integer | -32768 | 32767 | 16 |

ushort | Unsigned Short Integer | 0 | 65535 | 16 |

int | Signed Integer | -2147483648 | 2147483647 | 32 |

uint | Unsigned Integer | 0 | 4294967295 | 32 |

long | Signed Long Integer | -9×1018 | 9×1018 | 64 |

ulong | Unsigned Long Integer | 0 | 1.8×1019 | 64 |

float | Single Precision Number | ±1.5×10-45 to ±3.4×1038 | 7 digits | 32 |

double | Double Precision Number | ±5×10-324 to ±1.7×10308 | 15 or 16 digits | 64 |

decimal | Decimal Number | ±10-28 to ±7.9×1028 | 28 or 29 digits | 128 |

From the table sbyte, short, int and long are all known as signed integral types. Signed means that the numbers can have a negative value but at the expense of some range. The first bit indicates positive or negative.

Byte, ushort, uint, and ulong are all unsigned integral types and the only difference is these type can only take on a positive form but have additional range. The bit that would be used to represent positive/negative is used for range.

The last set of numbers are referred to as floating-point numbers, these include float, double and decimal, usually, these types of numbers are more useful for scientific and financial calculations as they can have numbers after the decimal point.

There are two different ways to represent the value in an integral type literal, these are the decimal (base-10) number system and the hexadecimal (base-16) number system.

```
int a = 32;
int b = 0x20; //HEX represents the decimal value 32
long c = 2147483648; //One more than the maximum value for an int
long d = 0x80000000; //HEX represents the decimal value 2147483648
//C# 7 supports underscores for readability
long e = 1_000_000_000_000;
//C# 7 Also supports specifying numbers in binary
long f = 0b0101_0111_0001_1100_0011_1010;
```

To declare a real literal you can either use a decimal or exponential notation, examples are below:

```
double g = 1.9;
double h = 1E06;
```

The compiler will attempt to infer numeric literals into integral or double types. It does this by checking to see if the literal contains ‘E’ for exponential otherwise it will check to see if the literal can fit into the types, starting with the smallest types.

```
var type = 1.0.GetType(); //Double or double
var type = 2E04.GetType(); //Double or double
var type = 1.GetType(); //Int32 or int
var type = 0x100000000.GetType(); //Int64 or long
```

Hexadecimal is not that unlike the decimal number system we are used to, the only difference being instead of using the numbers 0-9 in each position, we can use 0-F.

https://en.wikipedia.org/wiki/Hexadecimal

When we think of the number 456 in the decimal number system, we know this is:

It is a similar principle, however we use 16 as a base.

Converting the hexadecimal number 0xBCE to decimal would look like the following

If you wish to explicitly define the type of literal you want to use then you can use a suffix. It does not matter if the suffix is uppercase or lowercase.

From this list, the F and M suffixes are the most significant as they are the hardest for the compiler to automatically infer.

```
float f = 2.35f; //Otherwise would infer double
decimal d = 2384.234M //Otherwise would infer double
```

If the destination type can represent all possible values that the source type can handle then an implicit conversion will occur. For example, if you wished to convert from int to long then the compiler knows it will be able to handle this is in all situations.

If the destination type cannot support all possible values of the source type then an explicit conversion will need to be used, you will need to use the cast operator.

```
int toConvert = 50;
long destination = toConvert; //Implicit conversion
short explicitDestination = (short) toConvert; //Explicit conversion
float floatingPointToConvert = 1.0f;
double floatingPointDestination = floatingPointToConvert; //Implicit
int integralToFloat = 1;
float floatDestination = integralToFloat; //int to float is implicit
float floatToIntegral = 1.0000000000000001f;
int integralDestination = (int) floatToIntegral; //Explicit Conversion
```

When you make an explicit conversion from a floating-point type to an integral type you will lose information as there is truncation. You will lose the numbers after the decimal point, which are also not rounded.

With the exception of the byte/sbyte and short/ushort integral types, all numeric types support the following arithmetic operators.

It is worth noting that division on Integral types will always round down and truncate any remainders. There is a special type of error when you try to divide a number by 0, this is called a DivideByZeroException.

```
+ Addition
e.g. int a = 4 + 6;
- Subtraction
e.g. int b = 11 - 5;
* Multiplication
e.g. int c = 4 * 9;
/ Division
e.g. int d = 8 / 2;
% Remainder after Division
e.g. int e = 10 % 3;
```

If it is only required that a number need to be incremented or decremented by one then there is a shorthand notation for this. This notation is using the ++ or — operators.

```
//To add 1 to a number
int a = 1;
Console.WriteLine(++a); //a == 2 Outputs 2
Console.WriteLine(a++); //a == 3 Outputs 2
Console.WriteLine(--a); //a == 2 Outputs 2
Console.WriteLine(a--); //a == 1 Outputs 2
```

As Integral types are effectively a number of bits used to represent a value, it is possible that an operation may exceed the maximum range of this representation. Take an unsigned byte which has 8 bits.

The maximum number that can be represented is 255. If we go to add 1 to this value, we will not encounter an error but instead will experience called wrap around. The compiler expects that the type has more bits and will perform the operation as so. This will result in the following.

As we only have 8 bits, the number will actually be 0 (the eight 0s).

The same effect can be seen when you subtract from the minimum value represented by an integral type, the value will wrap around to the max representation. To get the maximum and minimum values represented by a type.

```
<integralType>.MinValue; //Minimum Value
<integralType>.MaxValue; //Maximum Value
```

For safety, you can check to see if an overflow is going to occur on the operation and generate an OverflowException if it does. To do this wrap your operations in the checked operator like so.

```
int total = checked(a * b);
// Or use it as a block
checked
{
int total = a * b;
}
```

The checked statement is relevant when using the ++, –, +, – * and / operators.

Non-runtime expressions are always checked by the compiler during compilation. If you exceed the maximum value of an integral type during compilation – the compiler will warn you.