c++ - origem - Como você define, desmarca e alterna um único bit?



bit-manipulation bitwise-operators (18)

Como você definir, limpar e alternar um pouco em C / C ++?


Answer #1

Como você define, desmarca e alterna um único bit?

Para resolver uma armadilha comum de codificação ao tentar formar a máscara:
1nem sempre é grande o suficiente

Que problemas acontecem quando numberé um tipo mais amplo do que 1?
xpode ser muito grande para a mudança que 1 << xleva ao comportamento indefinido (UB). Mesmo se xnão for muito grande, ~pode não virar os bits mais significativos.

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

Para garantir 1 é grande o suficiente:

Código pode usar 1ullou pedantically (uintmax_t)1e deixe o compilador otimizar.

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

Ou cast - o que gera problemas de codificação / revisão / manutenção, mantendo o elenco correto e atualizado.

number |= (type_of_number)1 << x;

Ou promova gentilmente ao 1forçar uma operação matemática que seja pelo menos tão ampla quanto o tipo de number.

number |= (number*0 + 1) << x;

Como a maioria das manipulações de bits, melhor trabalhar com não assinados tipos em vez de assinados os


Answer #2

Definindo um pouco

Use o operador OR bit a bit ( | ) para definir um bit.

number |= 1UL << n;

Isso irá definir o número n . n deve ser zero, se você quiser definir o 1º bit e assim por diante até n-1 , se quiser definir o n ésimo bit.

Use 1ULL se o number for mais largo que unsigned long ; a promoção de 1UL << n não acontece até depois de avaliar 1UL << n onde é indefinido o comportamento de deslocar por mais que a largura de um long . O mesmo se aplica a todos os outros exemplos.

Limpando um pouco

Use o operador AND bit a bit ( & ) para limpar um pouco.

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

Isso irá limpar o número n . Você deve inverter a cadeia de bits com o operador NOT bit a bit ( ~ ) e, em seguida, AND it.

Alternando um pouco

O operador XOR ( ^ ) pode ser usado para alternar um pouco.

number ^= 1UL << n;

Isso irá alternar o number de bits.

Verificando um pouco

Você não pediu por isso, mas eu poderia muito bem adicioná-lo.

Para verificar um pouco, mude o número n para a direita, então bit a bit E:

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

Isso colocará o valor do nésimo bit de number no bit variável.

Mudando o nésimo bit para x

A definição do n bit para 1 ou 0 pode ser obtida com o seguinte na implementação de complemento C ++ em 2:

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

Bit n será definido se x for 1 e limpo se x for 0 . Se x tem algum outro valor, você recebe lixo. x = !!x irá booleanizar para 0 ou 1.

Para tornar isso independente do comportamento de negação do complemento de 2 (onde -1 tem todos os bits definidos, diferentemente de um complemento de 1 ou implementação de C ++ de sinal / magnitude), use a negação não assinada.

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

ou

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

Geralmente é uma boa idéia usar tipos não assinados para manipulação de bit portável.

Também é geralmente uma boa idéia não copiar / colar código em geral e muitas pessoas usam macros de pré-processador (como a resposta do wiki da comunidade mais abaixo ) ou algum tipo de encapsulamento.


Answer #3

Verifique um bit em um local arbitrário em uma variável de tipo arbitrário:

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

Uso da amostra:

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

Notas: Isto é projetado para ser rápido (dada sua flexibilidade) e não-branchy. Isso resulta em código de máquina SPARC eficiente quando compilado no Sun Studio 8; Eu também testei-o usando o MSVC ++ 2008 em amd64. É possível fazer macros semelhantes para definir e limpar bits. A principal diferença desta solução em comparação com muitas outras aqui é que ela funciona para qualquer local em praticamente qualquer tipo de variável.


Answer #4

Às vezes, vale a pena usar um enum para nomear os bits:

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

Em seguida, use os nomes mais tarde. Ou seja, escrever

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

para definir, limpar e testar. Desta forma você esconde os números mágicos do resto do seu código.

