将 Double.NaN 与自身进行比较

Posted

技术标签:

【中文标题】将 Double.NaN 与自身进行比较【英文标题】:Comparing Double.NaN with itself 【发布时间】:2013-01-05 16:52:27 【问题描述】:

我一直试图找出这两个操作返回不同值的原因:

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

我在第一部分有answer,但没有第二部分,也没有“为什么这两个比较返回不同的值”

【问题讨论】:

可能是一个愚蠢的评论,但我会说,在案例 1 中,您正在比较值。在案例 2 中,您正在比较参考资料。 @jbl 不,您正在比较两种情况下的值 - doubles 不是引用,您甚至没有装箱,因为 System.Double.Equals 已超载。 @spender OMFG 我应该更好地阅读这个问题!谢谢! @RB。实际上,我怀疑这可能仍然是正确的原因。我会猜到同样的事情。 这与引用/值类型无关。这是关于语义的。如果你覆盖Equals,你必须维护一个合约。看我的回答。 【参考方案1】:

造成差异的原因很简单,即使不是很明显。

如果您使用相等运算符 ==,那么您使用的是 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&lt;T,U&gt;HashSet&lt;T&gt; 等)广泛使用object.Equals(object)object.GetHashCode() 方法,并依赖于以下保证他们的行为。

【讨论】:

啊。最后。 :-) (我非常希望在这里获得索赔的正确参考,但我相信这是正确的答案(合同部分肯定是),所以+1)。 是的,我应该指出我没有参考资料。这只是对我有意义的唯一原因。 +1。您能否提供一份 IEEE 平等测试的参考资料?我在任何地方都找不到它 为了完整起见,这在 ECMA-335 I.8.2.5.2 的注释中给出了“平等是通过 Equals 方法在 System.Object 上实现的。[注意:虽然两个浮点 NaN 定义为IEC 60559:1989 始终比较为不相等,System.Object.Equals 的合同要求覆盖必须满足等价运算符的要求。因此,System.Double.Equals 和 System.Single.Equals 在比较两个 NaN 时返回 True,而在这种情况下,根据 IEC 标准的要求,等式运算符返回 False。结束注]" 出色的推理。我们在这里不链接到引用,我们创建它们。【参考方案2】:

Double.Equals的备注区最底部,你会发现:

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

【讨论】:

感谢您的快速回答。你知道为什么会有这种差异吗? 这没有回答问题的“为什么”部分。 可能是因为IsNan是一个函数所以是一个引用,那么同一个函数的两个引用是Equals (孩子的声音)“买Whyyy?” @tschmit007,这与引用/值类型无关。【参考方案3】:

嗯,Oded's answer 很好,但我想说点什么;

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

public bool Equals(double obj)

    return ((obj == this) || (IsNaN(obj) && IsNaN(this)));

既然我们有 this = Double.NaNobj = Double.NaN

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

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

相当于

return ((obj == this)true

【讨论】:

这又只是在乞求问题。我们已经知道Double.Equals对NaN有特殊处理,问题是为什么 @KonradRudolph,我在回答中解释了原因。这是因为必须维护语义契约。 @Drew 是的,这就是为什么你的答案是这里唯一真正的答案。【参考方案4】:

如果你检查 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

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

来自msdn double.equals:

如果通过调用 Equals 来测试两个 Double.NaN 值是否相等 方法,该方法返回true。但是,如果测试了两个 NaN 值 对于使用相等运算符的相等,运算符返回 错误的。当你想判断一个 Double 的值是否不是 一个数字 (NaN),另一种方法是调用 IsNaN 方法。

这是为了符合IEC 60559:1989而故意这样做的;

根据 IEC 60559:1989,两个浮点数的值为 NaN 永远不相等。但是,根据 System.Object::Equals 的规范 方法,最好重写此方法以提供值 相等语义。由于 System.ValueType 提供了这个 通过使用反射的功能,描述为 Object.Equals 特别指出值类型应该考虑 覆盖默认的 ValueType 实现以获得性能 增加。其实从源头上看 System.ValueType::Equals(clr\src\BCL\System\ValueType.cs 的第 36 行 在 SSCLI 中),甚至有来自 CLR Perf 团队的评论 System.ValueType::Equals 不快的效果。

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

【讨论】:

问题是为什么这两种方法会产生不同的结果。你没有回答。 我相信我已经回答了; NaN 没有定义,不代表任何数字,它是未定义的。谈论平等是不可能的;但是 equals 方法定义了两个 NaN 值之间的相等性。 为什么 Equals 是这样定义的?你只是begging the question。 您阅读了进一步的更新吗?如果您还有其他问题,我可以回答。请阅读我最后发布的链接。 @daryal:如果== 运算符应该回答“X 是否应该被认为与 Y 无法区分”的问题,那么如果 X 和 Y 都是 NaN,则该问题的答案应该是正确的。我不太确定== 可以用 IEEE 语义回答什么有用的问题,因为两个浮点变量具有相同的标称值这一事实并不意味着它们都代表相同的数量——仅仅是代表的数量无法区分。

以上是关于将 Double.NaN 与自身进行比较的主要内容,如果未能解决你的问题,请参考以下文章

比较 Scala 和 Java Double.NaN

Scala Spark 在数据帧和数据集中以不同方式处理 Double.NaN

Linq Orderby 与自身进行比较。为啥?

与 NaN 不同,为啥浮点无穷大是相等的?

比较变量与自身

将列表与 DataFrame 中的每条记录进行比较