簡體   English   中英

浮點相等比較的SIMD指令(NaN == NaN)

[英]SIMD instructions for floating point equality comparison (with NaN == NaN)

哪些指令用於比較由4 * 32位浮點值組成的兩個128位向量?

是否存在將雙方的NaN值視為相等的指令? 如果不是,提供反身性的解決方案(即NaN等於NaN)的性能影響有多大?

我聽說,與IEEE語義相比,確保反身性會產生顯着的性能影響,因為NaN不等於自己,我想知道這種影響是否會很大。

我知道您在處理浮點值時通常需要使用epsilon比較而不是精確的質量。 但是這個問題是關於完全相等的比較,例如,您可以使用它來消除哈希集中的重復值。

要求

  • +0-0必須比較相等。
  • NaN必須與自身相等。
  • NaN的不同表示應該相等,但如果性能影響太大,可能會犧牲該要求。
  • 結果應該是一個布爾值, true如果所有四個浮動元件在兩種載體相同,並且如果至少一個元件不同假。 其中true由標量整數1false0

測試用例

(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)

我實現這一點的想法

我認為可以使用and組合兩個NotLessThan比較( CMPNLTPS ?)來實現所需的結果。 匯編程序等效於AllTrue(!(x < y) and !(y < x))AllFalse((x < y) or (y > x)

背景

這個問題的背景是微軟計划向.NET添加Vector類型。 我正在爭論一種反身的.Equals方法,並且需要更清晰地了解這種反身性能對IEEE等於.Equals性的影響。 請參閱Vector<float>.Equals是自反的還是應該遵循IEEE 754語義? 在程序員。長期的故事。

即使AVX VCMPPS(它具有極大增強的謂詞選擇)也沒有為我們提供單指令謂詞。 您必須至少進行兩次比較並合並結果。 不過,這並不算太糟糕。

  • 不同的NaN編碼相等:實際上有2個額外的insn(增加2個uop)。 沒有AVX:超出一個額外的movaps

  • 不同的NaN編碼相同的:有效4個額外的insn(增加4個uops)。 沒有AVX:兩個額外的movaps insn

IEEE比較和分支是3 cmpeqpscmpeqps / movmskps / test-and-branch。 英特爾和AMD都將測試和分支宏觀融合為單個uop / m-op。

使用AVX512:bitwise-NaN可能只是一個額外的指令,因為正常的向量比較和分支可能使用vcmpEQ_OQps / ktest same,same / jcc ,因此組合兩個不同的掩碼regs是免費的(只需將args更改為ktest )。 唯一的成本是額外的vpcmpeqd k2, xmm0,xmm1

AVX512 any-NaN只是兩個額外的指令(2x VFPCLASSPS ,第二個使用第一個作為零掩碼的結果。見下文)。 再次,然后ktest與兩個不同的args設置標志。


到目前為止我最好的主意: ieee_equal || bitwise_equal ieee_equal || bitwise_equal

如果我們放棄考慮相同的不同NaN編碼:

  • 按位相等可以捕獲兩個相同的NaN。
  • IEEE等於+0 == -0情況。

沒有任何一種比較給出誤報的情況(因為當任一操作數是NaN時ieee_equal為false:我們只需要相等,不等於或無序vcmpps提供兩個選項,而SSE僅提供明顯相等的操作。)

我們想知道所有元素何時相等,所以我們應該從反向比較開始。 檢查至少一個非零元素比檢查所有非零元素更容易。 (即水平AND很難,水平OR很容易( pmovmskb / testptest )。相反的比較是免費的( jnz而不是jz )。)這與Paul R使用的技巧相同。

; inputs in xmm0, xmm1
movaps    xmm2, xmm0    ; unneeded with 3-operand AVX instructions

cmpneqps  xmm2, xmm1    ; 0:A and B are ordered and equal.  -1:not ieee_equal.  predicate=NEQ_UQ in VEX encoding expanded notation
pcmpeqd   xmm0, xmm1    ; -1:bitwise equal  0:otherwise

; xmm0   xmm2
;   0      0   -> equal   (ieee_equal only)
;   0     -1   -> unequal (neither)
;  -1      0   -> equal   (bitwise equal and ieee_equal)
;  -1     -1   -> equal   (bitwise equal only: only happens when both are NaN)

andnps    xmm0, xmm2    ; NOT(xmm0) AND xmm2
; xmm0 elements are -1 where  (not bitwise equal) AND (not IEEE equal).
; xmm0 all-zero iff every element was bitwise or IEEE equal, or both
movmskps  eax, xmm0
test      eax, eax      ; it's too bad movmsk doesn't set EFLAGS according to the result
jz no_differences

對於雙精度, ...PSpcmpeqQ將工作相同。

如果不相等的代碼繼續找出哪個元素不相等,則movmskps結果上的位掃描將給出第一個差異的位置。

使用SSE4.1 PTEST您可以使用andnps movmskps替換andnps / movmskps / test-and-branch:

ptest    xmm0, xmm2   ; CF =  0 == (NOT(xmm0) AND xmm2).
jc no_differences

我希望這是大多數人第一次看到PTESTCF結果對任何事情PTEST用。 :)

