Как установить конкретные биты?

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

Пример:

uint16_t field = 0;

Это будет означать, что все биты равны нулю: 0000 0000 0000 0000

Теперь я получаю некоторые значения, которые мне нужно установить на определенных позициях.

val1=1; val2=2, val3=0, val4=4, val5=0;

Структура установки битов следующая

0|000|  0000| 0000 000|0

val1 должен быть установлен на первый бит слева. так что его только один или ноль.

val2 следует установить на следующие три бита. val3 на следующих четырех битах. val4 на следующих семи битах и val5 один последний бит.

Результат будет таким:
1010 0000 0000 1000

Я только узнал, как на один конкретный бит, но не «группы». (сдвиг или бит)

У кого-нибудь есть идеи, как решить эту проблему?

1

Решение

Существует (как минимум) два основных подхода. Можно было бы создать структуру с некоторыми битовыми полями:

struct bits {
unsigned a : 1;
unsigned b : 7;
unsigned c : 4;
unsigned d : 3;
unsigned e : 1;
};

bits b;

b.a = val1;
b.b = val2;
b.c = val3;
b.d = val4;
b.e = val5;

Чтобы получить 16-битное значение, вы можете (для одного примера) создать объединение этой структуры с uint16_t, Только одна небольшая проблема: стандарт не гарантирует, в каком порядке окажутся битовые поля, когда вы посмотрите на 16-битное значение. Например, вам может понадобиться изменить порядок, который я указывал выше, чтобы получить порядок от самых младших разрядов, которые вам действительно нужны (но смена компиляторов может снова испортить все).

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

int16_t result = val1 | (val2 << 1) | (val3 << 8) | (val4 << 12) | (val5 << 15);

На данный момент я предположил, что каждый из входов начинается в правильном диапазоне (т.е. имеет значение, которое может быть представлено в выбранном количестве битов). Если есть вероятность, что это может быть неправильно, вам нужно сначала замаскировать его на правильное количество бит. Обычный способ сделать это что-то вроде:

uint16_t result = input & ((1 << num_bits) - 1);

Если вам интересно узнать математику, она работает следующим образом. Предположим, что мы хотим обеспечить ввод в 4 бита. перевод 1 осталось 4 бита 00010000 (в двоичном виде). Вычитание одного из этого затем очищает один установленный бит и устанавливает все менее значимые биты, чем это, давая 00001111 для нашего примера. Это дает нам первые наименее значимые биты. Когда мы делаем немного AND между этим и входом любые более высокие биты, которые были установлены на входе, очищаются в результате.

7

Другие решения

Одним из решений было бы установить значение K-бита, начиная с N-го бита field как:

uint16_t value_mask = ((1<<K)-1) << N; // for K=4 and N=3 will be 00..01111000
field = field & ~value_mask; // zeroing according bits inside the field
field = field | ((value << N) & value_mask); // AND with value_mask is for extra safety

Или, если вы можете использовать struct вместо uint16_t, вы можете использовать Битовые поля и пусть компилятор выполнит все эти действия за вас.

1

finalvle = 0;
finalvle = (val1&0x01)<<15;
finalvle += (val2&0x07)<<12;
finalvle += (val3&0x0f)<<8
finalvle += (val4&0xfe)<<1;
finalvle += (val5&0x01);
0

Вы можете использовать побитовые или и операторы сдвига для достижения этой цели.

Используйте сдвиг << «сдвинуть байты влево»:

int i = 1;  // ...0001
int j = i << 3 // ...1000

Затем вы можете использовать побитовое или | поставить его в нужном месте (при условии, что у вас есть все нули в битах, которые вы пытаетесь перезаписать).

int k = 0;  // ...0000
k |= i // ...0001
k |= j // ...1001

Изменить: Обратите внимание, что ответ @ Inspired также объясняется обнулением определенной области битов. В целом это объясняет, как вы будете правильно его реализовывать.

0

попробуйте этот код:

uint16_t shift(uint16_t num, int shift)
{
return num | (int)pow (2, shift);
}

где shift — это позиция бита, которую вы хотите установить

0
По вопросам рекламы [email protected]