As we learned in the previous post, C# supports a type system. Part of that system is a group of "basic" types. These types, also called primitive types, form the foundation of many C# programs. Sign me up for an off-the-grid vacation! Photo by fran hogan / Unsplash

## Number Types

The most basic of the primitive types are the number types. These include integral numeric types (which represent whole numbers, like 1, 67, 1957321, 8, and so on) and floating-point numeric types (which represent non-whole numbers such as 1.2, 6.99, 8234.66, and so on).

### Int

Of the integral numeric types, the type `int` is the default and most common. `int` represents a 32-bit integer, with a positive or negative value.

``````int five = 5;
int thirteenHundred = 1300;
int negativeForty = -40;
int intMaxValue = int.MaxValue; //(2^31 - 1)``````

The type `int` is used for many kinds of variables, including math, counters, and iterators.

### Short, Long, and Byte

`short`, `long`, and `byte` are all integral numeric types, like `int`. However, they represent different ranges of values.

A `short` represents a 16-bit integer:

``````short three = 3;
short negativeOneHundred = -100;
short shortMaxValue = short.MaxValue; //(2^15 - 1)``````

A `long` represents a 64-bit integer:

``````long fifty = 50;
long longMaxValue = long.MaxValue; //(2^63 - 1)``````

Finally, a `byte` is a 8-bit integer that only represents positive values.

``````byte four = 4;
byte byteMaxValue = byte.MaxValue; //(2^7 - 1)``````

#### Signed and Unsigned

Integral types in C# are normally signed, meaning they can represent positive or negative values (the exception to this is `byte`, which is unsigned and therefore can only represent positive values).

We can use the types `ushort`, `uint`, and `ulong` to represent unsigned integers, and `sbyte` to represent signed bytes.

``````ushort unsignedShortMax = 65535;
uint unsignedIntMax = 4294967295;
ulong unsignedLongMax = 18446744073709551615;
sbyte signedByteMin = -127;``````

## Floating-Point Numeric Types

In C#, floating-point numeric types represents non-whole or partial numbers. They are used to do more complex math calculations, currency representations, and other places where we need more than simple integers.

### Double and Float

A `double` is an 8-byte number used when we need quick calculations but don't care about precision (see "A Note About Precision" below).

``````double fortytwo = 42.0;
double pi = 3.14159;``````

A `float` is used in the same situation as a `double`, but it has less range, since it is a 4-byte number. Consequently it can perform calculations within its range even more quickly than `double`.

When using `double` or `float`, precision is lost when doing complex calculations. For example, in C# we can do this:

``double sum = 0.1 + 0.2;``

But we will get a strange result: 0.30000000000000004

When doing arithmetic with `double` or `float`, precision (which is the accuracy of numbers on the right side of the decimal point) is sacrificed to gain speed. Calculations involving floating-point numbers are computationally expensive, meaning it takes a long time (comparatively) to get a result.

Programming language compilers, including C#'s compiler, take shortcuts when doing these kinds of calculations; these shortcuts dramatically speed up the calculations while not reducing precision too much, except in certain circumstances. Most applications will not care that 0.1 + 0.2 = 0.30000000000000004, because precision is not an absolute requirement for this calculation.

For the vast majority of applications that do not deal with money or currency, we as developers probably don't care about the loss of precision that comes from doing arithmetic using `double` or `float`; it is most likely small enough to be negligible.

However, there are times when precision cannot be lost, and for those times, we use the `decimal` type.

### Decimal

A `decimal` type is used when we need to keep precision, but don't mind that calculations are more computationally expensive to do. The type `decimal` is primarily used for currency or money calculations, since loss of precision would be harmful there.

### Mixing Number Types

It is possible to use `decimal`, `double`, and `float` in calculations with the integral types, though there are some rules.

For example, using any integral type and a `double` in a calculation results in a value of type `double`.

``````int five = 5;
double fivePointFive = 5.5;

double sum = five + fivePointFive; //Result is type double, value 10.5``````

This works similarly for `float`, though if the resulting value is too large, the type of the result is automatically converted to `double`.

As you might have guessed, mixing integral types and `decimal` gives a result of type `decimal`:

``````short three = 3;
decimal sixPointSevenTwo = 6.72;

decimal sum = three + sixPointSevenTwo; //Result is type decimal, value 9.72``````

In general, mixing integral numeric types and floating-point numeric types in math calculations will result in objects which have the floating-point numeric type.

## Non-Number Types

Besides the integral numeric types and the floating-point numeric types, there are also several non-number types that C# provides.

### Bool

C# includes a type `bool` to deal with boolean values (values that must be either true or false).

``````bool isTrue = true;
bool isFalse = false;``````

Boolean values are often utilized in boolean logic, which we will demonstrate more of in Part 4 (Operators) of this series.

