我想将8个布尔值保存到一个字节,然后将其保存到一个文件中(这项工作必须针对非常大的数据完成),我使用了以下代码,但我不确定它是最好的代码(在速度和空间):
int bits[]={1,0,0,0,0,1,1,1};
char a='\0';
for (int i=0;i<8;i++){
a=a<<1;
a+=bits[i]
}
//and then save "a"
任何人都可以给我一个更好的代码(更快的速度)吗?
如果您不介意使用 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));
这假设数组是字符数组。如果你不能做到这一点,就需要更多的负载和包装,这会变得有点烦人。
关于
”谁能给我一个更好的代码(更快)
您应该测量。对序列化到文件的速度的大部分影响是 I/O 速度。您对这些位所做的操作可能会产生“无法测量”的小影响,但如果有任何影响,那么这很可能主要受到布尔值序列的原始表示的影响。
现在关于给定的代码int bits[]={1,0,0,0,0,1,1,1};
char a='\0';
for (int i=0;i<8;i++){
a=a<<1;
a+=bits[i]
}
//and then save "a"
unsigned char
|
++
char
是有符号的。但我认为在代码中准确表达人们想要做什么通常是一个好主意,而不是将其重写为稍微不同的东西。这里的目的是处理位,一个无符号字节。
位级 OR 的“原则上”是因为对于这种特殊情况,位级 OR 和加法之间没有实际差异。但总的来说,用代码编写要表达的意思是个好主意。然后编写位级 OR 作为补充是没有好处的:它甚至可能会让你绊倒,在其他上下文中咬住你。
前缀
++
的“原则上”是因为在实践中,当不使用表达式结果时,编译器会将基本类型的前缀和后缀
++
优化为完全相同的机器代码。但同样,通常最好写出一个人想要表达的内容。当您从未使用过原始值时,要求原始值(后缀 ++
)只会误导代码读者 - 并且与表示为加法的位级 OR 一样,纯增量表示为后缀 ++
在其他情况下,例如,可能会绊倒你,咬你的**。使用迭代器。显式编码上移和 ORing 的一般方法在我看来很好,因为
std::bitset
不支持从布尔值序列初始化(仅从文本字符串初始化),因此它不会为您节省任何工作。但一般来说,检查标准库是个好主意,看看它是否支持人们想要做的任何事情。甚至可能会有其他人用一些我没有想到的基于标准库的方法插话! ;-)
int bits[] = {1, 0, 0, 0, 0, 1, 1, 1};
uint8_t result = bits[0] << 7 | bits[1] << 6 | bits[2] << 5 | bits[3] << 4 |
bits[4] << 3 | bits[5] << 2 | bits[6] << 1 | bits[7];
您可以通过以二进制格式执行结果的 printf 输出来验证结果是否正确,如下所示:
char buffer[1];
itoa(result, buffer, 2);
printf("binary: %s\n", buffer);
将打印出:
binary: 10000111
+=
运算符替换为
|=
,这是按位运算(实际上是您想要在此处执行的操作)。
如果可能的话,使用 unsigned char
作为您的真值。除非您想手动展开循环和/或使用 SIMD 内在函数,否则我猜这将是编译器最优化的解决方案。
还有另一个技巧:
struct
可以有位偏移,你可以在它们上使用
union
来将它们误用为整数。 顺便说一句:你的代码有错误。你先移动,然后写作;你使用加法,但是使用
signed char
,这对于第 7 和第 8 位肯定会出错(假设你错误地过早移位;如果你做得正确,只有第 8 位会造成危险)。