Last Updated on February 11, 2024

As a .NET developer, you will probably have to use floating-point numeric types at some point in your career. Basically, floating-point numbers are useful to do mathematics in the code.

Furthermore, there are 3 built-in types that you can use to represent floating-point numbers: ** float**,

**and**

*double***. Every one of these types has its strength and weakness. So, it’s important to understand the difference between these 3 types in order to choose the most appropriate type for your use case.**

*decimal*Let’s look at the specifics of each of these types.

## Single-precision floating-point numbers

The * float* type is the built-in type representing a single-precision floating-point format.

### .NET type

The equivalent .NET type is the structure: System.Single.

### Size

A * float* variable takes 4 bytes in memory (i.e. 32 bits).

### Literal Suffix

You can initialize a ** float **variable by using a literal with the

*f*or

*F*suffix.

float distance = 123.45f;

### Floating-point numbers precision

In the 32 bits representing the * float* structure, 1 bit is reserved for the sign (i.e. positive or negative), a maximum of 23 bits represent the significand or mantissa, and a maximum of 8 bits represent the exponent. Therefore, the

**type can represent numbers with a maximum precision of 9 decimal digits.**

*float*When doing operations with ** floa**t types, you must keep in mind that precision is not the strength of this type. This is due to the fact that the

*type gives an imprecise representation of some specific numbers. Therefore, doing mathematical operations with those imprecise representations will give you approximate numbers as a result. This is not necessarily a bad thing; it really depends on the type of application that you are coding. However, performance is one of the strengths of the*

**float***type: It takes less time to compute arithmetic operations with*

**float***types.*

**float**As you can see in the output of the following code, the 2 operations do not give the same result because of the imprecision that we talked about previously:

float width= 0.8f; float length = 4.1f; float perimeter1 = (width + length) * 2; float perimeter2 = (width + width + length +length); Console.WriteLine($"perimeter1 == perimeter2: {perimeter1==perimeter2}"); Console.WriteLine($"perimeter1: {perimeter1:R}"); Console.WriteLine($"perimeter2: {perimeter2:R}");

In this example, the numeric output format is R, which displays the whole value to its maximum precision. The R format, also known as the round-trip format, is available for ** double** or

**variables only.**

*float*### Floating-point numbers range

The** float **type can represent numbers between -3.402823 x 10

^{38}and 3.402823 x 10

^{38}.

### Predefined constants

Here are some of the predefined values of the ** float** type:

- PositiveInfinity: The result of dividing a positive number by zero.
- NegativeInfinity: The result of dividing a negative number by zero.
- NaN: represents an undefined number.
- Epsilon: the smallest positive float number (1.401298 x 10
^{-45})

## Double-precision floating-point numbers

The ** double** type is the built-in type representing a double-precision floating-point number.

### .NET type

The equivalent .NET type is the structure: System.Double.

### Size

A ** double** variable takes 8 bytes in memory (i.e 64 bits).

### Literal suffix

You can initialize a ** double** variable by using a literal with the d or D suffix.

double distance = 123.45d;

### Floating-point numbers precision

In the 64 bits representing the ** double** structure, 1 bit is reserved for the sign (i.e. positive or negative), a maximum of 52 bits represent the significand or mantissa, and a maximum of 11 bits represent the exponent. Therefore, the

**type can represent numbers with a maximum precision of 17 decimal digits.**

*double*Similar to the ** float** type, when doing operations with the

**type, you must keep in mind that precision is not the strength of this type as well. Again, that doesn’t mean that you shouldn’t use the double type. It really depends on the type of application that you are coding. Also, keep in mind that performance is the strength of the**

*double***type: It takes less time to compute arithmetic operations with**

*double***types than**

*double***types.**

*decimal*For instance, here is the output of the code sample that we used previously to demonstrate the imprecision of arithmetic operations with the ** float **type. We just changed the types of the variables from

**to**

*float***:**

*double*double width = 0.8d; double length = 4.1d; double perimeter1 = (width + length) * 2; double perimeter2 = (width + width + length + length); Console.WriteLine($"perimeter1 == perimeter2: {perimeter1 == perimeter2}"); Console.WriteLine($"perimeter1: {perimeter1:R}"); Console.WriteLine($"perimeter2: {perimeter2:R}");

