C#为什么等小数会产生不等的哈希值?

Jan*_*nes 42 .net c# hash decimal

我们碰到了一个神奇的十进制数字,打破了我们的哈希表.我把它归结为以下最小的情况:

decimal d0 = 295.50000000000000000000000000m;
decimal d1 = 295.5m;

Console.WriteLine("{0} == {1} : {2}", d0, d1, (d0 == d1));
Console.WriteLine("0x{0:X8} == 0x{1:X8} : {2}", d0.GetHashCode(), d1.GetHashCode()
                  , (d0.GetHashCode() == d1.GetHashCode()));
Run Code Online (Sandbox Code Playgroud)

给出以下输出:

295.50000000000000000000000000 == 295.5 : True
0xBF8D880F == 0x40727800 : False
Run Code Online (Sandbox Code Playgroud)

真正奇怪的是:更改,添加或删除d0中的任何数字,问题就会消失.甚至添加或删除其中一个尾随零!这个标志似乎并不重要.

我们的解决方法是将值除去以消除尾随零,如下所示:

decimal d0 = 295.50000000000000000000000000m / 1.000000000000000000000000000000000m;
Run Code Online (Sandbox Code Playgroud)

但我的问题是,C#怎么做错了?

Jon*_*eet 27

首先,C#完全没有做错任何事情.这是一个框架错误.

它确实看起来像一个bug - 基本上,比较相等所涉及的规范化应该以相同的方式用于哈希码计算.我已经检查过并且可以重现它(使用.NET 4),包括检查Equals(decimal)Equals(object)方法以及==操作符.

它肯定看起来像是问题的d0价值,因为添加尾随0 d1不会改变结果(直到它d0当然是相同的).我怀疑那里有一些角落案例被那里的确切位代表所绊倒.

我很惊讶它不是(正如你所说,它大部分时间都有效),但你应该报告Connect上的错误.

  • 已经在Connect上:http://connect.microsoft.com/VisualStudio/feedback/details/314630/system-decimal-gethashcode-violates-equality-contract(虽然标记为已修复!) (7认同)
  • FWIW也适用于.NET 2.0和3.5. (5认同)
  • 在.NET 4.5中仍然会出现问题(与上面的`decimal`s相同). (3认同)