This tip reveals number problems that exist in .NET. The first part describes common limitations of floating numbers. In the second part specific to .NET number problems are shown.
This will be very helpful for junior and mid level developers, though still in my career I have been meeting senior developers that are not aware of such basic things.
Part 1 - Floating Numbers
There are 3 types of floating numbers in .NET:
float or System.Single // 32 bit floating number with base 2
double or System.Double // 64 bit floating number with base 2
decimal or System.Decimal // 128 bit floating number with base 10
decimal are the keywords available in C# and not in VB.)
Decimal floating number is mostly convenient for people.
decimal m = 102.45m
Dim m As Decimal = 102.45
In the computer, it will be represented approximately as we expected:
10^2 10^1 10^0 . 10^-1 10^-2
100 10 1 . 1/10 1/100
1 0 2 . 4 5
The same strategy is used in
Double (base 2 floating numbers)
double d = 12.75
Dim d as Double = 12.75
2^3 2^2 2^1 2^0 . 2^-1 2^-2
8 4 2 1 . 1/2 1/4
1 1 0 0 . 1 1
8 + 4 + 0 + 0 . 1/2 + 1/4 = 12.75
The first restriction is that sum of 2^(-N) cannot map all fractional numbers with limitation in bits. For example, decimal 0.2 is represented in endless double number.
Using DoubleConverter created by Jon Skeet, we can look at the representation of the number 0.2 in double format:
So do not be surprised in equality checking for base-2 floating numbers (it is always approximation if exact conversion is not possible).
public void DoubleNumberEqualityCheck()
double d1 = 0.6 - 0.2;
double d2 = 0.4;
<TestMethod()> Public Sub DoubleNumberEqualityCheck()
Dim d1 As Double = 0.6 - 0.2
Dim d2 As Double = 0.4
Assert.AreNotEqual(d1, d2) ' d1 <> d2
Use number type that works for the task.
Decimal type fits well for money.
Single are good for scientific calculations, because processor works faster with them (than with
Decimal) and approximation can be smartly taken.
Because of its nature, this limitation exists for any program language (Java, .NET, SQL).
Part 2 Floating Numbers in .NET
The next conversion problem is less known. Casting and converting in C# have distinct implementation.
public void ConvertAndCastFromDecimalToInt32ShouldBeTheSame()
decimal m1 = 2.500001m;
int i1 = (int)m1;
int i2 = Convert.ToInt32(m1);
In C# integer
i1 = 2 after casting from decimal and conversion for the same decimal produces
i2 = 3.
It is interesting that the same code in VB.NET with CType casting does not produce this error.
<TestMethod()> Public Sub ConvertAndCastFromDecimalToInt32ShouldBeTheSame()
Dim m1 As Decimal = 2.500001D
Dim i1 As Integer = CType(m1, Integer)
Dim i2 As Integer = Convert.ToInt32(m1)
Only in C#, the same problem exists for
Not a Number
Single type, there are several special values reserved in .NET (and not for
These numbers can appear in special operations:
- square root from negative number. e.g.
- 0 / 0
- 0 * 8
- 8 / 8
NaN numbers is that it should not be equal to another
NaN number. Otherwise, how we should compare
Double.Nan != Double.Nan, however if we compare on equality with
Equals() method, it will produce very messy result for
Double.NaN != <span class="s1">Double</span>.NaN, but
Double.NaN.Equals(<span class="s1">Double</span>.NaN) and
<span class="s1">Double</span>.PositiveInfinity == <span class="s1">Double</span>.PositiveInfinity?
public void CheckNaNForInequality()
double d = Double.NaN;
Assert.IsFalse(d == Double.NaN);
double d2 = Double.PositiveInfinity;
Assert.IsTrue(d2 == Double.PositiveInfinity);
double d3 = Double.NegativeInfinity;
Assert.IsTrue(d3 == Double.NegativeInfinity);
<TestMethod()> Public Sub CheckNaNForInequality()
Dim d As Double = Double.NaN
Assert.IsFalse(d = Double.NaN)
Dim d2 As Double = Double.PositiveInfinity
Assert.IsTrue(d2 = Double.PositiveInfinity)
Dim d3 As Double = Double.NegativeInfinity
Assert.IsTrue(d3 = Double.NegativeInfinity)
To avoid errors, it is better to use next methods for special number checking:
There is a difference in representation between C# and VB in debug value inspection:
NaN is presented as
NaN in C# and
-1.#IND in VB (IND for indeterminate)
PositiveInfinity is presented as
+Infinity in C# and
1.#INF in VB
NegativeInfinity is presented as
-Infinity in C# and
-1.#INF in VB