With variables of type ** double**, we can see that the equality operator now returns

*true*, which is better than the result that we had with

**variables. However, if you look at the value displayed for the two perimeters, it has an imprecision of 10**

*float*^{-15}. The real value should be 9.8 instead of 9.799999999999999.

### Floating-point numbers range

The** double **type can represent numbers between -1.79769313486232 x 10

^{308}and 1.79769313486232 x 10

^{308}.

### Predefined constants

Here are some of the predefined values of the ** double** type:

- PositiveInfinity: The result of dividing a positive number by zero.
- NegativeInfinity: The result of dividing a negative number by zero.
- NaN: represents an undefined number.
- Epsilon: the smallest positive float number (4.94065645841247 x 10
^{-324})

## Decimal floating-point numbers

The ** decimal** type is the built-in type representing a decimal floating-point number. Basically, the

**type is recommended for financial calculations because it’s a very precise representation of floating-point numbers.**

*decimal*### .NET type

The equivalent .NET type is the structure: System.Decimal.

### Size

A decimal variable takes 16 bytes in memory (i.e 128 bits).

### Literal suffix

You can initialize a decimal variable by using a literal with the m or M suffix.

decimal balance = 1500.05m;

### Floating-point numbers precision

In the 128 bits representing the ** decimal **structure, 32 bits are reserved for the sign and the scaling factor and 96 bits represent the integer number. Accordingly, the

**structure can represent numbers with a maximum precision of 29 decimal digits.**

*decimal*When doing operations with ** decimal** types, you must keep in mind that precision is the strength of the decimal type. Contrary to the

**and**

*double***types, there are no unexpected rounding errors when doing operations with the**

*float***type. For that reason, Microsoft recommends using the**

*decimal**type if you are doing financial operations because you need the maximum precision for currencies.*

**decimal**However, arithmetic operations with ** decimal** types usually take a little more time to compute than arithmetic operations with the

**or**

*float***types. So, it’s a trade-off since you have more precision but a little less performance.**

*double*Here is the output of the code sample that we used previously to demonstrate the imprecision of arithmetic operations with the ** float** type. But, we changed the types of the variables from

**to**

*float***.**

*decimal*For instance, the arithmetic operation gives a more precise result:

decimal width = 0.8m; decimal length = 4.1m; decimal perimeter1 = (width + length) * 2; decimal perimeter2 = (width + width + length + length); Console.WriteLine($"perimeter1 == perimeter2: {perimeter1 == perimeter2}"); Console.WriteLine($"perimeter1: {perimeter1}"); Console.WriteLine($"perimeter2: {perimeter2}");

### Floating-point numbers range

The ** decima**l type can represent numbers between -7.9228 x 10

^{28}and 7.9228 x 10

^{28}.

### Predefined constants

Here are some of the predefined values of the ** decimal** type:

As you noticed, the NegativeInfinity and PositiveInfinity constants are not defined for the ** decimal** type. This is due to the fact that the

**type is not meant to be used for complex arithmetic operations.**

*decimal*## Summary

In conclusion, here is a summary of the difference between the 3 floating-point numeric types: ** float**,

**and**

*double***.**

*decimal*float | double | decimal | |

.NET type | System.Single | System.Double | System.Decimal |

Size | 32 bits | 64 bits | 128 bits |

Literal suffix | f or F | d or D | m or M |

Precision | 9 decimal digits | 17 decimal digits | 29 decimal digits |

Range | -3.402823 x 10^{38} to 3.402823 x 10 ^{38} | -1.79769313486232 x 10^{308} to 1.79769313486232 x 10 ^{308} | -7.9228 x 10^{28}to 7.9228 x 10 ^{28} |

Recommended For | Use float variables for non-integer operations where precision is not important. | Use double variables for operations where the values can be very large or extremely small floating-point numbers. Usually, in these cases, a very small margin of error in operations is still acceptable. | Use decimal variables for operations where precision is important (i.e financial application). |

I hope you enjoyed this article from our fundamentals series. You can find other articles with the fundamental tag here

## Leave a Reply