Как создать байт из 8 значений bool (и наоборот)?
У меня 8 bool
переменные, и я хочу "объединить" их в байт.
есть ли простой / предпочтительный метод для этого?
Как насчет наоборот, декодирования байта в 8 отдельных логических значений?
Я прихожу, предполагая, что это не необоснованный вопрос, но поскольку я не смог найти соответствующую документацию через Google, это, вероятно, еще один из тех случаев "nonono all your intuition is wrong".
9 ответов
трудный путь:
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
определяется реализацией.
вы можете посмотреть в std::bitset
. Это позволяет компактно хранить логические значения в виде битов со всеми операторами, которые вы ожидаете.
нет смысла дурачиться с Бит-листать и еще много чего, когда вы можете абстрагироваться.
#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);
}
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
нет способа упаковать 8 bool
переменных в один байт. Существует способ упаковки 8 логических состояний true / false в один байт с помощью Bitmasking.
вы бы использовали операцию побитового сдвига и приведение к архивированию. функция может работать следующим образом:
unsigned char toByte(bool *bools)
{
unsigned char byte = ;
for(int i = 0; i < 8; ++i) byte |= ((unsigned char) bools[i]) << i;
return byte;
}
спасибо Кристиан Рау исправления s!
Я хотел бы отметить, что тип каламбура через union
s-UB в C++ (as Родриго и в ответ. Самый безопасный способ сделать это -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;
}
как говорили другие, компилятор достаточно умен, чтобы оптимизировать out memcpy()
.
кстати, это то, как Boost делает тип каламбуров.
прохладный способ (с помощью техника умножения)
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]
to b[7]
чьи наименее значимые биты называются A-h соответственно, которые мы хотим упаковать в один байт. Лечим те 8 подряд bool
S как одно 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 в little endian.
эффективный способ
на более новых процессорах x86 с BMI2 здесь PEXT и PDEP инструкции для этой цели. The pack8bools
функция выше может быть заменена на
_pext_u64(*((uint64_t*)a), 0x0101010101010101ULL);
и unpack8bools
функция может быть реализована как
_pdep_u64(b, 0x0101010101010101ULL);
даже с 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
to uint_fast32_t
чтобы компилятор использовал подходящее место с быстрым доступом, которое имеет размер не менее 32 бит вместо 64 бит. Хотя и макросы, и функции будут производить почти, если не просто идентичный код ... в зависимости от архитектуры машины вы используете для компиляции кода.
так как решение вашей проблемы вы можете создать get
и set
методы, вроде этого:
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);
}
и вот как вы можете 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;
}
Я знаю, что это очень поздний ответ ...