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

Best way to convert 8 boolean to one byte?

我想把8个boolean保存到一个字节然后保存到一个文件中(这个工作必须对一个非常大的数据完成),我使用了下面的代码但我不确定它是最好的(在速度和space方面):

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

谁能给我更好的代码(更快)?

+= 运算符替换为 |=,这是按位运算(实际上你想在这里做的)。 如果可能,请使用 unsigned char 作为真值。

除非您想手动展开循环 and/or 使用 SIMD 内在函数,我猜这将是编译器最优化的解决方案。

还有一个技巧:structs 可以有位偏移,你可以对它们使用 union 来误用它们作为整数。

顺便说一句:您的代码有问题。你先移动,然后写;你使用加法,但是 signed char,这对于第 7 位和第 8 位肯定会出错(假设你错误地过早移动;如果你这样做正确,只有第 8 位会导致危险)。

关于

can anyone give me a better code(more speed)

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


现在关于给定的代码

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

第一点的“原则上”是因为实际上您的代码不会 运行 在任何具有符号和大小或有符号整数的补码表示的机器上,其中 char已签署。但我认为在代码中准确表达一个人打算做什么通常是个好主意,而不是将其重写为略有不同的东西。而这里的目的是处理位,一个无符号字节。

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

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


显式编码升档和 ORing 的一般方法在我看来很好,因为 std::bitset 不支持从布尔序列初始化(仅从文本字符串初始化),所以它不支持为您节省任何工作。但通常检查标准库是个好主意,它是否支持任何人想做的事情。甚至可能会发生其他人在这里使用我没有想到的一些基于标准库的方法! ;-)

如果您不介意使用 SSE 内在函数,那么 _mm_movemask_epi8 非常适合。它使用 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));

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