# Getting CSharper #4: Working with numbers

Continuing our road through C#, that already covered a basic C# program and how types are used in C#, now we will work with numeric types that, together with string and arrays, are the most used types in C#.

## Built-in numeric types

We saw in the last post that C# has some predefined types to represent numbers. To represent integers, C# has 8 types, sbyte, short, int, long and their respective unsigned versions, byte, ushort, uint and ulong. They can be used to represent numbers in the following sizes, 8 bits(`sbyte`

), 16 bits(`short`

), 32 bits(`int`

) and 64 bits(`long`

). When we need represent real numbers, C# gives to us 3 options. `Float`

and `double`

are normally used for scientific calculations while `decimal`

is normally used for financial calculations. The table below describes each one of these types, their sizes and their ranges.

Type | Size (bits) | Range |
---|---|---|

sbyte | 8 | -2^7 to 2^7 - 1 |

byte | 8 | 0 to 2^8 - 1 |

short | 16 | -2^15 to 2^15 - 1 |

ushort | 16 | 0 to 2^16 - 1 |

int | 32 | -2^31 to 2^31 - 1 |

uint | 32 | 0 to 2^32 - 1 |

long | 64 | -2^63 to 2^63 - 1 |

ulong | 64 | 0 to 2^64 - 1 |

float | 32 | -3.4 x 10^38 to 3.4 x 10^38 |

double | 64 | ±5.0 × 10 ^ −324 to ±1.7 × 10 ^ 308 |

decimal | 128 | -7.9 x 10^28 to 7.9 x 10^28 |

## Declaring Numbers

Numbers in C# can be declared using decimal, hexadecimal or exponential notation.

int decimalNotation = 10; // 10 int hexadecimalNotation = 0xA; // 10 double exponentialNotation = 1E2; // 100

## Type Inference

When we are declaring a number, the compiler always tries to infer the type of a given number is. If a given number contains a decimal point or an exponential notation (E), the given number is a `double`

. Otherwise, the compiler tries to find a integer type that can represent the given number, the following order is used: `int`

, `uint`

, `long`

and `ulong`

.

1.0 // double 1.25 // double 1E2 // double 1 // int 10000000000000000000 // ulong

When declaring a number, we can also use numeric suffixes (U, L, UL, F, D, M) to force the compiler to infer a given type.

1U // uint 1L // long 1UL // ulong 1F // float 1D // double 1M // decimal

## Type Conversions

Numeric conversions in C# can be implicit or explicit. If the destination type can represent all the possible values that a given type can, an implicit conversion can be executed. Otherwise, if a given type can represent more values that the destination type a cast (explicit conversion) have to be performed.

For example, when converting from `short`

to `int`

, we can make an implicit conversion, since an `int`

can represents all the possible values that a `short`

can represent. Otherwise, when converting from `long`

to `int`

, an explicit conversion is required, with possible losses, since a `long`

can represent more values that an `int`

.

Between `float`

and `double`

the same thing happens, you can make an implicit conversion from a `float`

to a `double`

, and have to make an explicit conversion when converting from a `double`

to a `float`

.

When converting to a `decimal`

, an exception exists, all integer values can be converted implicitly to a `decimal`

. Otherwise, a explicit conversion is required.

short shortNum = 1; int intNum = 1; long bigLongNum = 1100000000000000000; long smallLongNum = 1; intNum = shortNum; // 1 - implicit conversion intNum = (int)smallLongNum; // 1 - explicit conversion intNum = (int)bigLongNum; // 82706432 - explicit conversion with loss float floatNum = 1.0F; double smallDoubleNum = 2.0; double bigDoubleNum = 10E200; smallDoubleNum = floatNum; // 1.0 - implicit conversion floatNum = (float)smallDoubleNum; // 1.0 - explicit conversion floatNum = (float)bigDoubleNum; // +infinite - explicit conversion with loss decimal decimalNum = 0; decimalNum = 1U; // 1 decimalNum = 1; // 1 decimalNum = 1L; // 1 decimalNum = 1UL; // 1 decimalNum = (decimal) 1F; // 1 decimalNum = (decimal) 1D; // 1

## Operators

C# has the same basic operators than other languages (`+`

,`-`

,`/`

,`*`

) and due to being a C-Like, it also has unary operators with before and after increments/decrements.

int a = 3, b = 2; Console.WriteLine(a + b); // 5 Console.WriteLine(a - b); // 1 Console.WriteLine(a/b); // 1 - divisions between integers are truncated Console.WriteLine(a*b); // 6 Console.WriteLine(a++); // 3 - increment after to print the number Console.WriteLine(++a); // 5 - increment before to print the number Console.WriteLine(b--); // 2 - decrement after to print the number Console.WriteLine(--b); // 0 - decrement before to print the number

## Double or Decimal

`Decimal`

should be used when accuracy is important, money for example. Otherwise, we can use `double`

or `float`

. A common mistake when learning C# is to use `double`

to represent financial values, there is a reason to avoid it, `float`

and `double`

are represented internally in base 2, that is, it can only represent precisely base 2 numbers, while `decimal`

is represented in base 10, that is, it can represent floating point numbers more precisely but there is a cost, performance.

double doubleA = 0.1F; double doubleB = 1F; Console.WriteLine(doubleB - 10 * doubleA); // -1,49011611938477E-08 Console.WriteLine(doubleB + 10 * doubleA); // 2,00000001490116 decimal decimalA = 0.1M; decimal decimalB = 1M; Console.WriteLine(decimalB - 10 * decimalA); // 0,0 Console.WriteLine(decimalB + 10 * decimalA); // 2,0

## See also other posts from this series

Getting CSharper #1: A short introduction to C#

Getting CSharper #2: Understanding a C# Program

Getting CSharper #3: Understanding C# Types

## Leave a Reply