Além disso, eu apoio a solução de Jeremy.


Answer #5

A outra opção é usar campos de bits:

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

struct bits mybits;

define um campo de 3 bits (na verdade, são três campos de 1 bit). As operações de bits agora se tornam um pouco (haha) mais simples:

Para definir ou limpar um pouco:

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

Para alternar um pouco:

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

Verificando um pouco:

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

Isso só funciona com campos de bit de tamanho fixo. Caso contrário, você terá que recorrer às técnicas descritas nas postagens anteriores.


Answer #6

Aqui está a minha macro aritmética de bits preferida, que funciona para qualquer tipo de matriz de inteiro não unsigned char de unsigned char até size_t (que é o maior tipo que deve ser eficiente para se trabalhar):

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

Para definir um pouco:

BITOP(array, bit, |=);

Para limpar um pouco:

BITOP(array, bit, &=~);

Para alternar um pouco:

BITOP(array, bit, ^=);

Para testar um pouco:

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

etc.


Answer #7

Este programa é para alterar qualquer bit de dados de 0 para 1 ou 1 para 0:

{
    unsigned int data = 0x000000F0;
    int bitpos = 4;
    int bitvalue = 1;
    unsigned int bit = data;
    bit = (bit>>bitpos)&0x00000001;
    int invbitvalue = 0x00000001&(~bitvalue);
    printf("%x\n",bit);

    if (bitvalue == 0)
    {
        if (bit == 0)
            printf("%x\n", data);
        else
        {
             data = (data^(invbitvalue<<bitpos));
             printf("%x\n", data);
        }
    }
    else
    {
        if (bit == 1)
            printf("elseif %x\n", data);
        else
        {
            data = (data|(bitvalue<<bitpos));
            printf("else %x\n", data);
        }
    }
}

Answer #8

Eu uso macros definidas em um arquivo de cabeçalho para lidar com o conjunto de bits e claro:

/* 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)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

Answer #9

Para o iniciante, gostaria de explicar um pouco mais com um exemplo:

Exemplo:

value is 0x55;
bitnum : 3rd.

O operador & é usado para verificar o bit:

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)

Alternar ou Inverter:

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

| operador: definir o bit

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

Answer #10

Se você está fazendo um monte de coisas, você pode querer usar máscaras que tornem tudo mais rápido. As funções a seguir são muito rápidas e ainda são flexíveis (elas permitem o bit twiddling em mapas de bits de qualquer tamanho).

const unsigned char TQuickByteMask[8] =
{
   0x01, 0x02, 0x04, 0x08,
   0x10, 0x20, 0x40, 0x80,
};


/** Set bit in any sized bit mask.
 *
 * @return    none
 *
 * @param     bit    - Bit number.
 * @param     bitmap - Pointer to bitmap.
 */
void TSetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] |= TQuickByteMask[n];        // Set bit.
}


/** Reset bit in any sized mask.
 *
 * @return  None
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TResetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] &= (~TQuickByteMask[n]);    // Reset bit.
}


/** Toggle bit in any sized bit mask.
 *
 * @return   none
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TToggleBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] ^= TQuickByteMask[n];        // Toggle bit.
}


/** Checks specified bit.
 *
 * @return  1 if bit set else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitSet( short bit, const unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;    // Index to byte.
    n = bit % 8;    // Specific bit in byte.

    // Test bit (logigal AND).
    if (bitmap[x] & TQuickByteMask[n])
        return 1;

    return 0;
}


/** Checks specified bit.
 *
 * @return  1 if bit reset else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitReset( short bit, const unsigned char *bitmap)
{
    return TIsBitSet(bit, bitmap) ^ 1;
}


/** Count number of bits set in a bitmap.
 *
 * @return   Number of bits set.
 *
 * @param    bitmap - Pointer to bitmap.
 * @param    size   - Bitmap size (in bits).
 *
 * @note    Not very efficient in terms of execution speed. If you are doing
 *        some computationally intense stuff you may need a more complex
 *        implementation which would be faster (especially for big bitmaps).
 *        See (http://graphics.stanford.edu/~seander/bithacks.html).
 */
