半字节解包为字节:保留符号的高效实现方法问询
解决方案:符号扩展半字节到字节(SSE/AVX)
针对你的需求——将4位有符号半字节补码符号扩展为8位,以下是不同指令集下的高效实现方式:
1. AVX2 环境(推荐,指令最少)
AVX2 提供了直接操作8位元素的算术右移指令 _mm_srai_epi8,可以一步完成符号扩展:
const __m128i mask = _mm_set1_epi8(0x0F); // 提取低4位半字节并符号扩展 __m128i vec_unpack_one = _mm_and_si128(vec, mask); vec_unpack_one = _mm_slli_epi8(vec_unpack_one, 4); // 将4位符号位移到8位元素的最高位 vec_unpack_one = _mm_srai_epi8(vec_unpack_one, 4); // 算术右移,符号扩展高4位 // 提取高4位半字节并符号扩展 __m128i vec_unpack_two = _mm_and_si128(_mm_srli_epi16(vec, 4), mask); vec_unpack_two = _mm_slli_epi8(vec_unpack_two, 4); vec_unpack_two = _mm_srai_epi8(vec_unpack_two, 4);
原理:
- 先将4位半字节左移4位,让原半字节的符号位(第3位)成为8位元素的最高位(第7位)
- 算术右移4位时,CPU会用符号位填充高4位,自动完成补码的符号扩展。比如原半字节
0x0B(1011,-5)左移后变为0xB0(10110000),算术右移4位后得到0xFB(11111011,-5),完全符合需求。
2. SSE2 环境(兼容旧CPU)
SSE2 没有8位算术右移指令,可通过16位算术右移模拟,或用掩码加法实现:
方法一:16位算术右移模拟
const __m128i mask = _mm_set1_epi8(0x0F); // 处理低4位半字节 __m128i vec_unpack_one = _mm_and_si128(vec, mask); vec_unpack_one = _mm_unpacklo_epi8(vec_unpack_one, _mm_setzero_si128()); // 8位转16位 vec_unpack_one = _mm_slli_epi16(vec_unpack_one, 12); // 符号位移到16位元素最高位 vec_unpack_one = _mm_srai_epi16(vec_unpack_one, 12); // 16位算术右移完成符号扩展 vec_unpack_one = _mm_packepi16(vec_unpack_one, vec_unpack_one); // 16位转回8位 // 处理高4位半字节 __m128i vec_unpack_two = _mm_and_si128(_mm_srli_epi16(vec, 4), mask); vec_unpack_two = _mm_unpacklo_epi8(vec_unpack_two, _mm_setzero_si128()); vec_unpack_two = _mm_slli_epi16(vec_unpack_two, 12); vec_unpack_two = _mm_srai_epi16(vec_unpack_two, 12); vec_unpack_two = _mm_packepi16(vec_unpack_two, vec_unpack_two);
方法二:掩码加法实现
const __m128i mask = _mm_set1_epi8(0x0F); const __m128i sign_bit = _mm_set1_epi8(0x08); const __m128i extend_mask = _mm_set1_epi8(0xF0); // 处理低4位半字节 __m128i vec_unpack_one = _mm_and_si128(vec, mask); __m128i is_negative = _mm_cmpeq_epi8(_mm_and_si128(vec_unpack_one, sign_bit), sign_bit); vec_unpack_one = _mm_or_si128(vec_unpack_one, _mm_and_si128(is_negative, extend_mask)); // 处理高4位半字节 __m128i vec_unpack_two = _mm_and_si128(_mm_srli_epi16(vec, 4), mask); is_negative = _mm_cmpeq_epi8(_mm_and_si128(vec_unpack_two, sign_bit), sign_bit); vec_unpack_two = _mm_or_si128(vec_unpack_two, _mm_and_si128(is_negative, extend_mask));
原理:
- 通过
_mm_cmpeq_epi8判断半字节的符号位是否为1,生成全1的掩码字节 - 将掩码与
0xF0相与,得到符号扩展所需的高4位填充值,再与原半字节按位或完成扩展。
内容的提问来源于stack exchange,提问作者Srihari S
相关产品推荐
相关产品推荐