英特爾和AMD CPU((2ptest + 1jcc)vs(pandn + movmsk + fuse-test&branch))仍然是三個uops,但指令更少。 如果你要高效setcccmovcc代替jcc ,因為那些不能宏觀保險絲test

對於自反比較和分支,總共有6個uop(5個AVX),而IEEE比較和分支則為3個uop cmpeqps / movmskps / test-and-branch。)

PTEST在AMD Bulldozer系列CPU上的延遲非常高( 在Steamroller上為14c )。 它們有一個由兩個整數核共享的向量執行單元集群。 (這是超線程的替代方法。)這增加了可以檢測到分支錯誤預測的時間,或者數據依賴鏈的延遲( cmovcc / setcc )。

0==(xmm0 AND xmm2)時,PTEST設置ZF :如果沒有元素同時是bitwise_equal和IEEE(neq或unordered),則設置。 即如果任何元素是bitwise_equal同時也是!ieee_equal則取消設置ZF。 這只能在一對元素包含按位相等的NaN時發生(但是當其他元素不相等時可能會發生)。

    movaps    xmm2, xmm0
    cmpneqps  xmm2, xmm1    ; 0:A and B are ordered and equal.
    pcmpeqd   xmm0, xmm1    ; -1:bitwise equal

    ptest    xmm0, xmm2
    jc   equal_reflexive   ; other cases

...

equal_reflexive:
    setnz  dl               ; set if at least one both-nan element

沒有條件測試CF=1和任何關於ZF事情。 ja測試CF=0 and ZF=1 這是不可能的,你仍要測試,所以把一個jnzjc分支目標工作正常。 (如果你只想測試equal_reflexiveat_least_one_nan ,不同的設置可能會適當地設置標志)。


考慮到所有NaN都相等,即使不是按位相等:

這與Paul R的回答是一樣的,但是有一個錯誤修正(使用AND而不是OR將NaN檢查與IEEE檢查結合起來。)

; inputs in xmm0, xmm1
movaps      xmm2, xmm0
cmpordps    xmm2, xmm2      ; find NaNs in A.  (0: NaN.  -1: anything else).  Same as cmpeqps since src and dest are the same.
movaps      xmm3, xmm1
cmpordps    xmm3, xmm3      ; find NaNs in B
orps        xmm2, xmm3      ; 0:A and B are both NaN.  -1:anything else

cmpneqps    xmm0, xmm1      ; 0:IEEE equal (and ordered).  -1:unequal or unordered
; xmm0 AND xmm2  is zero where elements are IEEE equal, or both NaN
; xmm0   xmm2 
;   0      0     -> equal   (ieee_equal and both NaN (impossible))
;   0     -1     -> equal   (ieee_equal)
;  -1      0     -> equal   (both NaN)
;  -1     -1     -> unequal (neither equality condition)

ptest    xmm0, xmm2        ; ZF=  0 == (xmm0 AND xmm2).  Set if no differences in any element
jz   equal_reflexive
; else at least one element was unequal

;     alternative to PTEST:  andps  xmm0, xmm2 / movmskps / test / jz

所以在這種情況下我們PTEST不需要PTESTCF結果。 我們在使用PCMPEQDPCMPEQD ,因為它沒有反轉( cmpunordpscmpordps的方式)。

