Хранение данных в переменной с помощью битов
Я пытаюсь сохранить дату в неподписанной переменной int, мне нужно сохранить дату следующим образом:
- Биты с 11 по 0 за год (от 0 до 4095)
- Биты с 15 по 12 за месяц (как если бы они были битами от 0 до 3, поэтому я могу сохранить значение от 0 до 11)
- Биты с 20 до 16 в течение дня (0-31)
Сохранение года это не проблема, так как я делаю это:
unsigned int year=0;
year=year|2016
Но тогда я понятия не имею, я должен поставить месяц и день. Как я могу поместить число, подобное 10, в биты от 12 до 15, предполагая, что бит 12 имеет значение 1, бит 13 - значение 2 и т. Д.
Какую стратегию я должен использовать?
2 ответа
В C вы можете сдвигать биты в обе стороны, используя a >> b
или же a << b
где a - число сдвигов, а b - количество сдвигов. Вставленные биты будут равны 0 с.
В вашем случае это было бы
unsigned int time=0;
time |= year;
time |= month << 12;
time |= day << 16;
Чтобы распаковать его, вам просто нужно сдвинуть time
в другом направлении и &
это количество разыскиваемых битов:
int year = time & 0b111111111111;
int month = (time >> 12) & 0b1111;
int day = (time >> 16) & 0b11111;
РЕДАКТИРОВАТЬ
Если вы хотите, чтобы данные были упорядочены из старшего значащего бита и помещены в него
например:
11111111111111111111100000000000
\___________|___|___/
year month day
вам просто нужно сдвинуть данные соответственно
ПАКЕТ:
short int_length = sizeof(unsigned int); //usually 32
unsigned int time=0;
time |= year << (int_length - 12);
time |= month << (int_length - 16);
time |= day << (int_length - 21);
UNPACK:
short int_length = sizeof(unsigned int); //usually 32
int year = (time >> (int_length - 12)) & 0b111111111111;
int month = (time >> (int_length - 16)) & 0b1111;
int day = (time >> (int_length - 21)) & 0b11111;
Если вы хотите, чтобы данные упорядочивались от старшего значащего бита и располагались к младшему значащему биту
например:
00000000000111111111111111111111
\___________|___|___/
year month day
Используйте вместо этого
ПАКЕТ:
unsigned int time=0;
time |= year << 9;
time |= month << 5;
time |= day;
UNPACK:
int year = (time >> 9) & 0b111111111111;
int month = (time >> 5) & 0b1111;
int day = time & 0b11111;
Как уже упоминали другие, вам может показаться, что с битовым полем легче работать, чем смещением и / или выравниванием битов. Битовое поле объявляется как нормальная структура, но позволяет разделять биты между членами структуры. Для вашего года, месяца, дня вы можете использовать что-то похожее на:
typedef struct { /* bitfield for year, month, day */
unsigned year : 11,
mon : 4,
day : 4;
} datebits;
datebits
Структура разделяет первые 11 битов за год, следующие 4 за месяц и последние 4 за день. Вы используете его, как и любую другую структуру. Вот короткий пример:
#include <stdio.h>
typedef struct { /* bitfield for year, month, day */
unsigned year : 11,
mon : 4,
day : 4;
} datebits;
typedef union { /* union to avoid violating strict aliasing */
datebits d; /* when shifting datebits to print binary bits */
unsigned u; /* (this is only for putchar bit output) */
} duu;
int main (void) {
unsigned i = 19;
datebits d; /* declare bitfield */
duu du;
d.year = 1999; /* fill bitfield */
d.mon = 12;
d.day = 2;
du.d = d;
printf ("\n year : %u month : %u day : %u\n\n (bits in memory) d : ",
d.year, d.mon, d.day);
while (i--) /* verification of each bit in memory */
putchar ((du.u >> i) & 1 ? '1' : '0');
putchar ('\n');
return 0;
}
Выход
$ ./bin/bitfield_datebits
year : 1999 month : 12 day : 2
(bits in memory) d : 0010110011111001111
(примечание: объединение просто для облегчения двоичной печати отдельных битов, оно не требуется для нормального использования datebits
).
Как вы можете видеть, ваш день 0010
твой месяц 1100
и год 11111001111
в правильном порядке.