比较浮点值是否相等时,有两种不同的方法:
在C#(内置的IEEE浮点类型float
和double
)遵循IEEE语义==
和!=
(和关系运算符一样<
),但确保反思object.Equals
,IEquatable<T>.Equals
(和CompareTo
)。
现在考虑一个在float
/ 之上提供矢量结构的库double
。这样的向量类型将重载==
/ !=
并覆盖object.Equals
/ IEquatable<T>.Equals
。
大家都同意==
/ !=
应该遵循IEEE语义。问题是,这样的库是否应该以Equals
自反的方式或与IEEE语义相匹配的方式来实现该方法(与相等运算符分开)。
将IEEE语义用于Equals
以下方面的参数:
- 遵循IEEE 754
(可能更快),因为它可以利用SIMD指令
我已经在stackoverflow上问了一个独立的问题,关于如何使用SIMD指令表达自反式相等性及其性能影响:用于浮点相等性比较的SIMD指令
更新:似乎可以使用三个SIMD指令有效地实现自反性相等。
Equals
涉及浮点的文档不需要反射:对于Equals(Object)方法的所有实现,以下语句必须为true。在列表中
x
,y
和z
代表对象引用不为空。x.Equals(x)
返回true
,除非涉及浮点类型。参见ISO / IEC / IEEE 60559:2011,信息技术-微处理器系统-浮点运算。如果您将浮点数用作字典键,那么您将生活在一种罪过的状态中,不应期望表现出理智的行为。
自反的观点:
这是与现有的类型,包括一致的
Single
,Double
,Tuple
和System.Numerics.Complex
。我不知道BCL中
Equals
遵循IEEE而不是反身的任何先例。相反的例子包括Single
,Double
,Tuple
和System.Numerics.Complex
。Equals
主要用于依赖自反性的容器和搜索算法。对于这些算法,如果阻止它们工作,则性能提升是无关紧要的。不要牺牲性能的正确性。- 它打破了所有基于散列集和词典,
Contains
,Find
,IndexOf
各种收藏品/ LINQ,一套基于LINQ操作(Union
,Except
,等),如果数据中包含NaN
的值。 在IEEE语义可接受的情况下执行实际计算的代码通常适用于具体类型并使用
==
/!=
(或更可能是epsilon比较)。您目前无法使用泛型编写高性能的计算,因为您需要为此进行算术运算,但是这些不能通过接口/虚拟方法使用。
因此,较慢的
Equals
方法不会影响大多数高性能代码。对于需要IEEE语义或需要提高性能的情况,可以提供一种
IeeeEquals
方法或一种方法IeeeEqualityComparer<T>
。
我认为这些论点强烈支持反身执行。
微软的CoreFX团队计划在.NET中引入这种向量类型。与我不同,他们更喜欢IEEE解决方案,主要是因为性能方面的优势。由于这样的决定在最终发布后肯定不会改变,因此我想从社区中获得我认为是一个大错误的反馈。
float
/ double
等几个类型,==
并且Equals
都已经不同。我认为与现有类型的不一致甚至比之间的不一致更令人困惑==
,Equals
您仍然需要处理其他类型。2)几乎所有通用算法/集合都使用Equals
并依赖于其对功能的自反性(LINQ和字典),而具体的浮点算法通常==
在获得IEEE语义的地方使用。
Vector<float>
一个不同于简单的float
或的“野兽” double
。通过这种方式,我看不出原因Equals
或==
运营商遵守它们的标准。您对自己说:“如果您将浮点数用作字典键,那么您将生活在一种罪过的状态中,不应期望出现理智的行为”。如果将其存储NaN
在字典中,那么使用可怕的做法是他们自己该死的错。我几乎不认为CoreFX团队没有考虑到这一点。ReflexiveEquals
为了性能起见,我会选择一个或类似的产品。
==
并且Equals
会返回不同的结果。许多程序员以为自己是,并且做同样的事情。此外,通常,相等运算符的实现会调用该Equals
方法。您认为一个可以包含一个-方法IeeeEquals
,但是也可以ReflexiveEquals
相反地包含-方法。该Vector<float>
型可被用于许多性能关键应用,并且应该相应地最优化。