Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Как вы устанавливаете, очищаете и переключаете один бит?

Автор вопроса: JeffVОтветов: 12

Как настроить, очистить и немного переключить?

  • прочтите это: graphics.stanford.edu/~seander/bithacks.html и, когда вы освоите это, прочтите это: realtimecollisiondetection.net/blog/?p=78 ugasoft 18.09.2008
  • Вам также может быть интересно попробовать The Bit Twiddler, Bit Twiddling Hacks и Агрегированные магические алгоритмы. 05.01.2009

cc++bit-manipulationbitwise-operators

Ответы

Ответ 1 · рейтинг 3923 · Paige Ruten

Установка немного

Используйте побитовый оператор ИЛИ (|), чтобы установить бит.

number |= 1UL << n;

Это установит n-й бит number. n должен быть равен нулю, если вы хотите установить 1st бит и так далее до n-1, если вы хотите установить nth бит.

Используйте 1ULL, если number шире, чем unsigned long; продвижение 1UL << n не происходит до тех пор, пока не будет оценено 1UL << n, где неопределенное поведение сдвигается более чем на ширину long. То же самое относится ко всем остальным примерам.

Немного очищая

Используйте побитовый оператор И (&), чтобы немного очистить.

number &= ~(1UL << n);

Это очистит n-й бит number. Вы должны инвертировать битовую строку с помощью побитового оператора НЕ (~), а затем И.

Немного переключаюсь

Оператор XOR (^) может использоваться для небольшого переключения.

number ^= 1UL << n;

Это переключит n-й бит number.

Немного проверяю

Вы не просили об этом, но я могу это добавить.

Чтобы немного проверить, сдвиньте число n вправо, затем побитовое И:

bit = (number >> n) & 1U;

Это поместит значение n-го бита number в переменную bit.

Изменение n -го бита на x

Установка n-го бита равным 1 или 0 может быть достигнута с помощью следующего в реализации C ++ с дополнением до 2:

number ^= (-x ^ number) & (1UL << n);

Бит n будет установлен, если x равен 1, и сброшен, если x равен 0. Если x имеет другое значение, вы получите мусор. x = !!x преобразует его в 0 или 1.

Чтобы сделать это независимым от поведения отрицания дополнения до 2 (где -1 имеет все биты, в отличие от реализации дополнения до 1 или знака / величины C ++), используйте отрицание без знака.

number ^= (-(unsigned long)x ^ number) & (1UL << n);

or

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

Как правило, неплохо использовать беззнаковые типы для переносимых манипуляций с битами.

or

number = (number & ~(1UL << n)) | (x << n);

(number & ~(1UL << n)) очистит n-й бит, а (x << n) установит n-й бит в x.

Также, как правило, неплохо не копировать / вставлять код в целом, и поэтому многие люди используют макросы препроцессора (например, ответ сообщества вики ниже) или инкапсуляция.

Ответ 2 · рейтинг 494 · Martin York

Использование стандартной библиотеки C ++: std::bitset<N>.

Или версия Boost: _ 2_.

Свое катать не нужно:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}

[Alpha:] > ./a.out
00010

Версия Boost позволяет использовать битовый набор размера во время выполнения по сравнению с стандартной библиотекой размером во время компиляции. битсет.

Ответ 3 · рейтинг 263 · Ferruccio

Другой вариант - использовать битовые поля:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

определяет 3-битное поле (фактически, это три 1-битных поля). Битовые операции теперь стали немного (хаха) проще:

Чтобы установить или сбросить бит:

mybits.b = 1;
mybits.c = 0;

Чтобы немного переключиться:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

Немного проверяем:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

Это работает только с битовыми полями фиксированного размера. В противном случае вам придется прибегнуть к технике бит-твиддлинга, описанной в предыдущих сообщениях.

Ответ 4 · рейтинг 207 · Community

Я использую макросы, определенные в файле заголовка, для обработки и сброса битов:

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (!(~(x) & (y)))
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

Ответ 5 · рейтинг 128 · dmckee --- ex-moderator kitten

Иногда для имени битов стоит использовать enum:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

Затем используйте имена позже. Т.е. записывать

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

установить, очистить и протестировать. Таким образом вы скрываете магические числа от остальной части вашего кода.

В остальном я поддерживаю решение Джереми.

Ответ 6 · рейтинг 52 · yogeesh

Из bitops.h snip-c.zip

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

Хорошо, давайте разберемся ...

Распространенным выражением, с которым у вас, кажется, возникают проблемы во всех этих случаях, является "(1L ‹---------------- (posn))". Все, что это делает, - это создание маски с одним битом, которая будет работать с любым целочисленным типом. Аргумент posn указывает позицию, в которой вы хотите установить бит. Если posn == 0, тогда это выражение будет оцениваться как:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Если posn == 8, он будет оценивать как:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