用於Intel SnB系列CPU的9個融合域uops。 (7使用AVX:使用非破壞性3操作數指令來避免movaps 。)但是,Skylake之前的SnB系列CPU只能在p1上運行cmpps ,因此如果吞吐量受到關注,則FP-add單元會出現這種瓶頸。 Skylake在p0 / p1上運行cmpps

andps比更短的編碼pand ,和英特爾CPU從Nehalem處理器到Broadwell微架構只能在PORT5運行它。 可能需要防止它從周圍的FP代碼中竊取p0或p1周期。 否則pandn可能是更好的選擇。 在AMD BD系列中, andnps無論如何都在ivec域中運行,所以你不要避免int和FP向量之間的旁路延遲(如果你使用movmskps而不是ptest ,你可能希望管理movmskps ,在這個版本中只使用cmpps ,而不是pcmpeqd )。 另請注意,此處選擇了指令排序以供人類閱讀。 ANDPS之前更早地進行FP比較(A,B)可能會幫助CPU更快地開始這個循環。

如果重用一個操作數,則應該可以重用其自NaN查找結果。 新的操作數仍然需要進行自我NaN檢查,並與重用的操作數進行比較,因此我們只保存一個movaps / cmpps

如果向量在內存中,則至少需要向其中一個載入單獨的加載insn。 另一個可以從內存中引用兩次。 如果它未對齊或尋址模式不能微熔合 ,這很糟糕,但可能有用。 如果vcmpps的一個操作數是已知沒有任何NaN的向量(例如歸零寄存器),則vcmpunord_qps xmm2, xmm15, [rsi]將在[rsi]找到NaN。

如果我們不想使用PTEST ,我們可以通過使用相反的比較得到相同的結果,但是將它們與相反的邏輯運算符(AND與OR)組合。

; inputs in xmm0, xmm1
movaps      xmm2, xmm0
cmpunordps  xmm2, xmm2      ; find NaNs in A (-1:NaN  0:anything else)
movaps      xmm3, xmm1
cmpunordps  xmm3, xmm3      ; find NaNs in B
andps       xmm2, xmm3      ; xmm2 = (-1:both NaN  0:anything else)
; now in the same boat as before: xmm2 is set for elements we want to consider equal, even though they're not IEEE equal

cmpeqps     xmm0, xmm1      ; -1:ieee_equal  0:unordered or unequal
; xmm0   xmm2 
;  -1      0     -> equal   (ieee_equal)
;  -1     -1     -> equal   (ieee_equal and both NaN (impossible))
;   0      0     -> unequal (neither)
;   0     -1     -> equal   (both NaN)

orps        xmm0, xmm2      ; 0: unequal.  -1:reflexive_equal
movmskps    eax, xmm0
test        eax, eax
jnz  equal_reflexive

其他想法:未完成,不可行,破壞或比上述更糟糕

真正比較的全部結果是NaN的編碼。 嘗試一下 。也許我們可以避免使用PORPAND cmpps在每個操作數上組合來自cmpps結果?

; inputs in A:xmm0 B:xmm1
movaps      xmm2, xmm0
cmpordps    xmm2, xmm2      ; find NaNs in A.  (0: NaN.  -1: anything else).  Same as cmpeqps since src and dest are the same.
; cmpunordps wouldn't be useful: NaN stays NaN, while other values are zeroed.  (This could be useful if ORPS didn't exist)

; integer -1 (all-ones) is a NaN encoding, but all-zeros is 0.0
cmpunordps  xmm2, xmm1
; A:NaN B:0   ->  0   unord 0   -> false
; A:0   B:NaN ->  NaN unord NaN -> true

; A:0   B:0   ->  NaN unord 0   -> true
; A:NaN B:NaN ->  0   unord NaN -> true

; Desired:   0 where A and B are both NaN.

cmpordps xmm2, xmm1只是翻轉每個案例的最終結果,第一行仍然是“odd-man-out”。

如果兩個輸入都被反轉(NaN - >非NaN,反之亦然),我們只能得到我們想要的結果(如果A和B都是NaN則為真)。 這意味着我們可以使用這個想法cmpordps作為替代pand做后cmpordps self,self在A和B,這是沒有用的:即使我們有AVX但不AVX2,我們可以使用vandpsvandnps (和vmovmskps因為vptest只是AVX2)。 按位布爾值只是單周期延遲,並不會占用已經成為此代碼瓶頸的vector-FP-add執行端口。


VFIXUPIMMPS

我花了一段時間與手冊溝通其操作

如果源元素是NaN,它可以修改目標元素,但不能以關於dest元素的任何內容為條件。

我希望我能想到一種方法來vcmpneqps然后vcmpneqps結果,一次使用每個源操作數(以消除組合3 vcmpps指令結果的vcmpps指令)。 我現在相當確定這是不可能的,因為知道一個操作數是NaN本身就不足以改變IEEE_equal(A,B)結果。

我認為我們可以使用vfixupimmps的唯一方法是分別檢測每個源操作數中的NaN,比如vcmpunord_qps但更糟。 或者作為andps一個非常愚蠢的替代andps ,在先前比較的掩碼結果中檢測0或全1(NaN)。


AVX512掩碼寄存器

使用AVX512掩碼寄存器可以幫助組合比較結果。 大多數AVX512比較指令將結果放入掩碼寄存器而不是向量寄存器中的掩碼向量,因此如果我們想要以512b塊運行,我們實際上必須這樣做。

VFPCLASSPS k2 {k1}, xmm2, imm8寫入掩碼寄存器,可選地由不同的掩碼寄存器屏蔽。 通過僅設置imm8的QNaN和SNaN位,我們可以獲得向量中存在NaN的掩碼。 通過設置所有其他位,我們可以得到逆。

通過使用A中的掩碼作為B上vfpclassps的零掩碼,我們可以找到只有2個指令的兩個NaN位置,而不是通常的cmp / cmp / combine。 所以我們保存一個orandn指令。 順便說一句,我想知道為什么沒有OR-NOT操作。 可能它比AND-NOT更少出現,或者他們只是不想在指令集中使用porn

yasm和nasm都不能組合這個,所以我甚至不確定我的語法是否正確!

; I think this works

;  0x81 = CLASS_QNAN|CLASS_SNAN (first and last bits of the imm8)
VFPCLASSPS    k1,     zmm0, 0x81 ; k1 = 1:NaN in A.   0:non-NaN
VFPCLASSPS    k2{k1}, zmm1, 0x81 ; k2 = 1:NaNs in BOTH
;; where A doesn't have a NaN, k2 will be zero because of the zeromask
;; where B doesn't have a NaN, k2 will be zero because that's the FPCLASS result
;; so k2 is like the bitwise-equal result from pcmpeqd: it's an override for ieee_equal

vcmpNEQ_UQps  k3, zmm0, zmm1
;; k3= 0 only where IEEE equal (because of cmpneqps normal operation)

;  k2   k3   ; same logic table as the pcmpeqd bitwise-NaN version
;  0    0    ->  equal   (ieee equal)
;  0    1    ->  unequal (neither)
;  1    0    ->  equal   (ieee equal and both-NaN (impossible))
;  1    1    ->  equal   (both NaN)

;  not(k2) AND k3 is true only when the element is unequal (bitwise and ieee)

KTESTW        k2, k3    ; same as PTEST: set CF from 0 == (NOT(k2) AND k2)
jc .reflexive_equal

對於第二個vfpclassps insn,我們可以重復使用相同的掩碼寄存器作為零掩碼和目標,但是我想使用不同的寄存器以便在注釋中區分它們。 此代碼至少需要兩個掩碼寄存器,但不需要額外的向量寄存器。 我們也可以使用k0而不是k3作為vcmpps的目標,因為我們不需要將它用作謂詞,只能用作dest和src。 k0是不能用作謂詞的寄存器,因為編碼意味着“沒有屏蔽”。)

我不確定我們是否可以為每個元素創建一個帶有reflexive_equal結果的單個掩碼,而沒有k...指令在某個點組合兩個掩碼(例如kandnw而不是ktestw )。 掩碼僅用作零掩碼,而不是一個可以強制結果為一個掩碼的掩碼,因此組合vfpclassps結果僅用作AND。 所以我認為我們堅持使用1-means-both-NaN,這對於將它用作vcmpps的零掩碼是vcmpps 首先執行vcmpps ,然后使用掩碼寄存器作為目標和vfpclassps謂詞,也沒有用。 合並屏蔽而不是零屏蔽可以解決問題,但在寫入屏蔽寄存器時不可用。

;;; Demonstrate that it's hard (probably impossible) to avoid using any k... instructions
vcmpneq_uqps  k1,    zmm0, zmm1   ; 0:ieee equal   1:unequal or unordered

vfpclassps    k2{k1}, zmm0, 0x81   ; 0:ieee equal or A is NaN.  1:unequal
vfpclassps    k2{k2}, zmm1, 0x81   ; 0:ieee equal | A is NaN | B is NaN.  1:unequal
;; This is just a slow way to do vcmpneq_Oqps: ordered and unequal.

vfpclassps    k3{k1}, zmm0, ~0x81  ; 0:ieee equal or A is not NaN.  1:unequal and A is NaN
vfpclassps    k3{k3}, zmm1, ~0x81  ; 0:ieee equal | A is not NaN | B is not NaN.  1:unequal & A is NaN & B is NaN
;; nope, mixes the conditions the wrong way.
;; The bits that remain set don't have any information from vcmpneqps left: both-NaN is always ieee-unequal.

如果ktest最終像ptest那樣是2 ktest ,並且不能宏融合,那么kmov eax, k2 / test-and-branch可能會比ktest k1,k2 / jcc便宜。 希望它只有一個uop,因為屏蔽寄存器更像是整數寄存器,並且可以從一開始就設計為與標志“完全”“接近”。 ptest僅在SSE4.1中添加,經過多代設計,載體和EFLAGS之間沒有相互作用。

kmov確實為你設置了popcnt,bsf或bsr。 bsf / jcc沒有宏融合,所以在搜索循環中你可能仍然想要測試/ jcc而只有bsf才能找到非零。編碼tzcnt的額外字節不會給你買除非你做無bsf事情,因為bsf仍然在零輸入上設置ZF,即使dest寄存器未定義。 lzcnt給出32 - bsr ,所以即使你知道輸入是非零的,它也很有用。)

我們也可以使用vcmpEQps並以不同的方式組合我們的結果:

VFPCLASSPS      k1,     zmm0, 0x81 ; k1 = set where there are NaNs in A
VFPCLASSPS      k2{k1}, zmm1, 0x81 ; k2 = set where there are NaNs in BOTH
;; where A doesn't have a NaN, k2 will be zero because of the zeromask
;; where B doesn't have a NaN, k2 will be zero because that's the FPCLASS result
vcmpEQ_OQps     k3, zmm0, zmm1
;; k3= 1 only where IEEE equal and ordered (cmpeqps normal operation)

;  k3   k2
;  1    0    ->  equal   (ieee equal)
;  1    1    ->  equal   (ieee equal and both-NaN (impossible))
;  0    0    ->  unequal (neither)
;  0    1    ->  equal   (both NaN)

KORTESTW        k3, k2  ; CF = set iff k3|k2 is all-ones.
jc .reflexive_equal

這種方式只有當kortest的大小與我們向量中的元素數完全匹配時才有效。 例如,256b雙精度元素向量只有4個元素,但kortestb仍然根據輸入掩碼寄存器的低8位設置CF.


僅使用整數運算

除NaN外,+ / - 0是IEEE_equal與bitwise_equal不同的唯一時間。 (除非我遺漏了一些東西。在使用之前仔細檢查這個假設!) +0-0所有位都為零,除了-0有符號位設置(MSB)。

如果我們忽略不同的NaN編碼,那么bitwise_equal就是我們想要的結果,除了+/- 0情況。 除了符號位之外, A OR B將是0,如果A和B是+/- 0,則左移1會使其全為零或不為全為零,這取決於我們是否需要重寫按位 - 等量測試。

這使用了比cmpneqps更多的指令,因為我們使用por / paddD模擬我們需要的功能。 (或pslld加1,但這比一個字節長。它確實運行在與pcmpeq不同的端口上,但你需要考慮周圍代碼的端口分布,以便將其納入決策。)

該算法可能對不提供用於檢測NaN的相同矢量FP測試的不同SIMD架構有用。

