简体   繁体   English

c#NaN比较Equals()和==之间的差异

[英]c# NaN comparison differences between Equals() and ==

Check this out : 看一下这个 :

    var a = Double.NaN;

    Console.WriteLine(a == a);
    Console.ReadKey();

Prints "False" 打印“假”

    var a = Double.NaN;

    Console.WriteLine(a.Equals(a));
    Console.ReadKey();

Prints "True"! 打印“真实”!

Why it prints "True"? 为什么打印“真实”? Due to floating point numbers specification, value that is NaN is not equal to itself! 由于浮点数规范,NaN的值不等于它自己! So it seems that Equals() method is implemented wrong... Am I missing something ? 所以似乎Equals()方法实现错误......我错过了什么?

I found an article addressing your question: .NET Security Blog: Why == and the Equals Method Return Different Results for Floating Point Values 我发现了一篇解决您问题的文章: .NET安全博客:为什么==和等于方法返回浮点值的不同结果

According to IEC 60559:1989, two floating point numbers with values of NaN are never equal. 根据IEC 60559:1989,具有NaN值的两个浮点数永远不相等。 However, according to the specification for the System.Object::Equals method, it's desirable to override this method to provide value equality semantics. 但是,根据System.Object :: Equals方法的规范,需要重写此方法以提供值相等语义。 [...] [...]

So now we have two conflicting ideas of what Equals should mean. 所以现在我们对Equals应该意味着什么有两个相互矛盾的想法。 Object::Equals says that the BCL value types should override to provide value equality, and IEC 60559 says that NaN does not equal NaN. Object :: Equals表示BCL值类型应覆盖以提供值相等,而IEC 60559表示NaN不等于NaN。 Partition I of the ECMA spec provides resolution for this conflict by making a note about this specific case in section 8.2.5.2 [below] ECMA规范的分区I通过在8.2.5.2节[下面]中记录这个特定情况来解决这个冲突。


Update: The full text of section 8.2.5 from the CLI spec (ECMA-335) sheds some more light on this. 更新: CLI规范(ECMA-335)中 8.2.5节的全文对此有了更多的了解。 I've copied the relevant bits here: 我在这里复制了相关的部分:

8.2.5 Identity and equality of values 8.2.5价值的同一性和平等性

There are two binary operators defined on all pairs of values: identity and equality . 在所有值对上定义了两个二元运算符: identityequality They return a Boolean result, and are mathematical equivalence operators ; 它们返回一个布尔结果,是数学等价运算符 ; that is, they are: 也就是说,它们是:

  • Reflexive – a op a is true. 反身 - a op a是真的。
  • Symmetric – a op b is true if and only if b op a is true. 对称 - 当且仅当b op a为真时, a op b才为真。
  • Transitive – if a op b is true and b op c is true, then a op c is true. 传递 - 如果a op b为真且b op c为真,则a op c为真。

In addition, while identity always implies equality, the reverse is not true. 此外,虽然身份总是意味着平等,但事实并非如此。 [...] [...]

8.2.5.1 Identity 8.2.5.1身份

The identity operator is defined by the CTS as follows. 身份运营商由CTS定义如下。

  • If the values have different exact types, then they are not identical. 如果值具有不同的确切类型,则它们不相同。
  • Otherwise, if their exact type is a value type, then they are identical if and only if the bit sequences of the values are the same, bit by bit. 否则,如果它们的确切类型是值类型,那么它们是相同的,当且仅当值的位序列是相同的时,才是逐位的。
  • Otherwise, if their exact type is a reference type, then they are identical if and only if the locations of the values are the same. 否则,如果它们的确切类型是引用类型,那么它们是相同的,当且仅当值的位置相同时。

Identity is implemented on System.Object via the ReferenceEquals method. 通过ReferenceEquals方法在System.Object上实现标识。

8.2.5.2 Equality 8.2.5.2平等

For value types, the equality operator is part of the definition of the exact type. 对于值类型,等于运算符是精确类型定义的一部分。 Definitions of equality should obey the following rules: 等式的定义应遵守以下规则:

  • Equality should be an equivalence operator, as defined above. 如上所述,平等应该是等价运算符。
  • Identity should imply equality, as stated earlier. 如前所述,身份应该意味着平等。
  • If either (or both) operand is a boxed value, [...] 如果其中一个(或两个)操作数是一个盒装值,[...]

Equality is implemented on System.Object via the Equals method. 通过Equals方法在System.Object上实现Equality。

[ Note : Although two floating point NaNs are defined by IEC 60559:1989 to always compare as unequal, the contract for System.Object.Equals requires that overrides must satisfy the requirements for an equivalence operator. [ 注意 :尽管IEC 60559:1989定义了两个浮点NaN以始终比较为不等,但System.Object.Equals的合同要求覆盖必须满足等价运算符的要求。 Therefore, System.Double.Equals and System.Single.Equals return True when comparing two NaNs, while the equality operator returns False in that case, as required by the IEC standard. 因此, System.Double.EqualsSystem.Single.Equals在比较两个NaN时返回True,而在这种情况下,相等运算符返回False,符合IEC标准的要求。 end note ] 结束说明 ]

