c++ - examples - xor c




¿Cómo configura, borra y alterna un solo bit? (18)

¿Cómo configura, borra y alterna un solo bit?

Para abordar un error de codificación común al intentar formar la máscara:
1no siempre es lo suficientemente ancho

¿Qué problemas pasan cuando numberes un tipo más amplio que 1?
xpuede ser demasiado grande para el cambio que 1 << xconduce a un comportamiento indefinido (UB). Incluso si xno es demasiado grande, ~puede que no se volteen suficientes bits más 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 asegurar 1 es lo suficientemente ancho:

El código podría usar 1ullo pedanticalmente (uintmax_t)1y permitir que el compilador optimice.

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

O cast: lo que hace que los problemas de codificación / revisión / mantenimiento se mantengan correctos y actualizados.

number |= (type_of_number)1 << x;

O bien, promueva suavemente 1forzando una operación matemática que sea al menos tan amplia como el tipo de number.

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

Al igual que con la mayoría de las manipulaciones de bits, mejor trabajar con sin signo tipos en lugar de firmados los

¿Cómo configura, borra y alterna un poco en C / C ++?


Poniendo un poco

Use el operador OR a nivel de bits ( | ) para establecer un bit.

number |= 1UL << n;

Eso establecerá el n º bit de number . n debe ser cero, si desea establecer el primer bit y así sucesivamente hasta n-1 , si desea establecer el n º bit.

Use 1ULL si el number es más ancho que el unsigned long ; la promoción de 1UL << n no ocurre hasta después de evaluar 1UL << n donde el comportamiento indefinido cambia más que el ancho de un long . Lo mismo se aplica a todos los demás ejemplos.

Despejando un poco

Use el operador AND a nivel de bits ( & ) para borrar un poco.

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

Eso borrará el número n de number . Debe invertir la cadena de bits con el operador NO a nivel de bits ( ~ ), y luego Y.

Toggling un poco

El operador XOR ( ^ ) se puede utilizar para alternar un bit.

number ^= 1UL << n;

Eso cambiará el número n de number .

Revisando un poco

No pediste esto, pero también podría agregarlo.

Para verificar un bit, desplace el número n a la derecha, luego a bit y Y:

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

Eso pondrá el valor del n º bit de number en el bit variable.

Cambiando el bit n a x

La configuración del bit n en 1 o 0 se puede lograr con lo siguiente en una implementación de C ++ de complemento a 2:

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

El bit n se establecerá si x es 1 , y se borrará si x es 0 . Si x tiene algún otro valor, obtienes basura. x = !!x booleanize a 0 o 1.

Para hacer que este comportamiento de negación del complemento de 2 sea independiente (donde -1 tiene todos los bits establecidos, a diferencia del complemento de 1 o la implementación de C ++ de signo / magnitud), use la negación sin signo.

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

o

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

En general, es una buena idea usar tipos sin firma para la manipulación de bits portátil.

En general, también es una buena idea no copiar / pegar el código en general, por lo que muchas personas usan macros de preprocesador (como la respuesta de la wiki de la comunidad más abajo ) o algún tipo de encapsulación.


De los snip-c.zip de 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)))

OK, analicemos las cosas ...

La expresión común con la que pareces tener problemas en todo esto es "(1L << (posn))". Todo esto es crear una máscara con un solo bit y que funcionará con cualquier tipo de entero. El argumento "posn" especifica la posición en la que desea el bit. Si posn == 0, entonces esta expresión se evaluará como:

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

Si posn == 8, evaluará a

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

En otras palabras, simplemente crea un campo de 0 con un 1 en la posición especificada. La única parte difícil está en la macro BitClr () donde necesitamos configurar un solo bit 0 en un campo de 1. Esto se logra utilizando el complemento de 1 de la misma expresión que indica el operador de tilde (~).

Una vez que se crea la máscara, se aplica al argumento tal como sugiere, mediante el uso de los operadores a nivel de bits y (&), o (|) y xor (^). Como la máscara es del tipo long, las macros funcionarán igual de bien en las de char, short, int o long.

La conclusión es que esta es una solución general para toda una clase de problemas. Por supuesto, es posible e incluso apropiado reescribir el equivalente de cualquiera de estas macros con valores de máscara explícitos cada vez que lo necesite, pero ¿por qué hacerlo? Recuerde, la sustitución de macros se produce en el preprocesador y, por lo tanto, el código generado reflejará el hecho de que el compilador considera que los valores son constantes, es decir, es tan eficiente usar las macros generalizadas como para "reinventar la rueda" cada vez que necesite hacer la manipulación de bits.

¿No está convencido? Aquí hay un código de prueba: usé Watcom C con optimización completa y sin usar _cdecl, por lo que el desmontaje resultante sería lo más limpio posible:

---- [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 (desmontado)] -------------------------------------- ---------

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

---- [finis] ------------------------------------------- ----------------------


A veces vale la pena usar una enum para nombrar los bits:

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

Luego usa los nombres más adelante. Es decir, escribir

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

para configurar, borrar y probar. De esta manera ocultas los números mágicos del resto de tu código.

Aparte de eso apoyo la solución de Jeremy.


Aquí está mi macro aritmética de bits favorita, que funciona para cualquier tipo de matriz de enteros sin signo desde caracteres unsigned char hasta size_t (que es el tipo más grande con el que debería ser eficiente trabajar):

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

