Updated on
Use double for numbers, decimal for money. double is the fast, 64-bit default that every maths library and API expects; decimal is slower and bigger but stores decimal fractions exactly, so amounts add up to the cent.
float is the one to skip. It is half the size of double and gives roughly seven significant digits instead of fifteen, which is only worth having when we need millions of them at once: graphics, sensor buffers, machine-learning tensors.
What Are Floating-Point Types in C#?
C# has three types for numbers with a fractional part: float, double, and decimal.
float and double are binary floating-point types. They store a number as a sign, a significand, and a power of two, which makes them fast (the CPU has instructions for them) and inexact for most decimal fractions. float is 32 bits and carries roughly seven significant digits; double is 64 bits and carries roughly fifteen.
decimal works in base ten. It stores 128 bits as a scaled integer, so a value like 0.1 is held exactly rather than approximated, and it carries 28 to 29 significant digits.
That difference in base, not in size, is what decides which one to use. Binary types cannot represent 0.1 exactly for the same reason base ten cannot represent one third, and the tiny error repeats every time we add.
Literals need a suffix: 1.5F for float, 1.5M for decimal, and nothing for double, which is the default.
We will use a simple console project to discuss the three floating point types in C# (for a refresher on C#’s basic data types and variables, see our dedicated guide). For that, we can scaffold a new console application in Visual Studio.
After creating the project, let’s define a new FloatingPointArithmetic class, which we will use in the coming sections of the article.
Float
The float data type belongs to the System.Single .NET struct. In memory, it occupies 32 bits, and it carries a precision of roughly 7 significant digits. Precision here is how many significant digits the type represents accurately β not how large a value it can hold.
When declaring float variables, we attach the suffix F or f to the value:
float area = 14.5F;
or
float length = 20.5f;
When using float in our applications, we can perform all numeric computations on our variables. However, due to rounding off, we get inaccurate results from these computations.
Let’s demonstrate this accuracy when working with floating point numbers:
public bool FloatSumAndMultiplication(float firstValue, float secondValue, int factor)
{
float sum = 0F;
for (var i = 0; i < factor; i++)
{
sum += firstValue + secondValue;
}
float product = (firstValue + secondValue) * factor;
return sum == product;
}
We define the FloatSumAndMultiplication method, which takes two float values and one integer. To get the sum of the values, we add the values based on the factor. For instance, if the factor is 2, we add the values twice. To get the product of the values, we add the values and multiply by the factor.
Calling this method:
floatingArithmetic.FloatSumAndMultiplication(0.1f, 0.5f, 10);
We would expect that sum and product are equal. However, by printing both values, we get:
Sum: 5.9999995 Product: 6
For sum, we loop ten times performing the same computation, while product we only do the computation once. We get different results because looping gives us less precise results when working with float.
Double
The double data type belongs to the System.Double .NET struct. We use the double type to represent values with up 15 digits of precision. Simply put, with higher precision, we can represent more information using the double data type. In memory, we reserve 64 bits when we use this data type.
When declaring variables of double type, we add a suffix D or d to the value:
double length = 4.5D;
or
double total = 20.3d;
Similar to float, the results we get when we perform numeric calculations using the double type lack accuracy, because of rounding off errors.
Let’s demonstrate using double in mathematical computations:
public bool DoubleSumAndMultiplication(double firstValue, double secondValue, int factor)
{
double sum = 0D;
for (var i = 0; i < factor; i++)
{
sum += firstValue + secondValue;
}
double product = (firstValue + secondValue) * factor;
return sum == product;
}
Here we define the DoubleSumAndMultiplication method, which returns either true or false depending on whether the values of sum and product are equal.
Calling the method:
floatingArithmetic.DoubleSumAndMultiplication(0.2D, 1.5D, 10);
We expect sum and product to be equal, but printing results to the console, we get:
Sum: 16.999999999999996 Product: 17
Decimal
The decimal data type belongs to the System.Decimal .NET struct. We use this data type in cases where we need a lot more accuracy with our data. Decimal type occupies 128 bits in memory, which is twice that occupied by double. Also, it has the highest precision of the three floating point types.
We declare a decimal type by adding the suffix M or m to the value:
decimal value = 1.5M
or
decimal value1 = 2.5m
When we use decimal in calculations, we get more accurate results as opposed to double and float that have rounding-off errors. In this case, decimal could come in handy when doing financial computations. For keeping decimal results within a chosen number of places, see controlling the precision of decimal numbers.
Let’s look at decimal computations:
public bool DecimalSumAndMultiplication(decimal firstValue, decimal secondValue, int factor)
{
decimal sum = 0M;
for (var i = 0; i < factor; i++)
{
sum += firstValue + secondValue;
}
decimal product = (firstValue + secondValue) * factor;
return sum == product;
}
We pass two decimal parameters and one integer to the DecimalSumAndMultiplication method. Then, we calculate the sum and product of the values. Based on the result of comparing the two values, we return either true or false.
When we call the method:
floatingArithmetic.DecimalSumAndMultiplication(0.2M, 1.5M, 10);
We expect that both sum and product are equal.
Examining the results of the method call, we get:
Sum: 17.0 Product: 17.0
Both sum and product values are equal, and the method returns true. Compared to float and double, the decimal type is the most accurate and most precise.
Why Does 0.1 + 0.2 Not Equal 0.3 in C#?
Because double stores numbers in base two, and 0.1 has no exact base-two representation, just as one third has no exact base-ten one.
What gets stored is the closest 64-bit value, a hair above or below. Add two of those and the errors combine, so the sum is very slightly off 0.3 and an equality check fails even though the printed values look identical. Printing hides it, because .NET formats to the shortest string that round-trips.
The fix is not to round before comparing. It is to stop comparing floating-point numbers for equality at all, and instead test whether they are close enough for the problem: Math.Abs(a - b) < tolerance, with a tolerance we choose deliberately.
decimal sidesteps this for decimal fractions, since 0.1 is exact in base ten. It does not make the type immune: one third is inexact in decimal too, and dividing by three then multiplying back shows it.
The C# reference states the asymmetry exactly: “0.1, for example, can be exactly represented by a decimal instance, while there’s no double or float instance that exactly represents 0.1″ (Floating-point numeric types, Microsoft Learn, read 2026-08-09). The comparison below shows the same thing running: double fails the equality check, the round-trip string exposes the stored value, and decimal succeeds because both operands are exact in base ten. The practical approaches to comparing them safely are covered in our guide to checking floating-point equality.
Console.WriteLine(0.1 + 0.2 == 0.3); // False
Console.WriteLine((0.1 + 0.2).ToString("R")); // 0.30000000000000004
Console.WriteLine(0.1m + 0.2m == 0.3m); // True
The second line is the one to notice: it prints the actual stored value behind the False, and it is decimal, not double, that gets the third comparison right. The "R" round-trip specifier is one of the standard and custom numeric format strings.
Which Should We Use: float, double, or decimal?
Use double unless there is a reason not to. It is the default type for a fractional literal, the type every method on the Math class in C# takes and returns, and the type most APIs and serialisers expect.
Use decimal for money and anything else where the decimal digits are the point: prices, tax, interest, percentages shown to a user. Storing currency in double produces totals that are wrong by fractions of a cent, which is a defect the moment someone reconciles them. The cost is speed, and for the volumes typical financial code deals in, that cost does not matter.
Use float when we need many values and each one’s precision matters little: vertex buffers, image data, sensor readings, tensors. Halving the memory is the entire benefit, and outside those cases double is the better default.
For a single value in ordinary business logic, float saves four bytes and buys a rounding problem.
When a floating-point result must become a whole number, that is a deliberate step of its own β see rounding a number down to the nearest integer.
Microsoft’s own reference agrees, and puts a bound on it: “any difference in performance goes unnoticed by all but the most calculation-intensive applications” (Floating-point numeric types, Microsoft Learn, read 2026-08-09).
| float | double | decimal | |
|---|---|---|---|
| .NET type | System.Single | System.Double | System.Decimal |
| Size | 32 bits | 64 bits | 128 bits |
| Significant digits | ~6β9 | ~15β17 | 28β29 |
| Base | Binary | Binary | Decimal |
| Stores 0.1 exactly | No | No | Yes |
| Literal suffix | F / f | D / d (default) | M / m |
| Math class support | Casts to double | Native | Limited |
| Use it for | Large numeric buffers, graphics | Science, geometry, general maths | Money, percentages, anything shown to a user |
Precision and size in this table are the C# reference’s own figures: float ~6-9 digits / 4 bytes, double ~15-17 digits / 8 bytes, decimal 28-29 digits / 16 bytes (Floating-point numeric types, Microsoft Learn, read 2026-08-09).
Conclusion
In this article, we have covered floating-point types in C#. Handling data in our applications can be quite challenging at first and could impact how our applications perform. With this newly acquired knowledge, we can confidently work with numeric data and improve the quality of our applications.
Tested with .NET 10.0.10.
