[英]How to convert a sequence of 32 char (0/1) to 32 bits (uint32_t)?
我有一个从文件读取的char数组(通常长数千个字节),它们都由0和1(不是'0'和'1'组成,在这种情况下,我可以使用strtoul
)。 我想将它们打包为单个位,从而将每个32个字符转换为单个uint32_t。 我应该用32个部分编写一个移位操作,还是有一个更明智的方法?
out[i/32] =
data[i] << 31 |
data[i+1] << 30 |
data[i+2] << 29 |
data[i+3] << 28 |
data[i+4] << 27 |
data[i+5] << 26 |
data[i+6] << 25 |
data[i+7] << 24 |
data[i+8] << 23 |
data[i+9] << 22 |
data[i+10] << 21 |
data[i+11] << 20 |
data[i+12] << 19 |
data[i+13] << 18 |
data[i+14] << 17 |
data[i+15] << 16 |
data[i+16] << 15 |
data[i+17] << 14 |
data[i+18] << 13 |
data[i+19] << 12 |
data[i+20] << 11 |
data[i+21] << 10 |
data[i+22] << 9 |
data[i+23] << 8 |
data[i+24] << 7 |
data[i+25] << 6 |
data[i+26] << 5 |
data[i+27] << 4 |
data[i+28] << 3 |
data[i+29] << 2 |
data[i+30] << 1 |
data[i+31];
如果这种怪异的移位是运行时最快的,那么我就必须坚持下去。
限于x86平台,可以使用PEXT
指令。 它是新处理器上BMI2指令集扩展的一部分。
连续使用32位指令,然后通过移位将结果合并为一个值。
这可能是在Intel处理器上的最佳方法,但是缺点是该指令在AMD Ryzen上速度较慢。
如果您不需要输出位以与输入字节完全相同的顺序出现,但是如果可以以特定的方式对它们进行“交织”,那么一种快速且可移植的方法是占用8个块8个字节(总共64个字节),并将所有LSB合并为一个8字节的值。
就像是:
uint32_t extract_lsbs2(uint8_t (&input)[32]) {
uint32_t t0, t1, t2, t3, t4, t5, t6, t7;
memcpy(&t0, input + 0 * 4, 4);
memcpy(&t1, input + 1 * 4, 4);
memcpy(&t2, input + 2 * 4, 4);
memcpy(&t3, input + 3 * 4, 4);
memcpy(&t4, input + 4 * 4, 4);
memcpy(&t5, input + 5 * 4, 4);
memcpy(&t6, input + 6 * 4, 4);
memcpy(&t7, input + 7 * 4, 4);
return
(t0 << 0) |
(t1 << 1) |
(t2 << 2) |
(t3 << 3) |
(t4 << 4) |
(t5 << 5) |
(t6 << 6) |
(t7 << 7);
}
这会在大多数编译器上生成“不可怕,不完美”的代码。
如果使用uint64_t
而不是uint32_t
则在64位平台上,速度通常会快两倍(假设要转换的总字节数超过32个)。
使用SIMD,您可以像两个指令(对于AVX2,但可以使用任何x86 SIMD ISA一样)轻松地将整个操作向量化:compare和pmovmskb
。
移位是解决此问题的最简单方法。 最好编写反映您实际操作的代码,而不是尝试进行微优化。
所以你想要这样的东西:
char bits[32];
// populate bits
uint32_t value = 0;
for (int i=0; i<32; i++) {
value |= (uint32_t)(bits[i] & 1) << i;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.