Para establecer un bit:

BITOP(array, bit, |=);

Para borrar un poco:

BITOP(array, bit, &=~);

Para alternar un poco:

BITOP(array, bit, ^=);

Para probar un poco:

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

etc.


Como esto está etiquetado como "incrustado", asumiré que estás usando un microcontrolador. Todas las sugerencias anteriores son válidas y de trabajo (lectura-modificación-escritura, uniones, estructuras, etc.).

Sin embargo, durante un combate de depuración basada en el osciloscopio, me sorprendió descubrir que estos métodos tienen una sobrecarga considerable en los ciclos de la CPU en comparación con la escritura de un valor directamente en los registros PORTnSET / PORTnCLEAR de los micrófonos, lo que marca una diferencia real cuando hay bucles ajustados / altos -la frecuencia de los pines de conmutación de ISR.

Para aquellos que no están familiarizados: en mi ejemplo, el micro tiene un registro general de estado de PIN que refleja los pines de salida, por lo que hacer PORTn | = BIT_TO_SET da como resultado una lectura-modificación-escritura en ese registro. Sin embargo, los registros PORTnSET / PORTnCLEAR toman un '1' para significar "haga este bit 1" (SET) o "haga este bit cero" (CLEAR) y un '0' significa "dejar el pin solo". por lo tanto, termina con dos direcciones de puerto dependiendo de si está configurando o borrando el bit (no siempre es conveniente) pero una reacción mucho más rápida y un código ensamblado más pequeño.


Este programa es para cambiar cualquier bit de datos de 0 a 1 o 1 a 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);
        }
    }
}

La otra opción es usar campos de bits:

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

struct bits mybits;

define un campo de 3 bits (en realidad, son tres campos de 1 bit). Las operaciones de bits ahora se vuelven un poco más simples (jaja):

Para establecer o borrar un poco:

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

Para alternar un poco:

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

Comprobando un poco:

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

Esto solo funciona con campos de bits de tamaño fijo. De lo contrario, tendrá que recurrir a las técnicas de twiddling de bits descritas en publicaciones anteriores.


Para el principiante me gustaría explicar un poco más con un ejemplo:

Ejemplo:

value is 0x55;
bitnum : 3rd.

El operador & se utiliza comprobar el 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 o voltear:

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

| operador: establecer el bit

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

Si estás haciendo un poco de twiddling es posible que desees usar máscaras que harán que todo sea más rápido. Las siguientes funciones son muy rápidas y todavía son flexibles (permiten la combinación de bits en mapas de bits de cualquier tamaño).

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

Tenga en cuenta que para establecer el bit 'n' en un entero de 16 bits, haga lo siguiente:

TSetBit( n, &my_int);

Depende de usted asegurarse de que el número de bits esté dentro del rango del mapa de bits que pasa. Tenga en cuenta que para los procesadores little endian, los bytes, palabras, palabras clave, qwords, etc., se asignan correctamente entre sí en la memoria (razón principal por la que los procesadores endian son "mejores" que los procesadores big-endian, ah, siento que se avecina una guerra de llamas en...).


Uso macros definidas en un archivo de encabezado para manejar el conjunto de bits y borrar:

/* 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))

Utilice los operadores bitwise: & |

Para establecer el último bit en 000b :

foo = foo | 001b

Para comprobar el último bit en foo :

if ( foo & 001b ) ....

Para borrar el último bit en foo :

foo = foo & 110b

Utilicé XXXb para mayor claridad. Probablemente trabajará con la representación HEX, dependiendo de la estructura de datos en la que esté empaquetando bits.


Aquí hay algunas macros que utilizo:

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)

Pruebe una de estas funciones en el lenguaje C para cambiar n bits:

char bitfield;

// Start at 0th position

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

O

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

O

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

Variable utilizada

int value, pos;

valor -
Pos. datos : posición del bit que nos interesa configurar, borrar o alternar
Establecer un bit

value = value | 1 << pos;

Claro un poco

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

Cambiar un poco

value = value ^ 1 << pos;

Visual C 2010, y quizás muchos otros compiladores, tienen soporte directo para operaciones de bits integradas. Sorprendentemente, esto funciona, incluso el operador sizeof () funciona correctamente.

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

Entonces, para su pregunta, IsGph [i] = 1, o IsGph [i] = 0 hace que la configuración y la eliminación de bools sean fáciles.

Para encontrar personajes no imprimibles ...

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

Tenga en cuenta que no hay nada "especial" sobre este código. Se trata un poco como un entero, lo que técnicamente es. Un entero de 1 bit que puede contener 2 valores y 2 valores solamente.

Una vez utilicé este método para encontrar registros de préstamos duplicados, donde número de préstamo era la clave ISAM, utilizando el número de préstamo de 6 dígitos como un índice en la matriz de bits. Salvajemente rápido, y después de 8 meses, demostró que el sistema de mainframe del que obteníamos los datos funcionaba mal. La simplicidad de las matrices de bits hace que la confianza en su corrección sea muy alta; por ejemplo, un enfoque de búsqueda.


Utilice uno de los operadores como se define here .

Para establecer un bit, se utiliza int x = x | 0x?;donde ?está la posición del bit en forma binaria.


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