The above does not specify the properties of the == operator at all (except for the final note); 以上内容并未指定==运算符的属性(最终注释除外); it is primarily defining the behavior of ReferenceEquals and Equals . 它主要定义ReferenceEqualsEquals的行为。 For the behavior of the == operator, the C# language spec (ECMA-334) (section 14.9.2) is clear about how to treat NaN values: 对于==运算符的行为, C#语言规范(ECMA-334) (第14.9.2节)明确了如何处理NaN值:

If either operand [to operator == ] is NaN, the result is false 如果操作数[to operator == ]是NaN,则结果为false

Equals is made for things like hashtables. Equals哈希表之类的东西。 And thus it contract requires that a.Equals(a) . 因此它的合同要求a.Equals(a)

MSDN states: MSDN声明:

The following statements must be true for all implementations of the Equals method. 对于Equals方法的所有实现,以下语句必须为true。 In the list, x, y, and z represent object references that are not null. 在列表中,x,y和z表示非空的对象引用。

x.Equals(x) returns true, except in cases that involve floating-point types. x.Equals(x)返回true,但涉及浮点类型的情况除外。 See IEC 60559:1989, Binary Floating-point Arithmetic for Microprocessor Systems. 参见IEC 60559:1989,微处理器系统的二进制浮点算法。

x.Equals(y) returns the same value as y.Equals(x). x.Equals(y)返回与y.Equals(x)相同的值。

x.Equals(y) returns true if both x and y are NaN. 如果x和y都是NaN,则x.Equals(y)返回true。

If (x.Equals(y) && y.Equals(z)) returns true, then x.Equals(z) returns true. 如果(x.Equals(y)&& y.Equals(z))返回true,则x.Equals(z)返回true。

Successive calls to x.Equals(y) return the same value as long as the objects referenced by x and y are not modified. 只要未修改x和y引用的对象,对x.Equals(y)的连续调用将返回相同的值。

x.Equals(null) returns false. x.Equals(null)返回false。

See GetHashCode for additional required behaviors pertaining to the Equals method. 有关Equals方法的其他必需行为,请参阅GetHashCode。

What I find strange is that it states "x.Equals(x) returns true, except in cases that involve floating-point types. See IEC 60559:1989, Binary Floating-point Arithmetic for Microprocessor Systems." 我发现奇怪的是它声明“x.Equals(x)返回true,除了涉及浮点类型的情况。参见IEC 60559:1989,微处理器系统的二进制浮点运算。” but at the same time requires that NaN equals NaN. 但同时要求NaN等于NaN。 So why did they put that exception in? 那他们为什么要把这个例外放进去呢? Because of different NaNs? 因为不同的NaNs?

In a similar way when using an IComparer<double> the floating-point standard must be violated too. 以类似的方式使用IComparer<double> ,也必须违反浮点标准。 Since IComparer requires a consistent total ordering. 由于IComparer需要一致的总排序。

If I were to venture a guess, it might be that this is to support the use of double values as keys in a dictionary. 如果我冒险猜测,可能是支持在字典中使用double值作为键。

If x.Equals(y) returned false for x = double.NaN and y = double.NaN , then you could have code like this: 如果x.Equals(y)x = double.NaNy = double.NaN返回false ,那么你可以得到如下代码:

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!");

I think the majority of developers would find this behavior rather unintuitive. 我认为大多数开发人员会觉得这种行为不太直观。 But even more counterintuitive would be this: 更加违反直觉的是:

// This would output false!
Console.WriteLine(dict.ContainsKey(x));

Basically, with an implementation of Equals that never returns true for a certain value, what you would have is a type capable of providing keys with the following bizarre behavior: 基本上,对于某个值永远不会返回trueEquals实现,您将拥有一个能够为键提供以下奇怪行为的类型:

  • Could be added an unlimited number of times to a dictionary 可以无限次地添加到字典中
  • Could not be detected using ContainsKey , and therefore... 无法使用被检测到ContainsKey ,因此...
  • Could never be removed using Remove 永远不能使用Remove

Remember that Equals is very closely related to GetHashCode for this very reason (the C# compiler even warns you if you've overridden one without the other)—a big part of why they're there in the first place is to facilitate the use of types as hash table keys. 请记住,由于这个原因, EqualsGetHashCode密切相关(C#编译器甚至会警告你,如果你已经覆盖了一个而没有另一个) - 为什么他们首先在那里的很大一部分是为了方便使用类型作为哈希表键。

Like I said, it's just a guess. 就像我说的那样,这只是猜测。

While you're correct that NaN == NaN is false, double.Equals specially handles NaN differently, in a way that NaN.Equals(NaN) is true. 虽然NaN == NaN为假是正确的, double.EqualsNaN.Equals(NaN)为真的方式特别处理NaN Here's the .NET 4 implementation of the method from reflector: 这是反射器方法的.NET 4实现:

public bool Equals(double obj)
{
    return ((obj == this) || (IsNaN(obj) && IsNaN(this)));
}

Check out this link for more information on when to use == 's or Equals . 有关何时使用==Equals详细信息,请查看此链接 Written by the illustrious leader Jon Skeet. 由杰出的领导人Jon Skeet撰写。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM