valores tabla punto presentacion para numericos formatos flotante ejemplos binario assembly floating-point x86 x86-64 simd

assembly - tabla - punto flotante ejemplos



Instrucciones SIMD para comparación de igualdad de coma flotante(con NaN== NaN) (2)

Aquí hay una posible solución: no es muy eficiente, sin embargo, requiere 6 instrucciones:

__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

Tenga en cuenta que toda la lógica anterior está invertida, lo que puede hacer que el código sea un poco difícil de seguir (los OR son efectivamente AND , y viceversa ).

¿Qué instrucciones se usarían para comparar dos vectores de 128 bits que consisten en valores de punto flotante de 4 * 32 bits?

¿Hay alguna instrucción que considere que un valor NaN en ambos lados sea igual? Si no, ¿cuán grande sería el impacto en el rendimiento de una solución que proporciona reflexividad (es decir, NaN es igual a NaN)?

Escuché que garantizar la reflexividad tendría un impacto significativo en el rendimiento en comparación con la semántica IEEE, donde NaN no se iguala a sí mismo, y me pregunto si ese gran impacto sería.

Sé que normalmente desea utilizar comparaciones épsilon en lugar de la calidad exacta cuando se trata de valores de punto flotante. Pero esta pregunta se trata de comparaciones exactas de igualdad, que podría, por ejemplo, utilizar para eliminar valores duplicados de un conjunto de hash.

Requisitos

  • +0 y -0 deben comparar como iguales.
  • NaN debe comparar igual consigo mismo.
  • Las diferentes representaciones de NaN deberían ser iguales, pero ese requisito podría sacrificarse si el impacto en el rendimiento es demasiado grande.
  • El resultado debe ser booleano, true si los cuatro elementos flotantes son iguales en ambos vectores y falso si al menos un elemento es diferente. Donde true es representado por un entero escalar 1 y false por 0 .

Casos de prueba

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

Mi idea para implementar esto

Creo que podría ser posible combinar dos comparaciones de CMPNLTPS ( CMPNLTPS ?) Usando and para lograr el resultado deseado. El ensamblador equivalente a AllTrue(!(x < y) and !(y < x)) o AllFalse((x < y) or (y > x) .

Fondo

El contexto para esta pregunta es el plan de Microsoft para agregar un tipo de Vector a .NET. Donde estoy abogando por un método reflexivo de .Equals y necesito una imagen más clara de cuán grande sería el impacto en el rendimiento de este reflexivo sobre un IEEE igual. Consulte ¿Debería Vector<float>.Equals ser reflexivo o debería seguir la semántica de IEEE 754? en programadores.se para la larga historia.


Incluso cuando AVX VCMPPS está disponible (con una selección de predicados muy mejorada), es menos eficiente que la comparación IEEE. Tienes que hacer al menos dos comparaciones y combinar los resultados. No es tan malo, sin embargo.

  • las diferentes codificaciones de NaN no son iguales: efectivamente 2 insns adicionales (agregando 2 uops). Sin AVX: Un movaps extra más allá de eso.

  • las diferentes codificaciones de NaN son iguales: efectivamente 4 insns adicionales (agregando 4 uops). Sin AVX: dos más movaps insn

Un IEEE compare-and-branch es 3 uops: cmpeqps / movmskps / test-and-branch. Intel y AMD fusionan macro el test-and-branch en un solo uop / m-op.

Con AVX512: bit-NaN es probablemente solo una instrucción adicional, ya que la comparación y ramificación de vector normal probablemente use vcmpEQ_OQps / ktest same,same / jcc , por lo que combinar dos regmas de máscara diferentes es gratis (simplemente cambie los ktest a ktest ). El único costo es el vpcmpeqd k2, xmm0,xmm1 extra vpcmpeqd k2, xmm0,xmm1 .

AVX512 any-NaN es solo dos instrucciones adicionales (2x VFPCLASSPS , y el segundo usa el resultado del primero como zeromask. Consulte a continuación). De nuevo, luego ktest con dos ktest diferentes para establecer el indicador.

Mi mejor idea hasta ahora: ieee_equal || bitwise_equal ieee_equal || bitwise_equal

Si nos damos por vencidos considerando diferentes codificaciones NaN iguales entre sí:

  • Bitwise equal captura dos NaN idénticos.
  • IEEE igual atrapa el +0 == -0 caso.

No hay casos en que cualquiera de los dos compare da un falso positivo (ya que ieee_equal es falso cuando cualquiera de los operandos es NaN: queremos igual, no igual o desordenado. AVX vcmpps proporciona ambas opciones, mientras que SSE solo proporciona una operación simple igual).

Queremos saber cuándo todos los elementos son iguales, entonces debemos comenzar con las comparaciones invertidas. Es más fácil verificar al menos un elemento que no sea cero que verificar que todos los elementos no sean cero. (es decir, Y horizontal es difícil, horizontal O es fácil ( pmovmskb / test , o ptest ). Tomar el sentido opuesto de una comparación es libre ( jnz lugar de jz ).) Este es el mismo truco que usó 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

Para precisión doble, ...PS y pcmpeqQ funcionarán igual.

Si el código no igual continúa para descubrir qué elemento no es igual, un escaneo de bits en el resultado de movmskps le dará la posición de la primera diferencia.

Con SSE4.1 PTEST puede reemplazar andnps / movmskps / test-and-branch con:

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

Espero que esta sea la primera vez que la mayoría de las personas vean que el resultado de CF de PTEST sea ​​útil para cualquier cosa. :)

Siguen tres uops en las CPU Intel y AMD ((2ptest + 1jcc) vs (pandn + movmsk + fusionado-prueba y rama)), pero con menos instrucciones. Es más eficiente si va a setcc o cmovcc lugar de jcc , ya que no pueden fusionarse con test .

Eso hace un total de 6 uops (5 con AVX) para una comparación reflexiva y una rama, frente a 3 uops para una comparación y rama de IEEE . ( cmpeqps / movmskps / test-and-branch)

PTEST tiene una latencia muy alta en las CPU AMD Bulldozer-family ( 14c en Steamroller ). Tienen un grupo de unidades de ejecución de vectores compartidas por dos núcleos enteros. (Esta es su alternativa al hyperthreading). Esto aumenta el tiempo hasta que se puede detectar un cmovcc setcc , o la latencia de una cadena de dependencia de datos ( cmovcc / setcc ).

PTEST establece ZF cuando 0==(xmm0 AND xmm2) : establece si ningún elemento era bitwise_equal AND IEEE (neq o desordenado). es decir, ZF no está establecido si cualquier elemento era bitwise_equal mientras que también era !ieee_equal . Esto solo puede ocurrir cuando un par de elementos contiene NaN s bit a bit (pero puede suceder cuando otros elementos son desiguales).

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

No hay ninguna condición que pruebe CF=1 Y nada sobre ZF . ja prueba CF=0 and ZF=1 . Es poco probable que solo quieras probar eso de todos modos, por lo que poner un jnz en el objetivo de la rama de jc funciona bien. (Y si solo desea probar equal_reflexive AND at_least_one_nan , una configuración diferente probablemente podría establecer flags apropiadamente).

Considerando que todos los NaN son iguales, incluso cuando no son iguales a los bits de bit:

Esta es la misma idea que la respuesta de Paul R, pero con una corrección de errores (combina el control de NaN con el control IEEE usando Y en lugar de O).

; 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

Entonces, en este caso, no necesitamos el resultado CF PTEST después de todo. Lo hacemos cuando usamos PCMPEQD , porque no tiene un inverso (la forma en que cmpunordps tiene cmpordps ).

9 uops de dominio fusionado para CPU de la familia Intel SnB. (7 con AVX: utilice instrucciones de 3 operandos no destructivas para evitar las movaps ). Sin embargo, las CPU de la familia pre-Skylake SnB solo pueden ejecutar cmpps en p1, por lo que esto cmpps unidad FP-add si el rendimiento es una preocupación. Skylake ejecuta cmpps en cmpps / p1.

andps tiene una codificación más corta que pand , y las CPU Intel de Nehalem a Broadwell solo pueden ejecutarlo en port5. Eso puede ser deseable para evitar que robe un ciclo p0 o p1 del código FP circundante. De pandn contrario, pandn es probablemente una mejor opción. En AMD BD-family, andnps ejecuta en el dominio ivec de todos modos, por lo que no evita el retraso de bypass entre los vectores int y FP (que de otra manera esperaría gestionar si usa movmskps lugar de ptest , en esta versión que solo usa cmpps , no pcmpeqd ). También tenga en cuenta que el orden de instrucciones se elige para la legibilidad humana aquí. Poner el FP compare (A, B) antes, antes de ANDPS , podría ayudar a la CPU a comenzar ese ciclo antes.

Si se vuelve a utilizar un operando, debería ser posible reutilizar su resultado de búsqueda de NaN propio. El nuevo operando aún necesita su verificación de auto NaN y una comparación con el operando reutilizado, por lo que solo movaps un movaps / cmpps .

Si los vectores están en la memoria, al menos uno de ellos debe cargarse con una entrada de carga separada. El otro solo puede ser referenciado dos veces desde la memoria. Esto apesta si está desalineado o el modo de direccionamiento no puede micro fusibles , pero podría ser útil. Si uno de los operandos de vcmpps es un vector que no tiene ningún NaN (p. Ej., Un registro en cero), vcmpunord_qps xmm2, xmm15, [rsi] encontrará NaN en [rsi] .

Si no queremos usar PTEST , podemos obtener el mismo resultado utilizando las comparaciones opuestas, pero combinándolas con el operador lógico opuesto (AND vs. 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

Otras ideas: inacabado, no viable, roto o peor que el anterior

El resultado general de una comparación verdadera es una codificación de NaN . ( Pruébelo . ¿Quizás podamos evitar usar POR o PAND para combinar los resultados de cmpps en cada operando por separado?

; 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 solo voltea el resultado final para cada caso, con el "hombre extraño fuera" en la primera fila.

Solo podemos obtener el resultado que queremos (verdadero iff A y B son ambos NaN) si ambas entradas están invertidas (NaN -> no NaN y viceversa). Esto significa que podríamos usar esta idea para cmpordps como reemplazo de pand después de hacer cmpordps self,self en A y B. Esto no es útil: incluso si tenemos AVX pero no AVX2, podemos usar vandps y vandnps (y vmovmskps ya que vptest es solo AVX2). Los booleanos bit a bit son solo latencia de ciclo único y no vinculan los puertos de ejecución vector-FP-add que ya son un cuello de botella para este código.

VFIXUPIMMPS

Pasé un tiempo con el manual explicando su funcionamiento .

Puede modificar un elemento de destino si un elemento fuente es NaN, pero eso no puede ser condicional en nada sobre el elemento dest.

Esperaba poder pensar en una forma de vcmpneqps y luego arreglar ese resultado, una vez con cada operando fuente (para eludir las instrucciones booleanas que combinan los resultados de 3 instrucciones vcmpps ). Ahora estoy bastante seguro de que eso es imposible, ya que saber que un operando es NaN no es suficiente por sí mismo hacer un cambio en el resultado IEEE_equal(A,B) .

Creo que la única forma en que podríamos usar vfixupimmps es detectar NaNs en cada operando fuente por separado, como vcmpunord_qps pero peor. O como un reemplazo realmente estúpido para andps , detectando 0 o todos-uno (NaN) en los resultados de máscara de comparaciones anteriores.

Registros de máscara AVX512

El uso de registros de máscara AVX512 podría ayudar a combinar los resultados de las comparaciones. La mayoría de las instrucciones de comparación de AVX512 ponen el resultado en un registro de máscara en lugar de un vector de máscara en un registro vectorial, por lo que tenemos que hacer las cosas de esta manera si queremos operar en fragmentos de 512b.

VFPCLASSPS k2 {k1}, xmm2, imm8 escribe en un registro de máscara, opcionalmente enmascarado por un registro de máscara diferente. Al establecer solo los bits QNaN y SNaN del imm8, podemos obtener una máscara de dónde hay NaN en un vector. Al configurar todos los demás bits, podemos obtener el inverso.

Al usar la máscara de A como máscara cero para vfpclassps en B, podemos encontrar las posiciones ambas NaN con solo 2 instrucciones, en lugar de la combinación habitual de cmp / cmp /. Así que guardamos una instrucción or andn . A propósito, me pregunto por qué no hay operación OR-NOT. Probablemente aparece con menos frecuencia que AND-NOT, o simplemente no querían porn en el conjunto de instrucciones.

Ni yasm ni nasm pueden ensamblar esto, ¡así que ni siquiera estoy seguro si tengo la sintaxis correcta!

; 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

Podríamos reutilizar el mismo registro de máscara tanto zeromask como destino para el segundo vfpclassps insn, pero utilicé diferentes registros en caso de que quisiera distinguirlos en un comentario. Este código necesita un mínimo de dos registros de máscara, pero no registros de vectores adicionales. También podríamos usar k0 lugar de k3 como el destino de vcmpps , ya que no necesitamos usarlo como un predicado, solo como un dest y src. ( k0 es el registro que no se puede usar como predicado, porque los medios de codificación significan "no enmascarar").

No estoy seguro de que podamos crear una sola máscara con el resultado reflexive_equal para cada elemento, sin una k... instrucción para combinar dos máscaras en algún punto (por ejemplo, kandnw lugar de ktestw ). Las máscaras solo funcionan como máscaras cero, no como máscaras que pueden forzar un resultado a uno, por lo que la combinación de los resultados de vfpclassps solo funciona como AND. Así que creo que estamos atrapados con 1-means-both-NaN, que es el sentido equivocado para usarlo como zeromask con vcmpps . Hacer vcmpps primero, y luego usar el registro de máscara como destino y predicado para vfpclassps , tampoco ayuda. Merge-masking en lugar de zero-masking sería el truco, pero no está disponible cuando se escribe en un registro de máscara.

;;; 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.

Si ktest termina siendo 2 uops como ptest , y no puede macro fusibles, entonces kmov eax, k2 / test-and-branch probablemente sea más barato que ktest k1,k2 / jcc. Con suerte, solo será un uop, ya que los registros de máscara son más parecidos a los registros enteros, y se pueden diseñar desde el principio para ser "cercanos" a las banderas. ptest solo se agregó en SSE4.1, después de muchas generaciones de diseños sin interacción entre vectores y EFLAGS .

kmov embargo, kmov sí lo configuró para popcnt, bsf o bsr. ( bsf / bsf no macro fusible, por lo que en un bucle de búsqueda es probable que quiera probar / jcc y solo bsf cuando se encuentre un valor distinto de cero. El byte adicional para codificar tzcnt no lo compra cualquier cosa, a menos que esté haciendo algo sin ramas, porque bsf todavía establece ZF en una entrada cero, aunque el registro de lzcnt no está definido. lzcnt da 32 - bsr , por lo que puede ser útil incluso sabiendo que la entrada no es cero .)

También podemos usar vcmpEQps y combinar nuestros resultados de manera diferente:

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

De esta manera solo funciona cuando hay un tamaño de kortest que coincide exactamente con la cantidad de elementos en nuestros vectores. por ejemplo, un vector 256b de elementos de precisión doble solo tiene 4 elementos, pero kortestb todavía establece CF de acuerdo con los 8 bits bajos de los registros de la máscara de entrada.

Usar solo operaciones de enteros

Aparte de NaN, +/- 0 es el único momento en que IEEE_equal es diferente de bitwise_equal. (¡A menos que me falta algo! ¡Comprueba esta suposición antes de usar!) +0 y -0 tienen todos sus bits en cero, excepto que -0 tiene el bit de signo configurado (el MSB).

Si ignoramos diferentes codificaciones NaN, entonces bitwise_equal es el resultado que queremos, excepto en el caso +/- 0. A OR B será 0 en todas partes excepto el bit de signo iff A y B son +/- 0. Un desplazamiento hacia la izquierda en uno lo convierte en cero o no-todo-cero para que dependamos de si debemos o no reemplazar el bitwise -equal prueba.

Esto usa una instrucción más que cmpneqps , porque estamos emulando la funcionalidad que necesitamos de ella con por / paddD . (o pslld por uno, pero eso es un byte más largo. Se ejecuta en un puerto diferente que pcmpeq , pero debe considerar la distribución del puerto del código circundante para incluirlo en la decisión).

Este algoritmo podría ser útil en diferentes arquitecturas SIMD que no proporcionan las mismas pruebas FP de vector para detectar NaN.

;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

La latencia es inferior a la versión cmpneqps anterior, en uno o dos ciclos.

Realmente aprovechamos al máximo PTEST aquí: usa su ANDN entre dos operandos diferentes, y usa su comparación contra cero de todo el asunto. No podemos reemplazarlo con pandn / movmskps porque necesitamos verificar todos los bits, no solo el bit de signo de cada elemento.

En realidad, no he probado esto, por lo que podría ser incorrecto incluso si mi conclusión de que +/- 0 es la única vez que IEEE_equal es diferente de bitwise_equal (distinto de NaN).

Manejar NaNs no idénticos a bitwise con ops entero-probablemente no lo vale. La codificación es tan similar a +/- Inf que no puedo pensar en ninguna verificación simple que no tome varias instrucciones. Inf tiene todos los bits de exponente establecidos, y una mantisa de cero. NaN tiene todos los bits de exponente establecidos, con una mantisa no n. ° aka significand (por lo que hay 23 bits de carga útil). El MSB de la mantisa se interpreta como un indicador is_quiet para distinguir los is_quiet de señalización / silencio. También vea Intel manual vol1, tabla 4-3 ( Floating-Point Number and NaN Encodings ).

Si no fuera por -Inf utilizando la codificación top-9-bits-set, podríamos verificar NaN con una comparación sin signo para A > 0x7f800000 . ( 0x7f800000 es de precisión simple + Inf). Sin embargo, tenga en cuenta que pcmpgtd / pcmpgtq son comparaciones enteras con signo. AVX512F VPCMPUD es una comparación sin signo (dest = un registro de máscara).

La idea del OP:! !(a<b) && !(b<a)

La sugerencia de OP de !(a<b) && !(b<a) no puede funcionar, y tampoco puede variar. No se puede diferenciar entre un NaN y dos NaN solo de dos comparados con operandos invertidos. Incluso los predicados de mezcla no pueden ayudar: Ningún predicado VCMPPS diferencia un operando que es NaN de ambos operandos que son NaN , o depende de si es el primer o segundo operando que es NaN. Por lo tanto, es imposible que una combinación de ellos tenga esa información.

La solución de Paul R de comparar un vector consigo mismo nos permite detectar dónde hay NaN y manejarlos "manualmente". Ninguna combinación de resultados de VCMPPS entre los dos operandos es suficiente, pero el uso de operandos distintos de A y B ayuda. (O bien un vector conocido que no sea NaN o el mismo operando dos veces).

Sin la inversión, el código de NaN bit a bit encuentra cuando al menos un elemento es igual. (No hay pcmpeqd para pcmpeqd , por lo que no podemos usar diferentes operadores lógicos y aún así obtener una prueba para todos iguales):

; 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 no es útil de esta manera, ya que combinar con O es lo único útil.

// 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);