比较Double.NaN和自己

我被困在试图找出为什么这两个操作返回不同的值:

  1. Double.NaN == Double.NaN返回false
  2. Double.NaN.Equals(Double.NaN)返回true

我有第一部分的答案 ,但不是第二部分,而不是“为什么这两个比较返回不同的值”

这个差别的原因很简单,如果不是很明显的话。

如果您使用相等运算符== ,那么您使用IEEEtesting的相等性。

如果你使用Equals(object)方法,那么你必须维护object.Equals(object)的约定。 当你实现这个方法(和相应的GetHashCode方法)时,你必须维护那个不同于IEEE行为的契约。

如果Equals合同没有得到维护,那么散列表的行为就会中断。

 var map = new Dictionary<double,string>(); map[double.NaN] = "NaN"; var s = map[double.NaN]; 

如果!double.NaN.Equals(double.NaN) ,你永远不会从字典中得到你的价值!

如果前面的句子没有意义,那么理解散列的机制(在Dictionary<T,U>HashSet<T>等中使用object.GetHashCode()广泛地使用object.GetHashCode() object.Equals(object)object.GetHashCode()并依靠其行为的保证。

Double.Equals备注部分的Double.Equals ,你会发现:

如果通过调用Equals方法testing两个Double.NaN值是否相等,则该方法返回true。 但是,如果使用相等运算符testing两个NaN值是否相等,则运算符返回false。 当你想确定Double的值是不是一个数(NaN)时,另一种方法是调用IsNaN方法。

那么, Oded的回答很好,但我想说点什么。

当我反编译Double.Equals()方法,看起来像这样;

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

所以既然我们有这个= Double.NaNobj = Double.NaN

 (IsNaN(obj)) and (IsNaN(this)) returns `true`. 

所以基本上它可以return ((obj == this) || true

这是等价的

return ((obj == this)true

如果你检查Double.NaN;

  // Summary: // Represents a value that is not a number (NaN). This field is constant. public const double NaN = 0.0 / 0.0; 

第一个返回false,因为NaN不代表任何数字。

当操作的结果未定义时,方法或操作符返回NaN。 例如,零除零的结果是NaN

第二个返回true,因为NaN相等是在重载的equals方法中显式实现的。

来自msdn double.equals :

如果通过调用Equals方法testing两个Double.NaN值是否相等,则该方法返回true。 但是,如果使用相等运算符testing两个NaN值是否相等,则运算符返回false。 当你想确定Double的值是不是一个数(NaN)时,另一种方法是调用IsNaN方法。

这是完全符合IEC 60559:1989 ;

根据IEC 60559:1989,有两个NaN值的浮点数永远不会相等。但是根据System.Object :: Equals方法的规范,可以重写这个方法来提供值相等的语义。 由于System.ValueType通过使用Reflection来提供此function,因此Object.Equals的说明特别指出值types应考虑覆盖默认的ValueType实现以获得性能提升。 实际上,从查看System.ValueType :: Equals(SSCLI中的clr \ src \ BCL \ System \ ValueType.cs的第36行)的源代码,甚至还有来自CLR Perf团队对System.ValueType ::等于不快。

请参阅: http : //blogs.msdn.com/b/shawnfa/archive/2004/07/19/187792.aspx