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, double and decimal. 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.

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 float type can represent numbers with a maximum precision of 9 decimal digits.

When doing operations with float types, you must keep in mind that precision is not the strength of this type. This is due to the fact that the float 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.

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}");

single-precision floating-point type tests output

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 float variables only.

Floating-point numbers range

The float type can represent numbers between -3.402823 x 1038 and 3.402823 x 1038.

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 double type can represent numbers with a maximum precision of 17 decimal digits.

Similar to the float type, when doing operations with the double 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 decimal types.

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 float to 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}");

double-precision floating-point type tests output

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 float variables. However, if you look at the value displayed for the two perimeters, it has an imprecision of 10-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 10308 and 1.79769313486232 x 10308.

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 decimal type is recommended for financial calculations because it’s a very precise representation of floating-point numbers.

.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 decimal structure can represent numbers with a maximum precision of 29 decimal digits.

When doing operations with decimal types, you must keep in mind that precision is the strength of the decimal type. Contrary to the double and float types, there are no unexpected rounding errors when doing operations with the decimal type. For that reason, Microsoft recommends using the decimal type if you are doing financial operations because you need the maximum precision for currencies.

However, arithmetic operations with decimal types usually take a little more time to compute than arithmetic operations with the float or double types. So, it’s a trade-off since you have more precision but a little less performance.

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 float to 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}");

decimal type tests output

Floating-point numbers range

The decimal type can represent numbers between -7.9228 x 1028and 7.9228 x 1028.

Predefined constants

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

  • One: The number one
  • Zero: The number zero.
  • MinusOne: The number negative one.

As you noticed, the NegativeInfinity and PositiveInfinity constants are not defined for the decimal type. This is due to the fact that the decimal type is not meant to be used for complex arithmetic operations.

Summary

In conclusion, here is a summary of the difference between the 3 floating-point numeric types: float, double and decimal.

floatdoubledecimal
.NET typeSystem.SingleSystem.DoubleSystem.Decimal
Size32 bits64 bits128 bits
Literal suffixf or Fd or Dm or M
Precision9 decimal digits17 decimal digits29 decimal digits
Range-3.402823 x 1038 

to

3.402823 x 1038
-1.79769313486232 x 10308 

to

1.79769313486232 x 10308
-7.9228 x 1028

to

7.9228 x 1028
Recommended ForUse 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