Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mathematical explanation why Decimal's conversion to Double is broken and Decimal.GetHashCode separates equal instances

Tags:

I am not sure if this non-standard way of stating a Stack Overflow question is good or bad, but here goes:

What is the best (mathematical or otherwise technical) explanation why the code:

static void Main() {   decimal[] arr =   {     42m,     42.0m,     42.00m,     42.000m,     42.0000m,     42.00000m,     42.000000m,     42.0000000m,     42.00000000m,     42.000000000m,     42.0000000000m,     42.00000000000m,     42.000000000000m,     42.0000000000000m,     42.00000000000000m,     42.000000000000000m,     42.0000000000000000m,     42.00000000000000000m,     42.000000000000000000m,     42.0000000000000000000m,     42.00000000000000000000m,     42.000000000000000000000m,     42.0000000000000000000000m,     42.00000000000000000000000m,     42.000000000000000000000000m,     42.0000000000000000000000000m,     42.00000000000000000000000000m,     42.000000000000000000000000000m,   };    foreach (var m in arr)   {     Console.WriteLine(string.Format(CultureInfo.InvariantCulture,       "{0,-32}{1,-20:R}{2:X8}", m, (double)m, m.GetHashCode()       ));   }    Console.WriteLine("Funny consequences:");   var h1 = new HashSet<decimal>(arr);   Console.WriteLine(h1.Count);   var h2 = new HashSet<double>(arr.Select(m => (double)m));   Console.WriteLine(h2.Count); } 

gives the following "funny" (apparently incorrect) output:

42                              42                  40450000 42.0                            42                  40450000 42.00                           42                  40450000 42.000                          42                  40450000 42.0000                         42                  40450000 42.00000                        42                  40450000 42.000000                       42                  40450000 42.0000000                      42                  40450000 42.00000000                     42                  40450000 42.000000000                    42                  40450000 42.0000000000                   42                  40450000 42.00000000000                  42                  40450000 42.000000000000                 42                  40450000 42.0000000000000                42                  40450000 42.00000000000000               42                  40450000 42.000000000000000              42                  40450000 42.0000000000000000             42                  40450000 42.00000000000000000            42                  40450000 42.000000000000000000           42                  40450000 42.0000000000000000000          42                  40450000 42.00000000000000000000         42                  40450000 42.000000000000000000000        41.999999999999993  BFBB000F 42.0000000000000000000000       42                  40450000 42.00000000000000000000000      42.000000000000007  40450000 42.000000000000000000000000     42                  40450000 42.0000000000000000000000000    42                  40450000 42.00000000000000000000000000   42                  40450000 42.000000000000000000000000000  42                  40450000 Funny consequences: 2 3 

Tried this under .NET 4.5.2.