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

问题描述 投票:0回答:4

我想将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"

任何人都可以给我一个更好的代码(更快的速度)吗?

c++ performance algorithm byte bit
4个回答
4
投票

如果您不介意使用 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));

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


2
投票

关于

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

您应该测量。对序列化到文件的速度的大部分影响是 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
  • 作为字节类型。
    再次原则上使用位级 OR(
  • |
  • 运算符)。
    使用前缀
  • ++
  • ,是的,原则上也是如此。
    
    
    
  • 第一点的“原则上”是因为实际上你的代码不会在任何具有符号和数值或有符号整数的补码表示的机器上运行,其中
char

是有符号的。但我认为在代码中准确表达人们想要做什么通常是一个好主意,而不是将其重写为稍微不同的东西。这里的目的是处理位,一个无符号字节。


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

前缀

++

的“原则上”是因为在实践中,当不使用表达式结果时,编译器会将基本类型的前缀和后缀

++
优化为完全相同的机器代码。但同样,通常最好写出一个人想要表达的内容。当您从未使用过原始值时,要求原始值(后缀
++
)只会误导代码读者 - 并且与表示为加法的位级 OR 一样,纯增量表示为后缀
++
在其他情况下,例如,可能会绊倒你,咬你的**。使用迭代器。

显式编码上移和 ORing 的一般方法在我看来很好,因为
std::bitset

不支持从布尔值序列初始化(仅从文本字符串初始化),因此它不会为您节省任何工作。但一般来说,检查标准库是个好主意,看看它是否支持人们想要做的任何事情。甚至可能会有其他人用一些我没有想到的基于标准库的方法插话! ;-)

    


0
投票

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



-1
投票
+=

运算符替换为

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

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

还有另一个技巧:

struct

可以有位偏移,你可以在它们上使用

union
来将它们误用为整数。

顺便说一句:你的代码有错误。你先移动,然后写作;你使用加法,但是使用

signed char

,这对于第 7 和第 8 位肯定会出错(假设你错误地过早移位;如果你做得正确,只有第 8 位会造成危险)。

    

© www.soinside.com 2019 - 2024. All rights reserved.