将8个布尔值转换为一个字节的最佳方式



我想将8个布尔值保存到一个字节,然后将其保存到文件中(这项工作必须针对非常大的数据完成),我使用了以下代码,但我不确定它是最好的(就速度和空间而言):

int bits[]={1,0,0,0,0,1,1,1};
char a='';
for (int i=0;i<8;i++){
  a=a<<1;
  a+=bits[i]
}
//and then save "a"

有人能给我一个更好的代码(更快)吗?

如果您不介意使用SSE内部函数,那么_mm_movemask_ep8非常适合。它使用16个字节,但您可以将其他字节设置为零。

例如(未测试)

__m128i values = _mm_loadl_epi64((__m128i*)array);
__m128i order = _mm_set_epi8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                             0, 1, 2, 3, 4, 5, 6, 7);
values = _mm_shuffle_epi8(values, order);
int result = _mm_movemask_epi8(_mm_slli_epi32(values, 7));

这假设数组是一个字符数组。如果你不能做到这一点,它需要更多的负载和包装,这会变得有点烦人。

关于

"有人能给我一个更好的代码(更快)吗

您应该测量。对序列化到文件的速度的大部分影响是i/o速度。你对比特的处理可能会产生无法测量的小影响,但如果它有任何影响,那么这可能主要受布尔值序列的原始表示的影响。


现在关于给定的代码

int bits[]={1,0,0,0,0,1,1,1};
char a='';
for (int i=0;i<8;i++){
a=a<<1;
a+=bits[i]
}
//and then save "a"
  • 原则上使用unsigned char作为字节类型
  • 原则上再次使用位级OR,即|运算符
  • 使用前缀++,是的,原则上也是这样

“原则上";第一点是因为在实践中,您的代码不会在任何具有符号和幅度或有符号整数的补码表示的机器上运行,其中char是有符号的。但我认为在代码中准确地表达自己的意图通常是一个好主意,而不是将其重写为稍微不同的东西。这里的意图是处理位,一个无符号字节。

“原则上";对于位电平OR是因为对于这种特殊情况,位电平OR和加法之间没有实际区别。但总的来说,用代码来表达自己的意思是个好主意。然后写一个位级OR作为加法是没有好处的:在其他情况下,它甚至可能会绊倒你,咬你一口。

“原则上";前缀++是因为在实践中,当不使用表达式结果时,编译器会将基本类型的前缀和后缀++优化为相同的机器代码。但同样,写下一个人想要表达的内容通常会更好。当您从未使用过原始值时,要求原始值(后缀++)只是在误导代码的读者–与用加法表示的位级OR一样,用后缀++表示的纯增量可能会让你绊倒,在其他上下文中咬你一口,例如迭代器。


在我看来,显式编码向上移位和"或"运算的通用方法似乎很好,因为std::bitset不支持从布尔值序列初始化(只支持从文本字符串初始化),所以它不会为您节省任何工作。但一般来说,检查标准库是个好主意,它是否支持任何人想做的事情。甚至可能会有其他人加入进来,提出一些我没有想到的基于库的标准方法!;-)

|=替换+=运算符,这是按位操作(实际上也是您想要在此处执行的操作)。如果可能的话,使用unsigned char作为真实值。

除非您想手动展开循环和/或使用SIMD内部函数,否则我想这将是最适合编译器优化的解决方案。

还有另一个技巧:struct可以有位偏移,您可以在它们上使用union将它们误用为int。

顺便说一句:你的代码有漏洞。你先转换,然后写;您使用加法,但使用signed char,第7位和第8位肯定会出错(如果您错误地移位太早;如果操作得当,只有第8位会造成危险)。

相关内容

最新更新