如何用8个bool值创建一个字节(反之亦然)?

时间:2011-12-11 00:53:37

标签: c++ boolean bit-manipulation bit-packing

我有8个bool变量,我想将它们“合并”成一个字节。

有一种简单/首选的方法吗?

另一种方法是如何将一个字节解码为8个独立的布尔值?

我认为这不是一个不合理的问题,但由于我无法通过谷歌找到相关文档,这可能是另一个“非你所有直觉都是错误的”案例。

9 个答案:

答案 0 :(得分:20)

艰难的道路:

unsigned char ToByte(bool b[8])
{
    unsigned char c = 0;
    for (int i=0; i < 8; ++i)
        if (b[i])
            c |= 1 << i;
    return c;
}

void FromByte(unsigned char c, bool b[8])
{
    for (int i=0; i < 8; ++i)
        b[i] = (c & (1<<i)) != 0;
}

或者很酷的方式:

struct Bits
{
    unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
};
union CBits
{
    Bits bits;
    unsigned char byte;
};

然后您可以分配给联盟的一个成员并从另一个成员读取。但请注意Bits中位的顺序是实现定义的。

答案 1 :(得分:10)

您可能需要查看std::bitset。它允许您将布尔值紧凑地存储为位,以及您期望的所有运算符。

当你可以抽象出来时,没有必要愚弄比特翻转等等。

答案 2 :(得分:5)

#include <stdint.h>   // to get the uint8_t type

uint8_t GetByteFromBools(const bool eightBools[8])
{
   uint8_t ret = 0;
   for (int i=0; i<8; i++) if (eightBools[i] == true) ret |= (1<<i);
   return ret;
}

void DecodeByteIntoEightBools(uint8_t theByte, bool eightBools[8])
{
   for (int i=0; i<8; i++) eightBools[i] = ((theByte & (1<<i)) != 0);
}

答案 3 :(得分:3)

很酷的方法(使用multiplication technique

inline uint8_t pack8bools(bool* a)
{
    uint64_t t = *((uint64_t*)a);
    return 0x8040201008040201*t >> 56;
}

void unpack8bools(uint8_t b, bool* a)
{
    auto MAGIC = 0x8040201008040201ULL;
    auto MASK  = 0x8080808080808080ULL;
    *((uint64_t*)a) = ((MAGIC*b) & MASK) >> 7;
}

当然,您可能需要确保bool数组正确对齐了8个字节,以避免性能下降和/或UB


它们如何工作?

假设我们有8个布尔b[0]b[7],它们的最低有效位分别命名为a-h,我们希望将其打包为一个字节。将这8个连续的bool视为一个64位字并加载它们,我们将在Little-endian机器中以相反的顺序获取这些位。现在我们要做一个乘法(这里的点是零位)

  |  b7  ||  b6  ||  b4  ||  b4  ||  b3  ||  b2  ||  b1  ||  b0  |
  .......h.......g.......f.......e.......d.......c.......b.......a
x 1000000001000000001000000001000000001000000001000000001000000001
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  ↑......h.↑.....g..↑....f...↑...e....↑..d.....↑.c......↑b.......a
  ↑.....g..↑....f...↑...e....↑..d.....↑.c......↑b.......a
  ↑....f...↑...e....↑..d.....↑.c......↑b.......a
+ ↑...e....↑..d.....↑.c......↑b.......a
  ↑..d.....↑.c......↑b.......a
  ↑.c......↑b.......a
  ↑b.......a
  a       
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
= abcdefghxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

添加了箭头,以便更轻松地查看魔术数字中设置位的位置。此时,最低有效位已放入8个字节,我们只需要屏蔽掉其余位即可

因此打包的魔数将为0b10000000010000000010000000010000000010000000010000000010000000010x8040201008040201。如果您使用的是大端字节序计算机,则需要使用以类似方式计算的幻数0x0102040810204080

对于拆箱,我们可以进行类似的乘法

  |  b7  ||  b6  ||  b4  ||  b4  ||  b3  ||  b2  ||  b1  ||  b0  |
                                                          abcdefgh
x 1000000001000000001000000001000000001000000001000000001000000001
__________________________________________________________________
= h0abcdefgh0abcdefgh0abcdefgh0abcdefgh0abcdefgh0abcdefgh0abcdefgh
& 1000000010000000100000001000000010000000100000001000000010000000
__________________________________________________________________    
= h0000000g0000000f0000000e0000000d0000000c0000000b0000000a0000000

相乘后​​,我们在最高有效位置有所需的位,因此我们需要屏蔽掉无关的位并将其余的位移到最低有效的位置。输出将是在小端序中包含a到h的字节。


有效方式

在带有BMI2的较新x86 CPU上,有PEXTPDEP条指令用于此目的。上面的pack8bools函数可以替换为

_pext_u64(*((uint64_t*)a), 0x0101010101010101ULL);

unpack8bools函数可以实现为

_pdep_u64(b, 0x0101010101010101ULL);

答案 4 :(得分:2)

bool a,b,c,d,e,f,g,h;
//do stuff
char y= a<<7 | b<<6 | c<<5 | d<<4 | e <<3 | f<<2 | g<<1 | h;//merge

尽管你最好使用bitset

http://www.cplusplus.com/reference/stl/bitset/bitset/

答案 5 :(得分:2)

无法将8个bool变量打包到一个字节中。有一种方法可以使用Bitmasking在一个字节中打包8个逻辑真/假状态。

答案 6 :(得分:0)

您可以使用按位移位操作和强制转换来存档它。一个函数可以像这样工作:

unsigned char toByte(bool *bools)
{
    unsigned char byte = \0;
    for(int i = 0; i < 8; ++i) byte |= ((unsigned char) bools[i]) << i;
    return byte;
}

感谢Christian Rau进行更正 s

答案 7 :(得分:0)

我想请注意,通过union的类型惩罚是C ++中的UB(正如 rodrigo his answer中所做的那样。最安全的方法是{{} 1}}

memcpy()

正如其他人所说,编译器非常聪明,可以优化struct Bits { unsigned b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; }; unsigned char toByte(Bits b){ unsigned char ret; memcpy(&ret, &b, 1); return ret; }

顺便说一下,这就是Boost打字的方式。

答案 8 :(得分:-1)

即使使用C ++,我也在使用这个头文件:

#ifndef __bit_h__
#define __bit_h__

#ifdef __cplusplus
#include <cstdint>
extern "C" {
#else
#include <stdint.h>
#endif

#ifndef BITWISE_OPERATIONS_TYPE
#define BITWISE_OPERATIONS_TYPE uint_fast64_t
#endif

// gives a value with only the nth bit set
// usage: int flags = 10000b;
//        bool enabled = (flags & BIT(4)) ? true : false; // result is true
#define BIT(n) (((BITWISE_OPERATIONS_TYPE) 1) << (n))

// gives the input with the nth bit set
// usage: flags = BIT_SET(flags, 3);
// result: flags = 0b11000
#define BIT_SET(in, n) (in | BIT(n))

// gives the input with the nth bit clear
// usage: flags = BIT_CLR(flags, 3);
// result: flags = 0b10000
#define BIT_CLR(in, n) (in & ~BIT(n))

// gives the nth bit only of the input
// usage: bool both_clr = !(BIT_GET(flags1, 3) & BIT_GET(flags2, 3));
// result: both_clr = true (lets say `flags1, flags2 = 0, 0`)
#define BIT_GET(in, n) (in & BIT(n))

// gives 1 if the nth bit of the input is set else gives 0
// usage: if(IS_BIT_SET(flags, 3)) { /*... it will not run */ }
#define IS_BIT_SET(in, n) (BIT_GET(in, n) > 0)

static inline BITWISE_OPERATIONS_TYPE bit(unint_fast8_t n) {
    return (((BITWISE_OPERATIONS_TYPE) 1) << n); }

static inline BITWISE_OPERATIONS_TYPE bit_set(BITWISE_OPERATIONS_TYPE in, unint_fast8_t n) {
    return (in | bit(n)); }

static inline BITWISE_OPERATIONS_TYPE bit_clr(BITWISE_OPERATIONS_TYPE in, unint_fast8_t n) {
    return (in & ~bit(n)); }

static inline BITWISE_OPERATIONS_TYPE bit_get(BITWISE_OPERATIONS_TYPE in, unint_fast8_t n) {
    return (in & bit(n)); }

static inline unint_fast8_t is_bit_set(BITWISE_OPERATIONS_TYPE in, unint_fast8_t n) {
    return (bit_get(in, n) > 0); }

#ifdef __cplusplus
}
#endif

#endif // __bit_h__

简单易懂,没有类定义,您可以根据需要自由修改此文件...例如,您可以将uint_fast64_t更改为uint_fast32_t以让编译器使用具有至少32位大小而不是64位的快速访问的适当位置。虽然宏和函数几乎都会产生相同的代码......但这取决于您用来编译此代码的机器的体系结构。

因此,作为问题的解决方案,您实际上可以创建getset方法,如下所示:

bool get(const uint_fast8_t& nth) { // or `const unsigned char&` or `const char&`
    return IS_BIT_SET(this->somewhere, nth);
}

void set(const uint_fast8_t& nth) { // or `const unsigned char&` or `const char&`
    this->flags = BIT_SET(this->somewhere, nth);
}

这就是packunpack他们的方式:

static char pack8bit(bool* bools) { // `char` for an 8bit return (output) value and `bool*` for the input 8 bools ... should be unrolled args ?!?!
    char buff = 0;
    for(unsigned char i = 0; i < 8; ++i)
        buff = (bools[i]) ? bit_set(buff, i) : bit_clr(buff, i);
    return buff;
}

static void unpack8bit(const char& from, bool* bools) { // `from` for the packed input and `bool*` for the output 8 bools ... should be unrolled args ?!?!
    for(unsigned char i = 0; i < 8; ++i)
        bools[i] = is_bit_set(from, i) ? true : false;
}

我知道这是一个非常晚的答案......