Как извлечь конкретные 'n' битов 32-разрядного целого числа без знака в C? - PullRequest
29 голосов
/ 04 ноября 2011

Может кто-нибудь сказать мне, как извлечь 'n' конкретных битов из 32-разрядного целого числа без знака в C.

Например, скажем, я хочу первые 17 бит 32-разрядного значения;что я должен делать?
Я предполагаю, что должен использовать оператор модуля, и я попробовал это и смог получить последние 8 бит и последние 16 бит как

unsigned last8bitsvalue=(32 bit integer) % 16
unsigned last16bitsvalue=(32 bit integer) % 32

Этоправильный?Есть ли лучший и более эффективный способ сделать это?

Ответы [ 8 ]

41 голосов
/ 04 ноября 2011

Вместо того, чтобы думать об этом как об «извлечении», мне нравится думать об этом как о «выделении». Как только нужные биты изолированы, вы можете делать с ними что хотите.

Чтобы изолировать любой набор битов, примените маску AND.

Если вам нужны последние X битов значения, можно использовать простой прием.

unsigned  mask;
mask = (1 << X) - 1;
lastXbits = value & mask;

Если вы хотите выделить серию битов Х в середине значения, начиная с startBit ...

unsigned  mask;
mask = ((1 << X) - 1) << startBit;
isolatedXbits = value & mask;

Надеюсь, это поможет.

27 голосов
/ 04 ноября 2011

Если вы хотите указать n битов, вы можете сначала создать битовую маску, а затем AND с вашим номером, чтобы получить нужные биты.

Простая функция для создания маски от бита до б.

unsigned createMask(unsigned a, unsigned b)
{
   unsigned r = 0;
   for (unsigned i=a; i<=b; i++)
       r |= 1 << i;

   return r;
}

Вы должны проверить, что a <= b. </p>

Если вы хотите, чтобы биты с 12 по 16 вызывали функцию, а затем просто & (логическое И) r со своим номером N

r = createMask(12,16);
unsigned result = r & N;

Если вы хотите, вы можете изменить результат. Надеюсь, это поможет

8 голосов
/ 04 ноября 2011

Модуль работает, чтобы получить младшие биты (только), хотя я думаю, что value & 0x1ffff выражает «взять младшие 17 бит» более непосредственно, чем value % 131072, и поэтому легче понять, как это сделать.

Старшие 17 битов 32-битного значения без знака будут value & 0xffff8000 (если вы хотите, чтобы они все еще находились на своих верхних позициях), или value >> 15, если вы хотите, чтобы старшие 17 битов значения в нижних 17 битахрезультат.

7 голосов
/ 04 ноября 2011

Если вам нужно X последних битов вашего целого числа, используйте двоичную маску :

unsigned last8bitsvalue=(32 bit integer) & 0xFF
unsigned last16bitsvalue=(32 bit integer) & 0xFFFF
4 голосов
/ 12 мая 2016

Существует одна BEXTR (извлечение битового поля (с регистром)) x86 для процессоров Intel и AMD и UBFX для ARM. Существуют встроенные функции, такие как _bextr_u32() (ссылка требует входа), которые позволяют явно вызывать эту инструкцию.

Они реализуют код (source >> offset) & ((1 << n) - 1) C: получают n непрерывные биты от source, начиная с бита offset. Вот полное определение функции, которая обрабатывает крайние случаи:

#include <limits.h>

unsigned getbits(unsigned value, unsigned offset, unsigned n)
{
  const unsigned max_n = CHAR_BIT * sizeof(unsigned);
  if (offset >= max_n)
    return 0; /* value is padded with infinite zeros on the left */
  value >>= offset; /* drop offset bits */
  if (n >= max_n)
    return value; /* all  bits requested */
  const unsigned mask = (1u << n) - 1; /* n '1's */
  return value & mask;
}

Например, чтобы получить 3 бит из 2273 (0b100011100001), начиная с 5 -го бита, вызовите getbits(2273, 5, 3) - это извлекает 7 (0b111).

Например, скажем, я хочу первые 17 бит 32-битного значения; что мне делать?

unsigned first_bits = value & ((1u << 17) - 1); // & 0x1ffff

Предполагается, что CHAR_BIT * sizeof(unsigned) равно 32 в вашей системе.

Я предполагаю, что должен использовать оператор модуля, и я попробовал его и смог получить последние 8 бит и последние 16 бит

unsigned last8bitsvalue  = value & ((1u <<  8) - 1); // & 0xff
unsigned last16bitsvalue = value & ((1u << 16) - 1); // & 0xffff

Если смещение всегда равно нулю, как во всех ваших примерах в вопросе, тогда вам не нужно более общее getbits(). Существует специальная инструкция процессора BLSMSK, которая помогает вычислить маску ((1 << n) - 1).

4 голосов
/ 04 ноября 2011

Побитовое И ваше целое число с маской, в которой установлены именно те биты, которые вы хотите извлечь.Затем сдвиньте результат вправо, чтобы переместить извлеченные биты, если это необходимо.

unsigned int lowest_17_bits = myuint32 & 0x1FFFF;
unsigned int highest_17_bits = (myuint32 & (0x1FFFF << (32 - 17))) >> (32 - 17);

Редактировать: последний перемещает старшие 17 битов как младшие 17;это может быть полезно, если вам нужно извлечь целое число изнутри большего.Вы можете опустить сдвиг вправо (>>), если это нежелательно.

2 голосов
/ 08 июня 2015

Это более короткий вариант принятого ответа: нижеприведенная функция извлекает биты от-до включительно, создавая битовую маску. После применения логики AND над исходным числом результат сдвигается, поэтому функция возвращает только извлеченные биты. Пропущены проверки индекса / целостности для ясности.

uint16_t extractInt(uint16_t orig16BitWord, unsigned from, unsigned to) 
{
  unsigned mask = ( (1<<(to-from+1))-1) << from;
  return (orig16BitWord & mask) >> from;
}
0 голосов
/ 16 апреля 2015
#define GENERAL__GET_BITS_FROM_U8(source,lsb,msb) \
    ((uint8_t)((source) & \
        ((uint8_t)(((uint8_t)(0xFF >> ((uint8_t)(7-((uint8_t)(msb) & 7))))) & \
             ((uint8_t)(0xFF << ((uint8_t)(lsb) & 7)))))))

#define GENERAL__GET_BITS_FROM_U16(source,lsb,msb) \
    ((uint16_t)((source) & \
        ((uint16_t)(((uint16_t)(0xFFFF >> ((uint8_t)(15-((uint8_t)(msb) & 15))))) & \
            ((uint16_t)(0xFFFF << ((uint8_t)(lsb) & 15)))))))

#define GENERAL__GET_BITS_FROM_U32(source,lsb,msb) \
    ((uint32_t)((source) & \
        ((uint32_t)(((uint32_t)(0xFFFFFFFF >> ((uint8_t)(31-((uint8_t)(msb) & 31))))) & \
            ((uint32_t)(0xFFFFFFFF << ((uint8_t)(lsb) & 31)))))))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...