int TCountBits( const unsigned char *bitmap, int size)
{
    int i, count = 0;

    for (i=0; i<size; i++)
        if (TIsBitSet(i, bitmap))
            count++;

    return count;
}

Note que, para definir o bit 'n' em um inteiro de 16 bits, você faz o seguinte:

TSetBit( n, &my_int);

Cabe a você garantir que o número do bit esteja dentro do intervalo do mapa de bits que você passa. Note que para processadores little endian bytes, palavras, dwords, qwords, etc., mapeiam corretamente uns aos outros na memória (principal razão que processadores little endian são 'melhores' que processadores big-endian, ah, eu sinto uma guerra flamejante vindo em...).


Answer #11

Usando a Biblioteca C ++ Padrão: std::bitset<N> .

Ou a versão do Boost : boost::dynamic_bitset .

Não há necessidade de fazer o seu próprio:

#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

A versão Boost permite que um conjunto de bits em tempo de execução seja comparado com um conjunto de bits padrão em tempo de compilação da biblioteca .


Answer #12

Use os operadores bit a bit: & |

Para definir o último bit em 000b :

foo = foo | 001b

Para verificar o último bit no foo :

if ( foo & 001b ) ....

Para limpar o último bit no foo :

foo = foo & 110b

Eu usei o XXXb para maior clareza. Você provavelmente estará trabalhando com representação HEX, dependendo da estrutura de dados em que você está compactando bits.


Answer #13

O Visual C 2010, e talvez muitos outros compiladores, têm suporte direto para operações de bits embutidas. Surpreendentemente, isso funciona, até mesmo o operador sizeof () funciona corretamente.

bool    IsGph[256], IsNotGph[256];

//  Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++)  {
    IsGph[i] = isgraph((unsigned char)i);
}

Então, para sua pergunta, IsGph [i] = 1 ou IsGph [i] = 0 facilitam a configuração e limpeza de bools.

Para encontrar caracteres não imprimíveis ...

//  Initialize boolean array to detect UN-printable characters, 
//  then call function to toggle required bits true, while initializing a 2nd
//  boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++)  {
    if(IsGph[i])    {
         IsNotGph[i] = 0;
    }   else   {
         IsNotGph[i] = 1;
    }
}

Note que não há nada "especial" sobre este código. Trata-se um pouco como um inteiro - o que tecnicamente é. Um inteiro de 1 bit que pode conter 2 valores e 2 valores apenas.

Certa vez, usei essa abordagem para localizar registros de empréstimos duplicados, em que loan_number era a chave ISAM, usando o número de empréstimo de seis dígitos como um índice na matriz de bits. Rapidamente, e depois de 8 meses, provou que o sistema de mainframe de onde estávamos obtendo os dados estava, de fato, funcionando mal. A simplicidade dos arrays de bits torna a confiança na sua exatidão muito alta - por exemplo, uma abordagem de busca.


Answer #14

Tente uma dessas funções na linguagem C para alterar n bits:

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

Ou

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

Ou

void chang_n_bit(int n, int value)
{
    if(value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}

Answer #15

Variável usada

int value, pos;

valor - Dados
pos - posição do bit que estamos interessados ​​em definir, limpar ou alternar
Definir um bit

value = value | 1 << pos;

Limpar um pouco

value = value & ~(1 << pos); 

Alternar um pouco

value = value ^ 1 << pos;

Answer #16

Expandindo a bitsetresposta:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}

Answer #17

Use um dos operadores conforme definido here .

Para definir um bit, use int x = x | 0x?;onde ?é a posição do bit na forma binária.


Answer #18
int set_nth_bit(int num, int n){

    return (num | 1 << n);
}

int clear_nth_bit(int num, int n){

    return (num & ~( 1 << n));
}

int toggle_nth_bit(int num, int n){

    return num ^ (1 << n);
}

int check_nth_bit(int num, int n){

    return num & (1 << n);
}




bitwise-operators