Хранение данных в переменной с помощью битов

Я пытаюсь сохранить дату в неподписанной переменной 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 в правильном порядке.

Другие вопросы по тегам