提问人:illegal-immigrant 提问时间:2/8/2011 最后编辑:illegal-immigrant 更新时间:4/16/2019 访问量:3358
c# NaN Equals() 和 == 之间的比较差异
c# NaN comparison differences between Equals() and ==
问:
看看这个:
var a = Double.NaN;
Console.WriteLine(a == a);
Console.ReadKey();
打印“False”
var a = Double.NaN;
Console.WriteLine(a.Equals(a));
Console.ReadKey();
打印“真实”!
为什么打印“True”?由于浮点数规范,NaN 的值不等于自身!所以似乎 Equals() 方法实现错误...... 我错过了什么吗?
答:
虽然你是对的,但这是错误的,特别是以不同的方式处理,这是真的。下面是 reflector 中该方法的 .NET 4 实现:NaN == NaN
double.Equals
NaN
NaN.Equals(NaN)
public bool Equals(double obj)
{
return ((obj == this) || (IsNaN(obj) && IsNaN(this)));
}
如果我冒昧地猜测,这可能是为了支持在字典中使用值作为键。double
如果返回 for 和 ,则可以有如下代码: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!");
我认为大多数开发人员会发现这种行为相当不直观。但更违反直觉的是:
// This would output false!
Console.WriteLine(dict.ContainsKey(x));
基本上,对于某个值的实现,您将拥有一种能够为键提供以下奇怪行为的类型:Equals
true
- 可以无限次添加到字典中
- 无法使用 检测到 ,因此...
ContainsKey
- 永远无法使用
Remove
请记住,这与这个原因密切相关(如果您重写了一个而没有另一个,C# 编译器甚至会警告您)——它们首先存在的很大一部分原因是为了方便将类型用作哈希表键。Equals
GetHashCode
就像我说的,这只是一个猜测。
我找到了一篇关于您的问题的文章:.NET 安全博客:为什么 == 和 Equals 方法为浮点值返回不同的结果
根据 IEC 60559:1989,两个 值为 NaN 永远不会相等。然而 根据规范 System.Object::Equals 方法,它是 希望将此方法覆盖为 提供价值相等语义。 [...]
所以现在我们有两个相互矛盾的想法 等于应该意味着什么。 Object::Equals 表示 BCL 值 类型应重写以提供值 平等,IEC 60559 说 NaN 不等于 NaN。分区 I 的 ECMA 规范提供了以下分辨率 通过记下关于 第 8.2.5.2 节中的此特定情况 [下文]
更新:CLI 规范 (ECMA-335) 第 8.2.5 节的全文对此进行了更多说明。我在这里复制了相关位:
8.2.5 价值认同和平等
定义了两个二进制运算符 在所有价值观对上:身份和平等。它们返回布尔值结果,并且是数学等价运算符;也就是说,它们是:
- 反身性——是真的。
a op a
- 对称 – 当且仅当为 true 时为 true。
a op b
b op a
- 传递 – 如果为 true 并且是 true,则为 真。
a op b
b op c
a op c
此外,虽然身份总是 意味着平等,反之则不然 真。[...]
8.2.5.1 身份
标识运算符由 CTS 定义如下。
- 如果值具有不同的确切类型,则它们不相同。
- 否则,如果它们的确切类型是值类型,则它们在 并且仅当 值是相同的,一点一点地。
- 否则,如果它们的确切类型是引用类型,则它们是 当且仅当位置相同时 的值是相同的。
Identity 是通过该方法实现的。
System.Object
ReferenceEquals
8.2.5.2 平等
对于值类型,相等运算符 是确切定义的一部分 类型。平等的定义应 请遵守以下规则:
- 如上所述,相等应为等价运算符。
- 如前所述,身份应该意味着平等。
- 如果任一(或两者)操作数是装箱值,则 [...]
相等是通过该方法实现的。
System.Object
Equals
[注意:虽然两个浮点数 NaN 由 IEC 60559:1989 定义 总是比较为不等,, System.Object.Equals 的协定 要求替代必须满足 等同性的要求 算子。因此,并返回 True 在比较两个 NaN 时,而 相等运算符返回 False 根据IEC的要求,这种情况 标准。结束语
System.Double.Equals
System.Single.Equals
]
以上根本没有指定运算符的属性(最后一个注释除外);它主要定义 和 的行为。对于运算符的行为,C# 语言规范 (ECMA-334) (第 14.9.2 节) 清楚地说明了如何处理 NaN 值:==
ReferenceEquals
Equals
==
如果任一操作数 [to ] 为 NaN,则结果为 false
operator ==
评论
Object.Equals
double.NaN
double.NaN
Equals
是为哈希表之类的东西而制作的。因此,它的合同要求.a.Equals(a)
MSDN 指出:
对于 Equals 方法的所有实现,以下语句必须为 true。在列表中,x、y 和 z 表示不为 null 的对象引用。
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。
请参阅 GetHashCode,了解与 Equals 方法相关的其他必需行为。
我觉得奇怪的是,它指出“x.Equals(x) 返回 true,除非涉及浮点类型。参见 IEC 60559:1989,微处理器系统的二进制浮点算术“,但同时要求 NaN 等于 NaN。那么,他们为什么要把这个例外放进去呢?因为不同的NaN?
以类似的方式,当使用浮点标准时,也必须违反浮点标准。由于需要一致的总排序。 IComparer<double>
IComparer
评论
x==x
x
x
NaN
评论