## Introduction

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

(`float`

, `double`

and `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 `Single`

and `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 // bit representation ---------------------------------------- 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:

0.200000000000000011102230246251565404236316680908203125

So do not be surprised in equality checking for base-2 floating numbers (it is always approximation if exact conversion is not possible).

[TestMethod] public void DoubleNumberEqualityCheck() { double d1 = 0.6 - 0.2; double d2 = 0.4; Assert.AreNotEqual(d1, d2); // d1 != d2 } <TestMethod()> Public Sub DoubleNumberEqualityCheck() Dim d1 As Double = 0.6 - 0.2 Dim d2 As Double = 0.4 Assert.AreNotEqual(d1, d2) ' d1 <> d2 End Sub

Use number type that works for the task.`Decimal`

type fits well for money. `Double`

and `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

### Conversion Problem

The next conversion problem is less known. Casting and converting in C# have distinct implementation.

[TestMethod] public void ConvertAndCastFromDecimalToInt32ShouldBeTheSame() { decimal m1 = 2.500001m; int i1 = (int)m1; // calls Decimal.ToInt32(value) int i2 = Convert.ToInt32(m1); // has own algorithm Assert.AreEqual(i1, i2); // i1 = 2; i2 = 3 }

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) Assert.AreEqual(i1, i2) ' i1 = 3; i2 = 3 End Sub

Only in C#, the same problem exists for `double`

and `int`

, `float`

and `int (Int32/Int64)`

.

### Not a Number

For `Double`

and `Single`

type, there are several special values reserved in .NET (and not for `Decimal`

).

Double.NaN; Double.NegativeInfinity; Double.PositiveInfinity; Single.NaN; Single.NegativeInfinity; Single.PositiveInfinity;

These numbers can appear in special operations:

- square root from negative number. e.g.
`Math.Sqrt(-2)`

- 0 / 0
- 0 * 8
- 8 / 8

Idea behind `NaN`

numbers is that it should not be equal to another `NaN`

number. Otherwise, how we should compare `Math.Sqrt(-2) `

and `Math.Sqrt(-100)`

?

So, `Double.Nan != Double.Nan`

, however if we compare on equality with `Equals()`

method, it will produce very messy result for `NaN`

and `Infinity`

.

Why `Double.NaN != Double.NaN`

, but `Double.NaN.Equals(Double.NaN)`

and `Double.PositiveInfinity == Double.PositiveInfinity`

?

[TestMethod] public void CheckNaNForInequality() // Test will pass without errors { double d = Double.NaN; Assert.IsFalse(d == Double.NaN); Assert.IsTrue(d.Equals(Double.NaN)); double d2 = Double.PositiveInfinity; Assert.IsTrue(d2 == Double.PositiveInfinity); Assert.IsTrue(d2.Equals(Double.PositiveInfinity)); double d3 = Double.NegativeInfinity; Assert.IsTrue(d3 == Double.NegativeInfinity); Assert.IsTrue(d3.Equals(Double.NegativeInfinity)); } <TestMethod()> Public Sub CheckNaNForInequality() // Test will pass without errors Dim d As Double = Double.NaN Assert.IsFalse(d = Double.NaN) Assert.IsTrue(d.Equals(Double.NaN)) Dim d2 As Double = Double.PositiveInfinity Assert.IsTrue(d2 = Double.PositiveInfinity) Assert.IsTrue(d2.Equals(Double.PositiveInfinity)) Dim d3 As Double = Double.NegativeInfinity Assert.IsTrue(d3 = Double.NegativeInfinity) Assert.IsTrue(d3.Equals(Double.NegativeInfinity)) End Sub

To avoid errors, it is better to use next methods for special number checking:

Double.IsNaN(value) Double.IsInfinity(value) Double.IsNegativeInfinity(value) Double.IsPositiveInfinity(value)

(similar for `Single`

)

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