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

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

Какую стратегию мне использовать?


person Marco_D    schedule 22.02.2016    source источник
comment
Узнайте о битовых операторах.   -  person too honest for this site    schedule 23.02.2016
comment
Shift для выравнивания каждого поля.   -  person Weather Vane    schedule 23.02.2016
comment
или использовать структуру, объединенную с uint32   -  person user3528438    schedule 23.02.2016
comment
@BLUEPIXY: порядок битов в битовом поле определяется реализацией. Следовательно, не гарантируется, что результат будет иметь желаемый макет.   -  person Dirk Herrmann    schedule 23.02.2016
comment
@DirkHerrmann да, я согласен.   -  person BLUEPIXY    schedule 23.02.2016
comment
Голосование за закрытие этого вопроса, потому что OP, кажется, хочет, чтобы год был в наиболее значимых битах, но не смог выразить это в вопросе.   -  person user3528438    schedule 23.02.2016


Ответы (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;

ИЗМЕНИТЬ

Если вы хотите, чтобы данные упорядочивались от самого старшего бита и укладывались в него

ex:

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

РАСКУПАТЬ:

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;

Если вы хотите, чтобы данные были упорядочены от самого старшего бита и сложены до самого младшего бита

ex:

 00000000000111111111111111111111
            \___________|___|___/
                  year  month day

Используйте вместо этого

ПАКЕТ:

unsigned int time=0;
time |= year << 9;
time |= month << 5;
time |= day;

РАСКУПАТЬ:

int year = (time >> 9) & 0b111111111111;
int month = (time >> 5) & 0b1111;
int day = time & 0b11111;
person AdminXVII    schedule 22.02.2016
comment
Будет ли это двигаться все биты? в этом случае я не смогу восстановить год или месяц из значения - person Marco_D; 23.02.2016
comment
Да, вам также нужно рассказать, как распаковать данные. - person user3528438; 23.02.2016
comment
Но в этом случае биты не так, как мне нужно - person Marco_D; 23.02.2016
comment
Я не хочу показаться ниткой, но для полноты картины константы битовой маски во втором блоке должны иметь префикс 0b. - person Craig Estey; 23.02.2016
comment
@Marco_D, пожалуйста, объясни, почему это не то, что ты хотел. Говоря о битах, первое и последнее сбивают с толку. В машинных терминах первый бит означает бит 0, который является младшим битом. Программисты обычно следуют этому соглашению. Если вы хотите, чтобы год был помещен вверху или в наиболее значимых битах, отредактируйте вопрос, чтобы отразить это. - person user3528438; 23.02.2016

Как уже упоминалось, вам может быть легче работать с битовым полем, чем сдвигом и или для выравнивания битов. Битовое поле объявляется как обычная структура, но позволяет разделять биты между членами структуры. Для года, месяца, дня вы можете использовать что-то вроде:

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 в правильном порядке.

person David C. Rankin    schedule 23.02.2016