Другими словами, он просто создает поле из 0 с 1 в указанной позиции. Единственная сложная часть - в макросе BitClr (), где нам нужно установить единственный бит 0 в поле единиц. Это достигается с помощью дополнения к 1 того же выражения, которое обозначено оператором тильда (~).

Как только маска создана, она применяется к аргументу так, как вы предлагаете, с помощью побитовых операторов и (&), или (|) и xor (^). Поскольку маска имеет тип long, макросы будут одинаково хорошо работать с типами char, short, int или long.

Суть в том, что это общее решение целого класса проблем. Конечно, возможно и даже уместно переписывать эквивалент любого из этих макросов с явными значениями маски каждый раз, когда они вам нужны, но зачем это делать? Помните, что подстановка макросов происходит в препроцессоре, и поэтому сгенерированный код будет отражать тот факт, что значения считаются постоянными компилятором, то есть использовать обобщенные макросы столь же эффективно, как и "изобретать колесо" каждый раз, когда вам нужно делать битовые манипуляции.

Не уверены? Вот небольшой тестовый код - я использовал Watcom C с полной оптимизацией и без использования _cdecl, чтобы полученный дизассемблер был максимально чистым:

---- [TEST.C] ----------------------------------------- -----------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

---- [TEST.OUT (в разобранном виде)] -------------------------------------- ---------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret

No disassembly errors

---- [финиш] ------------------------------------------- ----------------------

Ответ 7 · рейтинг 36 · kapilddit

Для новичка я хотел бы немного подробнее объяснить на примере:

Пример:

value is 0x55;
bitnum : 3rd.

Используется оператор & для проверки бита:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

Переключить или перевернуть:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| оператор: установить бит

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

Ответ 8 · рейтинг 27 · John U

Поскольку это помечено как "встроенный", я предполагаю, что вы используете микроконтроллер. Все приведенные выше предложения действительны и работают (чтение-изменение-запись, объединения, структуры и т. Д.).

Однако во время отладки на основе осциллографа я был поражен, обнаружив, что эти методы имеют значительные накладные расходы в циклах ЦП по сравнению с записью значения непосредственно в регистры PORTnSET / PORTnCLEAR микроконтроллера, что дает реальную разницу там, где есть узкие петли / высокие значения. переключатели частоты ISR.

Для тех, кто не знаком: в моем примере микроконтроллер имеет общий регистр состояния контактов PORTn, который отражает выходные контакты, поэтому выполнение PORTn | = BIT_TO_SET приводит к чтению-изменению-записи в этот регистр. Однако регистры PORTnSET / PORTnCLEAR принимают "1", чтобы означать "пожалуйста, сделайте этот бит 1" (SET) или "пожалуйста, сделайте этот бит нулевым" (CLEAR) и "0", чтобы означать "оставьте вывод в покое". Таким образом, вы получаете два адреса порта в зависимости от того, устанавливаете ли вы бит или очищаете его (не всегда удобно), но намного быстрее реагируют и меньше собираемый код.

Ответ 9 · рейтинг 27 · R.. GitHub STOP HELPING ICE

Вот мой любимый макрос битовой арифметики, который работает с любыми типами целочисленных массивов без знака от unsigned char до size_t (это самый большой тип, с которым должно быть эффективно работать):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

Чтобы установить бит:

BITOP(array, bit, |=);

Чтобы немного прояснить:

BITOP(array, bit, &=~);

Чтобы немного переключиться:

BITOP(array, bit, ^=);

Чтобы немного протестировать:

if (BITOP(array, bit, &)) ...

и т.п.

Ответ 10 · рейтинг 25 · Roddy

У подхода битового поля есть и другие преимущества во встраиваемой сфере. Вы можете определить структуру, которая отображается непосредственно на биты в конкретном аппаратном регистре.

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

Вам нужно знать порядок упаковки битов - я думаю, что сначала это MSB, но это может зависеть от реализации. Также проверьте, как ваш компилятор обрабатывает поля, пересекающие границы байтов.

Затем вы можете читать, писать, тестировать отдельные значения, как и раньше.

Ответ 11 · рейтинг 20 · John Zwinck

Проверить бит в произвольном месте в переменной произвольного типа

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

Пример использования:

int main(void)
{
    unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };

    for (int ix = 0; ix < 64; ++ix)
        printf("bit %d is %d\n", ix, bit_test(arr, ix));

    return 0;
}

Примечания. Он разработан, чтобы быть быстрым (учитывая его гибкость) и не ветвиться. Результатом является эффективный машинный код SPARC при компиляции Sun Studio 8; Я также тестировал его с помощью MSVC ++ 2008 на amd64. Можно сделать аналогичные макросы для установки и сброса битов. Ключевое отличие этого решения от многих других здесь заключается в том, что оно работает для любого местоположения практически с любым типом переменных.

Ответ 12 · рейтинг 20 · bill

В более общем случае для растровых изображений произвольного размера:

#define BITS 8
#define BIT_SET(  p, n) (p[(n)/BITS] |=  (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] &   (0x80>>((n)%BITS)))

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы