7
votes

SSE intrinsèque sur INT16 [8] pour extraire le signe de chaque élément

Je travaille avec des fonctions intrinsèques SSE. J'ai un __M128I représentant un tableau de 8 valeurs courtes (16 bits) signées.

existe une fonction pour obtenir le signe de chaque élément?

EDIT1: quelque chose qui peut être utilisé comme ceci: xxx

bien sûr "_mm_sign_epi16" n'existe pas, c'est ce que je cherche.

Comment Lent, c'est de le faire de l'élément par élément?

Edit2: Comportement souhaité: 1 Pour les valeurs positives, 0 pour zéro et -1 pour les valeurs négatives.

Merci


10 commentaires

Dans quel format souhaitez-vous recevoir le panneau? S'il vous plaît poster un exemple.


Guide Intel intrinsics


Dépend de la manière dont vous utilisez la valeur, utilisez une comparaison avec 0, un décalage droit de 15 ou et chaque élément avec 0x8000


Merci! Qu'entendez-vous par «Comment j'utilise la valeur»?


Voulez-vous que le résultat du signe de chaque élément soit + 1/0 / -1, ou + 1 / -1, ou 1/0 ou quoi?


Si vous ne voulez que le bit de signalisation, vous pouvez utiliser _mm_movemask_epi8 (_mm_packs_epi16 (TMP, _mm_setzero_si128 ())) . En mots: emballer dans des valeurs de 8 bits via saturation signée, régler les 64 bits supérieurs à zéro. Cela préserve le signe. Ensuite, extrayez les 16 bits de signe. Étant donné que les 64 bits supérieurs sont nuls, les 8 bits de signe supérieur seront nuls.


Je vais essayer, mais cela ne me donnera pas 0 si l'élément est zéro, n'est-ce pas?


Le bit de signalisation est 1 si la valeur est négative et que c'est 0 si la valeur est nulle ou positive. Je vois que vous avez clarifié dans votre édition que vous souhaitez -1/0/1, auquel cas _mm_movemask_epi8 ne vous aidera pas.


@Paulr, comment le feriez-vous pour les numéros de points flottants lorsqu'il est limité à SSE3? Je vous remercie.


@ROYI: Cela devrait être assez simple, mais postez une nouvelle question avec une balise [SSE], et je ferai de mon mieux pour que vous puissiez trouver une solution (comme les autres sans doute). Assurez-vous de spécifier imppact quelles seuils vous avez besoin pour différents cas d'entrée.


3 Réponses :


1
votes

Remplissez un registre des zéros et comparez-le avec votre registre, d'abord avec "supérieur à", qu'avec "inférieur à" (ou inverser l'ordre des opérandes dans l'instruction "supérieure à").
http://msdn.microsoft.com/ EN-US / Bibliothèque / XD43YFSA% 28V = vs.90% 29.aspx
http://msdn.microsoft.com/ EN-US / Bibliothèque / T863EDB2% 28V = vs.90% 29.aspx

Le problème à ce stade est que la valeur réelle est représentée comme 0xFFFF, qui se trouve être -1, résultat correct pour le nombre négatif mais non pour le positif. Cependant, comme indiqué par Raymond Chen dans les commentaires, 0x0000 - 0xFFFF = 0x0001, il suffit donc de soustraire le résultat "supérieur à" du résultat "inférieur à". http://msdn.microsoft.com/ EN-US / Bibliothèque / Y25YYA27% 28V = vs.0% 29.aspx

Bien sûr Réponse Paul R est préférable, car il utilise uniquement 2 instructions.


3 commentaires

Vous n'avez pas besoin de changer. Il suffit de soustraire dans l'ordre inverse.


Si vous faites une soustraction signée, il n'y a pas de débordement. 0 - (-1) = 1.


Je veux dire que vous pouvez faire _mm_sub_epi16 (_mm_cmpgt_epi16 (_mm_setzero_si128 (), valeur), _mm_cmpgt_epi16 (valeur, _mm_setzero_si128 ()))) . Le point est que 0x0000 - 0xFFFF = 0x0001, vous n'avez donc pas besoin de changer du tout.



0
votes

Vous pouvez déplacer tous les 8 shorts à la fois à l'aide de _mm_srai_epi16 (TMP, 15) qui retournera huit entiers 16 bits, chacun étant tous ceux (c'est-à-dire -1) si l'entrée était négative, ou tous les zéros (c'est-à-dire 0) si positif.


7 commentaires

La fonction de panneau doit renvoyer 1 pour des valeurs positives, 0 pour zéro et -1 pour les valeurs négatives. en.wikipedia.org/wiki/sign_function


@Antonio: Comment savez-vous que c'est ce que l'OP veut? C'est une question honnête: je n'ai rien vu aussi précis de lui.


Réellement avoir 0 pour zéro serait le résultat dont j'ai besoin.


@Mike: Ma solution donne 0 pour zéro. -1 Pour négatif, 0 sinon. Si vous avez besoin de quelque chose d'autre, veuillez être plus précis.


@Mike: Veuillez rendre votre question plus précise - c'est vague sur ce qui vous entraîne, et vous avez ignoré les commentaires demandant des éclaircissements.


Oh désolé, j'ai mal compris le commentaire d'Antonio et pensais qu'il soulignait un comportement différent de celui-ci pour tous les zéros. 1 Pour les valeurs positives, 0 pour zéro et -1 pour les valeurs négatives est en effet le comportement que je veux. Je vais le mettre dans la question principale. Merci


@Johnzwinck, je travaillais sur ce moi-même mais Paul m'a battu (encore). __ m128i s1 = _mm_srai_epi16 (v, 15); __m128i s2 = _mm_add_epi16 (_mm_set1_epi16 (1), S1); __M128I S3 = _MM_ADD_EPI16 (S1, S2); Cela semblait le faire mais je suppose que la réponse de Paul est toujours meilleure.



14
votes

Vous pouvez utiliser des opérations min / max pour obtenir le résultat souhaité, par exemple

inline __m128i _mm_sgn_epi16(__m128i v)
{
#ifdef __SSSE3__
    v = _mm_sign_epi16(_mm_set1_epi16(1), v); // use PSIGNW on SSSE3 and later
#else
    v = _mm_min_epi16(v, _mm_set1_epi16(1));  // use PMINSW/PMAXSW on SSE2/SSE3.
    v = _mm_max_epi16(v, _mm_set1_epi16(-1));
#endif
    return v;
}


4 commentaires

Notez que les deux _mm_set1_epi16 S peuvent être optimisés sur _MM_CMPEQ_EPI16 (V, V) et _MM_SRL_EPI16 (MM_CMPEQ_EPI16 (V, V), 15) . Cela évite les stands de domaine et les codes à seulement 1 ou 2 instructions.


@Raymondchen: True, mais tout compilateur décent hissera ces constantes en dehors de la boucle. Je suppose que cela ne ferait pas de mal de les déclarer explicitement avant la boucle.


Cela dépend de la pression du registre que vous êtes sous. Cela pourrait être une victoire ou une perte de toute façon - vous devez juste l'essayer à la fois et voir.


@Raymondchen: Encore vrai, mais dans ce cas particulier, l'utilisation du registre est très faible, même pour une construction 32 bits.