我一直试图找出这两个操作返回不同值的原因:
Double.NaN == Double.NaN
返回false
Double.NaN.Equals(Double.NaN)
返回true
我有第一部分的答案,但没有第二部分的答案,也没有“为什么这两个比较返回不同的值”的答案
造成这种差异的原因很简单,即使不是很明显。
如果您使用相等运算符==
,那么您正在使用 IEEE 相等性测试。
如果您使用该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.Equals(object)
和object.GetHashCode()
方法,并依赖于对其行为的保证。
在备注部分的最底部Double.Equals
,您会发现:
如果通过调用 Equals 方法测试两个 Double.NaN 值是否相等,则该方法返回 true。但是,如果使用相等运算符测试两个 NaN 值是否相等,则运算符返回 false。当您想要确定 Double 的值是否不是数字 (NaN) 时,另一种方法是调用 IsNaN 方法。
如果你检查 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
如果通过调用 Equals 方法测试两个 Double.NaN 值是否相等,则该方法返回 true。但是,如果使用相等运算符测试两个 NaN 值是否相等,则运算符返回 false。当您想要确定 Double 的值是否不是数字 (NaN) 时,另一种方法是调用 IsNaN 方法。
故意这样做是为了符合IEC 60559:1989
;
根据 IEC 60559:1989,具有 NaN 值的两个浮点数永远不会相等。但是,根据 System.Object::Equals 方法的规范,最好覆盖此方法以提供值相等语义。由于 System.ValueType 通过使用反射来提供此功能,因此 Object.Equals 的描述明确指出,值类型应考虑覆盖默认的 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
好吧,Oded 的回答很好,但我想说点什么;
当我反编译Double.Equals()
方法时,它看起来像这样;
public bool Equals(double obj)
{
return ((obj == this) || (IsNaN(obj) && IsNaN(this)));
}
所以既然我们有this = Double.NaN和obj = Double.NaN
(IsNaN(obj)) and (IsNaN(this)) returns `true`.
所以基本上可以return ((obj == this) || true
这相当于
return ((obj == this)
是true
。