我一直在试图找出为什么这两个操作返回不同的值:
Double.NaN == Double.NaN
false
Double.NaN.Equals(Double.NaN)
true
7gcisfzg1#
差异的原因很简单,如果不是显而易见的话。如果使用相等运算符==,则使用IEEE相等性测试。如果你使用的是Equals(object)方法,那么你必须维护object.Equals(object)的约定。当你实现这个方法(和对应的GetHashCode方法)时,你必须维护那个约定,这与IEEE的行为不同。如果不支持Equals协定,那么哈希表的行为将被破坏。
==
Equals(object)
object.Equals(object)
GetHashCode
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.NaN.Equals(double.NaN)
Dictionary<T,U>
HashSet<T>
object.GetHashCode()
czfnxgou2#
在Double.Equals的备注部分的最底部,您将找到:如果通过调用Equals方法来测试两个Double.NaN值是否相等,则该方法返回true。但是,如果通过使用相等运算符来测试两个NaN值是否相等,则该运算符返回false。如果要确定Double的值是否不是数字(NaN),另一种方法是调用IsNaN方法。
Double.Equals
ego6inou3#
好吧,Oded's answer是伟大的,但我想说的是;当我反编译Double.Equals()方法时,它看起来是这样的;
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其相当于就是true。
return ((obj == this) || true
9lowa7mx4#
如果检查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方法来测试两个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::Equals速度不快进行了注解。请参阅:http://blogs.msdn.com/b/shawnfa/archive/2004/07/19/187792.aspx
NaN
equals
IEC 60559:1989
b4qexyjb5#
我会避免这两种操作中的任何一种。根据Microsoft文档中有关代码分析的使用规则,规则CA 2242(TestforNaN correctly)表示“要修复此规则的冲突并准确确定某个值是否表示System.Double.NaN,使用System.Single.IsNaN或System.Double.IsNaN来测试该值。"。因此,我建议您使用double.IsNaN(double.NaN)&& double.IsNaN(double.NaN),它总是返回true。这个例子虽然没有意义,但却是正确的。我建议你避免比较双精度数是否相等。舍入错误可能导致非常小的差异,但这些差异足以破坏相等性。当比较名为a和b的两个双精度数时,我会使用如下代码:如果(Math.Abs(a-b)〈1 e-20)。也许这没有回答这个问题,但我认为它需要说出来。
5条答案
按热度按时间7gcisfzg1#
差异的原因很简单,如果不是显而易见的话。
如果使用相等运算符
==
,则使用IEEE相等性测试。如果你使用的是
Equals(object)
方法,那么你必须维护object.Equals(object)
的约定。当你实现这个方法(和对应的GetHashCode
方法)时,你必须维护那个约定,这与IEEE的行为不同。如果不支持
Equals
协定,那么哈希表的行为将被破坏。如果是
!double.NaN.Equals(double.NaN)
,你就永远不会从字典中得到你的值!如果前面的句子没有意义,那么请理解散列机制(用于
Dictionary<T,U>
、HashSet<T>
等)广泛使用object.Equals(object)
和object.GetHashCode()
方法,并依赖于它们行为的保证。czfnxgou2#
在
Double.Equals
的备注部分的最底部,您将找到:如果通过调用Equals方法来测试两个Double.NaN值是否相等,则该方法返回true。但是,如果通过使用相等运算符来测试两个NaN值是否相等,则该运算符返回false。如果要确定Double的值是否不是数字(NaN),另一种方法是调用IsNaN方法。
ego6inou3#
好吧,Oded's answer是伟大的,但我想说的是;
当我反编译
Double.Equals()
方法时,它看起来是这样的;既然我们有 this = Double.NaN 和 obj = Double.NaN
所以基本上是
return ((obj == this) || true
其相当于
就是
true
。9lowa7mx4#
如果检查Double.NaN;
第一个函数返回false,因为NaN不表示任何数字。
当运算结果未定义时,方法或运算符返回NaN。例如,零除以零的结果为NaN
第二个函数返回true,因为
NaN
等式在重载的equals
方法中显式实现。从msdn double.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::Equals速度不快进行了注解。
请参阅:http://blogs.msdn.com/b/shawnfa/archive/2004/07/19/187792.aspx
b4qexyjb5#
我会避免这两种操作中的任何一种。根据Microsoft文档中有关代码分析的使用规则,规则CA 2242(TestforNaN correctly)表示“要修复此规则的冲突并准确确定某个值是否表示System.Double.NaN,使用System.Single.IsNaN或System.Double.IsNaN来测试该值。"。因此,我建议您使用double.IsNaN(double.NaN)&& double.IsNaN(double.NaN),它总是返回true。这个例子虽然没有意义,但却是正确的。我建议你避免比较双精度数是否相等。舍入错误可能导致非常小的差异,但这些差异足以破坏相等性。当比较名为a和b的两个双精度数时,我会使用如下代码:如果(Math.Abs(a-b)〈1 e-20)。也许这没有回答这个问题,但我认为它需要说出来。