浮点相等比较的SIMD指令(NaN == NaN)

时间:2016-01-22 16:41:10

标签: assembly floating-point x86 x86-64 simd

哪些指令用于比较由4 * 32位浮点值组成的两个128位向量?

是否有指令认为双方的NaN值相等?如果不是,提供反身性的变通方(即NaN等于NaN)对性能的影响有多大?

我听说,与IEEE语义相比,确保反身性会产生显着的性能影响,因为NaN并不等于自己,而且我想知道这种影响是否会很大。

我知道您在处理浮点值时通常需要使用epsilon比较而不是精确的质量。但是这个问题是关于完全相等的比较,你可以用它来消除哈希集中的重复值。

要求

  • +0-0必须相等。
  • NaN必须与自己相等。
  • NaN的不同表示应该相等,但如果性能影响太大,可能会牺牲这一要求。
  • 如果两个向量中的所有四个float元素相同,则结果应为布尔值true,如果至少有一个元素不同,则为false。其中true由标量整数1表示,false0表示。

测试用例

(NaN, 0, 0, 0) == (NaN, 0, 0, 0) // for all representations of NaN
(-0,  0, 0, 0) == (+0,  0, 0, 0) // equal despite different bitwise representations
(1,   0, 0, 0) == (1,   0, 0, 0)
(0,   0, 0, 0) != (1,   0, 0, 0) // at least one different element => not equal 
(1,   0, 0, 0) != (0,   0, 0, 0)

我实现此想法的想法

我认为可以使用NotLessThan组合两个CMPNLTPS比较(and?)来获得所需的结果。汇编程序等效于AllTrue(!(x < y) and !(y < x))AllFalse((x < y) or (y > x)

背景

这个问题的背景是微软计划向.NET添加Vector类型。我正在争论一种反身.Equals方法,并且需要更清楚地了解这种反身性能对IEEE平等的性能影响有多大。有关长篇故事,请参阅programmers.se上的Should Vector<float>.Equals be reflexive or should it follow IEEE 754 semantics?

1 个答案:

答案 0 :(得分:3)

这是一种可能的解决方案 - 但效率不高,需要6条指令:

__m128 v0, v1; // float vectors

__m128 v0nan = _mm_cmpeq_ps(v0, v0);                   // test v0 for NaNs
__m128 v1nan = _mm_cmpeq_ps(v1, v1);                   // test v1 for NaNs
__m128 vnan = _mm_or_si128(v0nan, v1nan);              // combine
__m128 vcmp = _mm_cmpneq_ps(v0, v1);                   // compare floats
vcmp = _mm_and_si128(vcmp, vnan);                      // combine NaN test
bool cmp = _mm_testz_si128(vcmp, vcmp);                // return true if all equal

请注意,上面的所有逻辑都是反转的,这可能会使代码难以理解(OR s实际上是AND s,而反之亦然)。