;inputs in xmm0:A  xmm1:B
movaps    xmm2, xmm0
pcmpeqd   xmm2, xmm1     ; xmm2=bitwise_equal.  (0:unequal -1:equal)

por       xmm0, xmm1
paddD     xmm0, xmm0     ; left-shift by 1 (one byte shorter than pslld xmm0, 1, and can run on more ports).

; xmm0=all-zero only in the +/- 0 case (where A and B are IEEE equal)

; xmm2     xmm0          desired result (0 means "no difference found")
;  -1       0        ->      0          ; bitwise equal and +/-0 equal
;  -1     non-zero   ->      0          ; just bitwise equal
;   0       0        ->      0          ; just +/-0 equal
;   0     non-zero   ->      non-zero   ; neither

ptest     xmm2, xmm0         ; CF = ( (not(xmm2) AND xmm0) == 0)
jc  reflexive_equal

延遲低於上面的cmpneqps版本一個或兩個周期。

我們在這里真正充分利用了PTEST :在兩個不同的操作數之間使用它的ANDN,並使用它對整個事物的零比較。 我們不能用pandn / movmskps替換它,因為我們需要檢查所有位,而不僅僅是每個元素的符號位。

我實際上沒有對此進行過測試,所以即使我的結論是+/- 0是IEEE_equal與bitwise_equal(除了NaNs)之外的唯一時間,也可能是錯誤的。


使用僅整數運算處理非按位相同的NaN可能不值得。 編碼與+/- Inf非常相似,我無法想到任何不需要多條指令的簡單檢查。 Inf具有設置的所有指數位和全零尾數。 NaN具有所有指數位設置,具有非零尾數aka有效數(因此有23位有效載荷)。 尾數的MSB被解釋為is_quiet標志以區分信令/安靜NaN。 另請參閱英特爾手冊vol1,表4-3( Floating-Point Number and NaN Encodings )。

如果它不是-Inf使用前9位設置編碼,我們可以檢查NaN與A > 0x7f800000的無符號比較。 0x7f800000是單精度+ Inf)。 但是,請注意pcmpgtd / pcmpgtq是有符號整數比較。 AVX512F VPCMPUD是無符號比較(dest =掩碼寄存器)。


OP的想法: !(a<b) && !(b<a)

OP的建議是!(a<b) && !(b<a)不起作用,也不能改變它。 你不能分辨出一個NaN和兩個NaN之間的區別只是兩個與反向操作數的比較。 甚至混合謂詞也無濟於事: 沒有VCMPPS謂詞將一個操作數NaN與兩個操作數NaN區VCMPPS ,或者取決於它是第一個還是第二個操作數NaN。 因此,他們的組合不可能擁有這些信息。

Paul R將矢量與自身進行比較的解決方案讓我們可以檢測出NaN的位置並“手動”處理它們。 兩個操作數之間沒有VCMPPS結果的組合就足夠了,但使用AB以外A操作數確實有幫助。 (兩次已知的非NaN向量或相同的操作數)。


如果沒有反轉,則按位NaN代碼會在至少一個元素相等時找到。 pcmpeqd沒有反轉,所以我們不能使用不同的邏輯運算符,仍然可以測試全部相等):

; inputs in xmm0, xmm1
movaps   xmm2, xmm0
cmpeqps  xmm2, xmm1    ; -1:ieee_equal.  EQ_OQ predicate in the expanded notation for VEX encoding
pcmpeqd  xmm0, xmm1    ; -1:bitwise equal
orps     xmm0, xmm2
; xmm0 = -1:(where an element is bitwise or ieee equal)   0:elsewhere

movmskps eax, xmm0
test     eax, eax
jnz at_least_one_equal
; else  all different

PTEST在這方面沒有用,因為與OR結合是唯一有用的東西。


// UNFINISHED start of an idea
bitdiff = _mm_xor_si128(A, B);
signbitdiff = _mm_srai_epi32(bitdiff, 31);   // broadcast the diff in sign bit to the whole vector
signbitdiff = _mm_srli_epi32(bitdiff, 1);    // zero the sign bit
something = _mm_and_si128(bitdiff, signbitdiff);

這是一種可能的解決方案 - 但效率不高,需要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, 反之亦然 )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM