X86 SSE intrinsics:将32位浮点转换为无符号8位整数

X86 SSE intrinsics:将32位浮点转换为无符号8位整数,x86,sse,mmx,X86,Sse,Mmx,使用SSE intrinsic,我得到了一个由四个32位浮点组成的向量,它被钳制到0-255范围,并四舍五入到最接近的整数。我现在想把这四个字节写出来 有一个内在的\u mm\u cvtps\u pi8将32位转换为8位有符号整数,但问题是超过127的任何值都会被钳制为127。我找不到任何将钳制为无符号8位值的指令 我有一种直觉,我可能想做的是将\u mm\u cvtps\u pi16和\u mm\u shuffle\u pi8结合起来,然后执行move指令,将我关心的四个字节放入内存。这是最好

使用SSE intrinsic,我得到了一个由四个32位浮点组成的向量,它被钳制到0-255范围,并四舍五入到最接近的整数。我现在想把这四个字节写出来

有一个内在的
\u mm\u cvtps\u pi8
将32位转换为8位有符号整数,但问题是超过127的任何值都会被钳制为127。我找不到任何将钳制为无符号8位值的指令

我有一种直觉,我可能想做的是将
\u mm\u cvtps\u pi16
\u mm\u shuffle\u pi8
结合起来,然后执行move指令,将我关心的四个字节放入内存。这是最好的方法吗?我要看看我是否能想出如何编码洗牌控制面具

更新:以下内容似乎完全符合我的要求。有更好的办法吗

#包括
#包括
未签名字符输出[8];
无符号字符shuf[8]={0,2,4,6,128,128,128,128};
float-ins[4]={500,0,120,240};
int main()
{
__m128 x=_mm_load_ps(ins);//加载浮点数
__m64 y=_mm_cvtps_pi16(x);//将它们转换为16位整数
__m64 sh=*(uu m64*)shuf;//将洗牌掩码放入寄存器
y=_-mm_-shuffle_-pi8(y,sh);//将每个字节的低位字节洗牌到前四个字节中
*(int*)out=_mm_cvtsi64_si32(y);//存储较低的32位
printf(“%d\n”,out[0]);
printf(“%d\n”,out[1]);
printf(“%d\n”,out[2]);
printf(“%d\n”,out[3]);
返回0;
}
更新2:根据哈罗德的回答,这里有一个更好的解决方案:

#包括
#包括
未签名字符输出[8];
浮点数[4]={10.4,10.6,120,100000};
int main()
{   
__m128 x=_mm_load_ps(ins);//加载浮点数
__m128i y=_mm_cvtps_epi32(x);//将它们转换为32位整数
y=_-mm_-packus_-epi32(y,y);//压缩到16位
y=_-mm_-packus_-epi16(y,y);//压缩到8位
*(int*)out=_mm_cvtsi128_si32(y);//存储较低的32位
printf(“%d\n”,out[0]);
printf(“%d\n”,out[1]);
printf(“%d\n”,out[2]);
printf(“%d\n”,out[3]);
返回0;
}

没有从浮点到字节的直接转换,
\u mm\u cvtps\u pi8
是一个组合<代码>\u mm\u cvtps\u pi16
也是一个复合文件,在本例中,它只是做了一些无意义的事情,您可以用shuffle撤消这些事情。它们还返回恼人的
\uuu m64

无论如何,我们可以将其转换为DWORD(有符号,但这并不重要),然后将其打包(无符号)或混洗为字节<代码>\u mm\u shuffle(e)pi8
生成一个
pshufb
,Core2 45nm和AMD处理器不太喜欢它,你必须从某处获得一个掩码

无论采用哪种方式,您都不必首先四舍五入到最接近的整数,转换程序将执行此操作。至少,如果你没有弄乱取整模式的话

使用pack1:(未测试)--可能没有用,
packusdw
已经输出了未签名的单词,但是
packuswb
想要再次使用已签名的单词。因为它在别处被引用而被保留

cvtps2dq xmm0, xmm0  
packusdw xmm0, xmm0     ; unsafe: saturates to a different range than packuswb accepts
packuswb xmm0, xmm0
movd somewhere, xmm0
使用不同的洗牌:

cvtps2dq xmm0, xmm0  
packssdw xmm0, xmm0     ; correct: signed saturation on first step to feed packuswb
packuswb xmm0, xmm0
movd somewhere, xmm0
使用洗牌:(未测试)


我们可以通过使用符号饱和进行第一阶段打包来解决无符号箝位问题<代码>[0-255]适用于带符号的16位整数,因此该范围内的值将保持未压缩状态。超出该范围的值将保持在该范围的同一侧。因此,signed16->unsigned8步骤将正确夹紧它们

;; SSE2: good for arrays of inputs
cvtps2dq xmm0, [rsi]      ; 4 floats
cvtps2dq xmm1, [rsi+16]   ; 4 more floats
packssdw xmm0, xmm1       ; 8 int16_t

cvtps2dq xmm1, [rsi+32]
cvtps2dq xmm2, [rsi+48]
packssdw xmm1, xmm2       ; 8 more int16_t
                          ; signed because that's how packuswb treats its input
packuswb xmm0, xmm1       ; 16 uint8_t
movdqa   [rdi], xmm0
对于
packusdw

我假设这就是为什么SSE2只包括从dword到word的有符号包,但包括从word到字节的有符号包和无符号包
packuswd
仅当您的最终目标是
uint16\u t
而不是进一步打包时才有用。(从那时起,你就需要在将标志位送入下一个包装之前遮住它)

如果您确实使用了
packusdw->packuswb
,那么当第一步饱和为
uint16\u t
>0x7fff时,您会得到虚假的结果
packuswb
会将其解释为负值
int16\t
,并将其饱和为0
packssdw
将使此类输入饱和到
0x7fff
,即最大
int16\t


(如果您的32位输入总是等待,您知道
\u mm\u shuffle\u pi8
是mm寄存器版本,对吗?不要忘记您的
\u mm\u empty
@harold:哦,很好。但是,我在编译器命令行上有
-mfpmath=sse
。我是否可以建议将
\u mm\u packus\u epi32
替换为
\u mm\u epi32
er说,它工作得很好,只需要SSE2。您的(基于harold的)需要SSE4.1我真的很喜欢你的包解决方案。很好的是,取整和夹持会自动发生。但是,有一个角盒,尽管我认为它不会影响我:如果我在其中一个浮点数中放入100000,第一次,它会夹持到65535(我假设).然而,第二次,它被重新解释为一个有符号值(-1),然后被packuswb钳制为零。对此有什么低成本的修复方法吗?@TimothyMiller也许,我真的想不出什么聪明的方法,只是显而易见的“
pminuw
带255”@蒂莫西米勒:是的,
packuswb
将其输入视为有符号,但将其输出视为无符号,因此存在一个问题。您可以使用
pand
屏蔽
packuswb
packuswb
之间的偶数字节,以获得与
pminuw
相同的结果。或者使用[-128..127]中的浮点值范围,并将其转换为[0..255]用
paddb
一个128秒的向量来表示范围。我想我解决了这个问题:只要把
packssdw
作为第一步,因为
packuswb
就是这样解释的。我补充了这一点作为一个答案。我觉得我肯定错过了什么,否则我会因为没有想到上次我在写一篇文章时看到的这一点而感到愚蠢
;; SSE2: good for arrays of inputs
cvtps2dq xmm0, [rsi]      ; 4 floats
cvtps2dq xmm1, [rsi+16]   ; 4 more floats
packssdw xmm0, xmm1       ; 8 int16_t

cvtps2dq xmm1, [rsi+32]
cvtps2dq xmm2, [rsi+48]
packssdw xmm1, xmm2       ; 8 more int16_t
                          ; signed because that's how packuswb treats its input
packuswb xmm0, xmm1       ; 16 uint8_t
movdqa   [rdi], xmm0
;; SSE4.1, good for a single vector.  Use the PACK version above for arrays
cvtps2dq   xmm0, xmm0
pmaxsd     xmm0, zeroed-register
pshufb     xmm0, [mask]
movd       [somewhere], xmm0