看一下这个 :
var a = Double.NaN;
Console.WriteLine(a == a);
Console.ReadKey();
Run Code Online (Sandbox Code Playgroud)
打印"假"
var a = Double.NaN;
Console.WriteLine(a.Equals(a));
Console.ReadKey();
Run Code Online (Sandbox Code Playgroud)
打印"真实"!
为什么打印"真实"?由于浮点数规范,NaN的值不等于它自己!所以似乎Equals()方法实现错误......我错过了什么?
Rei*_*ica 14
我发现了一篇解决您问题的文章:.NET安全博客:为什么==和等于方法返回浮点值的不同结果
根据IEC 60559:1989,具有NaN值的两个浮点数永远不相等.但是,根据System.Object :: Equals方法的规范,需要重写此方法以提供值相等语义.[...]
所以现在我们对Equals应该意味着什么有两个相互矛盾的想法.Object :: Equals表示BCL值类型应覆盖以提供值相等,而IEC 60559表示NaN不等于NaN.ECMA规范的分区I通过在8.2.5.2节[下面]中记录这个特定情况来解决这个冲突.
更新:CLI规范(ECMA-335)中 8.2.5节的全文对此有了更多的了解.我在这里复制了相关的部分:
8.2.5价值的同一性和平等性
在所有值对上定义了两个二元运算符:identity和 equality.它们返回一个布尔结果,是数学 等价运算符 ; 也就是说,它们是:
- 反身 -
a op a是真的.- 对称 -
a op b当且仅当b op a为真时才为真.- 传递 - 如果
a op b是真的,那b op c是真的,那a op c就是真的.此外,虽然身份总是意味着平等,但事实并非如此.[...]
8.2.5.1身份
身份运营商由CTS定义如下.
- 如果值具有不同的确切类型,则它们不相同.
- 否则,如果它们的确切类型是值类型,那么它们是相同的,当且仅当值的位序列是逐位相同时.
- 否则,如果它们的确切类型是引用类型,那么它们是相同的,当且仅当值的位置相同时.
System.Object通过该ReferenceEquals方法实现身份.8.2.5.2平等
对于值类型,等于运算符是精确类型定义的一部分.等式的定义应遵守以下规则:
- 如上所述,平等应该是等价运算符.
- 如前所述,身份应该意味着平等.
- 如果其中一个(或两个)操作数是一个盒装值,[...]
System.Object通过该Equals方法实现平等 .[ 注意:虽然IEC 60559:1989定义了两个浮点NaN以始终比较为不等,但System.Object.Equals的合同要求覆盖必须满足等价运算符的要求.因此,
System.Double.Equals和System.Single.Equals比较,当两个NaN的,而等于运算符将返回在这种情况下假,所要求的IEC标准返回True.结束说明 ]
以上内容并未详细说明==操作员的属性(最终注释除外); 它主要是定义的行为ReferenceEquals和Equals.对于==运算符的行为,C#语言规范(ECMA-334)(第14.9.2节)明确了如何处理NaN值:
如果任一操作数[to
operator ==]为NaN,则结果为false
Equals是为哈希表之类的东西.因此合同要求a.Equals(a).
MSDN声明:
对于Equals方法的所有实现,以下语句必须为true.在列表中,x,y和z表示非空的对象引用.
x.Equals(x)返回true,但涉及浮点类型的情况除外.参见IEC 60559:1989,微处理器系统的二进制浮点算法.
x.Equals(y)返回与y.Equals(x)相同的值.
如果x和y都是NaN,则x.Equals(y)返回true.
如果(x.Equals(y)&& y.Equals(z))返回true,则x.Equals(z)返回true.
只要未修改x和y引用的对象,对x.Equals(y)的连续调用将返回相同的值.
x.Equals(null)返回false.
有关Equals方法的其他必需行为,请参阅GetHashCode.
我发现奇怪的是它声明"x.Equals(x)返回true,除了涉及浮点类型的情况.参见IEC 60559:1989,微处理器系统的二进制浮点运算." 但同时要求NaN等于NaN.那他们为什么要把这个例外放进去呢?因为不同的NaNs?
以类似的方式使用IComparer<double>浮点标准也必须违反.因为IComparer需要一致的总排序.
如果我冒险猜测,可能是支持将double值用作字典中的键.
如果x.Equals(y)返回false的x = double.NaN和y = double.NaN,那么你可以有这样的代码:
var dict = new Dictionary<double, string>();
double x = double.NaN;
dict.Add(x, "These");
dict.Add(x, "have");
dict.Add(x, "duplicate");
dict.Add(x, "keys!");
Run Code Online (Sandbox Code Playgroud)
我认为大多数开发人员会觉得这种行为不太直观.但更直观的是:
// This would output false!
Console.WriteLine(dict.ContainsKey(x));
Run Code Online (Sandbox Code Playgroud)
基本上,的实现Equals是永远不会返回true一定值时,你必须能够提供具有以下行为怪异键的类型:
ContainsKey,因此...Remove请记住,由于这个原因Equals非常密切相关GetHashCode(C#编译器甚至会警告你,如果你已经覆盖了一个没有另一个) - 为什么他们首先在那里的很大一部分是促进类型的使用哈希表键.
就像我说的那样,这只是猜测.