### Char

C# also has a `char` type to represent a single text character.

``````char a = 'a';
char ampersand = '&';
char x = 'x';
char comma = ',';
char semicolon = ';';``````

### String

C# has a `string` type that represents a collection of characters.

``````string sentence = "This is a sentence.";
string otherSentence = "The quick brown fox jumped over the lazy dog.";``````

Please note that by Microsoft's own definition of a "primitive" type, `string` is NOT considered a primitive.

The term "string" comes from the idea of this type being a "string" of characters. In fact, the type `string` is implemented as collection of characters, and can be used as though it is an array. We will discuss arrays in Part 12 of this series.

Also, unlike all the other primitive types in this article, `string` is a reference type, not a value type, meaning its default value is `null`. In a later article in this series, we will see many ways of manipulating strings using a variety of C# operators.

### DateTime

The type `DateTime`, like type `string`, is not considered a "primitive" type but it is so commonly used in C# applications that I felt it was worthy of inclusion in this post.

An instance of `DateTime` represents a point in time. Typically, this is expressed as a date and a time.

``````DateTime date1 = new DateTime();
DateTime date2 = new DateTime(2020, 3, 15); //15 March 2020
DateTime date3 = new DateTime(2020, 3, 15, 10, 30, 00); //15 March 2020, 10:30``````

There are many ways to create instances of this object; the above is just a few of them. We will see many other ways to manipulate `DateTime` objects during a later post.

## Literal Values

The C# compiler makes assumptions as to what type a variable has if we do not directly tell it what that type should be. In C#, if we write this code:

``var myValue = 7.8;``

The type of `myValue` will be `double`, because `double` is the default type for any number with a decimal point.

If we want to create `myValue` as type `decimal`, we need to declare it with the literal marker M.

``var myValue = 7.8M;``

There are many types of these literal markers, including:

``````var myDouble = 5.6D; //double
var myFloat = 2.88F; //float
var myLong = 568373L; //Type long, will be type ulong if the value is too large
var myUnsignedInt = 98765U; //Type uint, will be type ulong if the value is too large``````

## Nullable Types

C# allows for the use of nullable types, where a primitive value type can be either one of its "normal" values or `null`.  Nullable types are identified with the operator `?`.

Nullable types get the value `null` as their default value.

``````char? a = null;
double? myDouble; //Value will be null
decimal? myMoney = 45.61M;
bool? trueFalseOrNotFound = false;
DateTime? myDate = null;
int? myNumber = null;
float? myFloat = 6.3F;``````

There are two special constrictions on nullable types:

1. We cannot use `var` and make the type nullable AND
2. Type `string` cannot be nullable, since it is a reference type.

When types are made nullable, the C# compiler gives them two special properties: `HasValue` and `Value`. `HasValue` can be used to check if the variable has a value at all, and `Value` gives the non-null value but will throw an exception if the value is indeed null.

``````int? myValue = 5;

if(myValue.HasValue)
{
Console.WriteLine(myValue.Value); //Output: 5
}

int? myValue2 = null;

if(myValue2.HasValue)
{
Console.WriteLine(myValue.Value); //Line does not execute,
//since myValue2 is null
}``````

## Glossary

• Primitive types - "Basic" C# types, including `int`, `char`, `bool`, `string` and others.
• Integral numeric types - C# types which represent whole numbers.
• Floating-point numeric types - C# types which represent partial numbers (e.g. decimals or fractions).
• Signed - A value which can be positive or negative.
• Unsigned - A value which can only be positive.
• Precision - The degree to which values of "numbers after the decimal point" are correct. Types `float` and `double` sacrifice precision for speed, whereas type `decimal` keeps precision but is more computationally expensive.
• Boolean - A value which must be either true or false.
• Literal marker - In C#, a single character which identifies the type of the specified value. For example, `5.67M` identifies this value as type `decimal`.
• Nullable types - A value type in C# that allows an object to be null, in addition to its normal value range.

## Summary

Basic types in C# include integral numeric types `int`, `long`, `short`, and `byte`; floating-point numeric types `double`, `float` and `decimal`; and non-numeric types `bool`, `char`, and `string`, plus others; each has a distinct purpose.

Combining objects of different numeric types in math calculations generally results in an object having the more-general type (e.g. combining an `int` and a `double` will result in a `double`, combining a `short` and a `decimal` results in a `decimal`, etc.)

To specify which type a given value should be, we can use literal markers, such as `M` for `decimal` or `F` for `float`. We can also make some objects nullable to allow them to have the value `null` in addition to the normal values those types can have.

In the next part of this series, we expand on C#'s type system to show how we can take objects of one type and change them to another type via casting, conversion, parsing, the `is` and `as` keywords, and more. Check it out!

Happy Coding!