Как создать байт из 8 значений типа bool (и наоборот)? - PullRequest
20 голосов
/ 11 декабря 2011

У меня есть 8 bool переменных, и я хочу "объединить" их в байт.

Есть ли простой / предпочтительный способ сделать это?

Как насчет другихнаоборот, расшифровывать байт в 8 отдельных логических значений?

Я пришел к выводу, что это не лишний вопрос, но, поскольку я не смог найти соответствующую документацию через Google, это, вероятно, еще один из тех "nonono все вашиинтуиция - это неправильно ».

Ответы [ 9 ]

21 голосов
/ 11 декабря 2011

Сложный путь:

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 определяется реализацией.

11 голосов
/ 11 декабря 2011

Возможно, вы захотите взглянуть на std::bitset. Он позволяет компактно хранить логические значения в виде битов со всеми ожидаемыми операторами.

Нет смысла дурачиться с переворотом и еще много чего, когда можно абстрагироваться.

5 голосов
/ 11 декабря 2011
#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);
}
4 голосов
/ 08 августа 2018

Крутой способ (с использованием техники умножения )

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 bools b[0] - b[7], чьи наименее значимые биты называются a-h соответственно, которые мы хотим упаковать в один байт. Рассматривая эти 8 последовательных bool как одно 64-битное слово и загружая их, мы получим биты в обратном порядке на машине с прямым порядком байтов. Теперь мы сделаем умножение (здесь точки - нулевые биты)

  |  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 младших битов помещены в верхний байт, нам просто нужно замаскировать оставшиеся биты

Таким образом, магическое число для упаковки будет 0b1000000001000000001000000001000000001000000001000000001000000001 или 0x8040201008040201. Если вы используете машину с прямым порядком байтов, вам нужно использовать магическое число 0x0102040810204080, которое рассчитывается аналогичным образом

Для распаковки мы можем сделать аналогичное умножение

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

После умножения у нас есть необходимые биты в наиболее значимых позициях, поэтому нам нужно замаскировать нерелевантные биты и сдвинуть остатки в наименее значимые позиции. Выходными данными будут байты, содержащие от a до h с прямым порядком байтов.


Эффективный способ

На более новых процессорах x86 с BMI2 есть инструкции PEXT и PDEP . Функцию pack8bools, указанную выше, можно заменить на

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

И функция unpack8bools может быть реализована как

_pdep_u64(b, 0x0101010101010101ULL);
2 голосов
/ 11 декабря 2011

Нет способа упаковать 8 bool переменных в один байт. Существует способ упаковки 8 логических состояний истина / ложь в один байт с использованием Битовая маскировка .

2 голосов
/ 11 декабря 2011
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

хотя вам, вероятно, лучше использовать битовый набор

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

0 голосов
/ 01 января 2018

Хотелось бы отметить, что набирание типа через union s - это UB в C ++ (как rodrigo делает в его ответ . Самый безопасный способ сделать это - 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;
}

Как уже говорили другие, компилятор достаточно умен, чтобы оптимизировать memcpy().

Кстати, это способ, которым Boost печатает на клавиатуре.

0 голосов
/ 01 января 2018

Даже с 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 uint16_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(unint8_t n) {
    return (((BITWISE_OPERATIONS_TYPE) 1) << n); }

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

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

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

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

#ifdef __cplusplus
}
#endif

#endif // __bit_h__

Простой и понятный, без определений классов, и вы можете свободно изменять этот файл в соответствии со своими потребностями ... например, вы можете изменить uint16_t на uint32_t или uint64_t. Хотя и макросы, и функции будут генерировать практически, если не просто идентичный код ... в зависимости от архитектуры машины, которую вы используете для компиляции этого кода.

Итак, как решение вашей проблемы, вот как вы можете pack и unpack их:

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;
}

Я знаю, что это очень поздний ответ ...

0 голосов
/ 11 декабря 2011

Вы бы использовали операцию побитового сдвига и приведение для ее архивирования.функция может работать так:

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

Спасибо Кристиан Рау за исправление s !

...