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

¿Cómo se configura, borra y alterna un poco?

preguntado el 06 de septiembre de 08 a las 22:09

26 Respuestas

Estableciendo un poco

Utilice el operador OR bit a bit (|) para establecer un poco.

number |= 1UL << n;

Eso establecerá el nel poco de number. n debe ser cero, si desea establecer el 1st poco y así sucesivamente hasta n-1, si desea configurar el nth bit.

Utiliza 1ULL if number es más ancho que unsigned long; Promoción de 1UL << n no sucede hasta después de evaluar 1UL << n donde es un comportamiento indefinido cambiar más que el ancho de un long. Lo mismo se aplica al resto de los ejemplos.

Aclarando un poco

Utilice el operador AND bit a bit (&) para aclarar un poco.

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

Eso despejará el nel poco de number. Debe invertir la cadena de bits con el operador NOT bit a bit (~), luego Y eso.

Alternando un poco

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

number ^= 1UL << n;

Eso alternará el nel poco de number.

Comprobando un poco

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

Para verificar un poco, mueva el número n hacia la derecha, luego bit a bit Y:

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

Eso pondrá el valor de la nel poco de number en la variable bit.

Cambiando el nth bit a x

Configurando el nth bit a cualquiera 1 or 0 se puede lograr con lo siguiente en una implementación de C ++ complementaria a 2:

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

Poco n se establecerá si x is 1y aclarado si x is 0. Si x tiene algún otro valor, obtienes basura. x = !!x lo booleanizará a 0 o 1.

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

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

or

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

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

or

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

(number & ~(1UL << n)) limpiará el nth bit y (x << n) establecerá el nth bit a x.

También es generalmente una buena idea no copiar / pegar código en general y muchas personas usan macros de preprocesador (como la respuesta wiki de la comunidad más abajo) o algún tipo de encapsulación.

Respondido el 20 de junio de 20 a las 10:06

Me gustaría señalar que en las plataformas que tienen soporte nativo para bit set / clear (por ejemplo, microcontroladores AVR), los compiladores a menudo traducirán 'myByte | = (1 << x)' en las instrucciones nativas de bit set / clear siempre que x sea una constante, por ejemplo: (1 << 5), o const sin signo x = 5. - Aaron

bit = número & (1 << x); no pondrá el valor del bit x en bit a menos que bit tenga el tipo _Bool ( ). De lo contrario, bit = !! (number & (1 << x)); voluntad.. - Chris Young

¿Por qué no cambias el último a bit = (number >> x) & 1 - Aaronman

1 es un int literal, que está firmado. Entonces, todas las operaciones aquí operan con números firmados, lo que no está bien definido por los estándares. Los estándares no garantizan el complemento a dos o el cambio aritmético, por lo que es mejor usar 1U. - Siyuán Ren

yo prefiero number = number & ~(1 << n) | (x << n); para cambiar el n-ésimo bit ax. - jiasli

Usando la biblioteca estándar de C ++: std::bitset<N>.

O la Boost Versión: boost::dynamic_bitset.

No es necesario enrollar el tuyo:

#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

La versión Boost permite un conjunto de bits de tamaño de ejecución en comparación con un biblioteca estándar conjunto de bits de tamaño en tiempo de compilación.

Respondido 16 Feb 18, 01:02

+1. No es que std :: bitset se pueda usar desde "C", pero como el autor etiquetó su pregunta con "C ++", AFAIK, su respuesta es la mejor por aquí ... std :: vector es de otra manera, si uno conoce sus pros y sus contras - parcebal

@andrewdotnich: vector es (desafortunadamente) una especialización que almacena los valores como bits. Ver gotw.ca/publications/mill09.htm para más información... - Niklas

Quizás nadie lo mencionó porque estaba etiquetado como incrustado. En la mayoría de los sistemas integrados, evita STL como una plaga. Y el soporte de refuerzo es probablemente un pájaro muy raro de detectar entre la mayoría de los compiladores integrados. - Lundín

@Martin Es muy cierto. Además de los asesinos de rendimiento específicos como STL y plantillas, muchos sistemas embebidos incluso evitan por completo las bibliotecas estándar, porque son muy difíciles de verificar. La mayor parte de la rama embebida está adoptando estándares como MISRA, que requiere herramientas de análisis de código estático (cualquier profesional de software debería usar estas herramientas por cierto, no solo gente embebida). Generalmente, las personas tienen mejores cosas que hacer que ejecutar análisis estáticos a través de toda la biblioteca estándar, si su código fuente está disponible para ellos en el compilador específico. - Lundín

@Lundin: Tus declaraciones son excesivamente amplias (por lo tanto, es inútil discutir sobre ellas). Estoy seguro de que puedo encontrar situaciones en las que sean verdaderas. Esto no cambia mi punto inicial. Ambas clases están perfectamente bien para su uso en sistemas integrados (y sé con certeza que se utilizan). Su punto inicial acerca de que STL / Boost no se usa en sistemas integrados también es incorrecto. Estoy seguro de que hay sistemas que no los usan e incluso los sistemas que los usan se usan juiciosamente, pero decir que no se usan simplemente no es correcto (porque hay sistemas en los que se usan). - martín york

La otra opción es utilizar 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 (jaja) más simples:

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 manipulación de bits descritas en publicaciones anteriores.

respondido 29 nov., 12:00

Siempre he encontrado que usar campos de bits es una mala idea. No tiene control sobre el orden en que se asignan los bits (desde la parte superior o la inferior), lo que hace que sea imposible serializar el valor de una manera estable / portátil, excepto bit a bit. También es imposible mezclar aritmética de bits de bricolaje con campos de bits, por ejemplo, haciendo una máscara que pruebe varios bits a la vez. Por supuesto, puede usar && y esperar que el compilador lo optimice correctamente ... R .. GitHub DEJA DE AYUDAR A ICE

Los campos de bits son malos en muchos sentidos, casi podría escribir un libro al respecto. De hecho, casi tuve que hacer eso para un pequeño programa de campo que necesitaba el cumplimiento de MISRA-C. MISRA-C obliga a que se documente todo el comportamiento definido por la implementación, por lo que terminé escribiendo un buen ensayo sobre todo lo que puede salir mal en los campos de bits. Orden de bits, endiabilidad, bits de relleno, bytes de relleno, varios otros problemas de alineación, conversiones de tipo implícitas y explícitas hacia y desde un campo de bits, UB si no se usa int, etc. En su lugar, utilice operadores bit a bit para obtener menos errores y código portátil. Los campos de bits son completamente redundantes. - Lundín

Como la mayoría de las funciones del lenguaje, los campos de bits se pueden usar correctamente o se pueden abusar de ellos. Si necesita empaquetar varios valores pequeños en un solo int, los campos de bits pueden ser muy útiles. Por otro lado, si comienza a hacer suposiciones sobre cómo los campos de bits se asignan al int que lo contiene, solo está buscando problemas. - Ferruccio

@endolith: Eso no sería una buena idea. Podría hacerlo funcionar, pero no necesariamente sería portátil a un procesador diferente, o a un compilador diferente o incluso a la próxima versión del mismo compilador. - Ferruccio

@Yasky y Ferruccio obteniendo diferentes respuestas a un tamaño de () para este enfoque deberían ilustrar los problemas con la compatibilidad no solo entre compiladores sino también entre hardware. A veces nos engañamos a nosotros mismos pensando que hemos resuelto estos problemas con lenguajes o tiempos de ejecución definidos, pero realmente se reduce a '¿funcionará en mi máquina?'. Ustedes chicos integrados tienen mi respeto (y simpatías). - Kelly S. Francés

Utilizo 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)))
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

Respondido el 29 de Septiembre de 20 a las 00:09

Uh, me doy cuenta de que esta es una publicación de hace 5 años, pero no hay duplicación de argumentos en ninguna de esas macros, Dan. robert kelly

BITMASK_CHECK(x,y) ((x) & (y)) debe ser ((x) & (y)) == (y) de lo contrario, devuelve un resultado incorrecto en la máscara multibit (ej. 5 vs 3) / * Hola a todos los sepultureros:) * / - Brigadir

1 debiera ser (uintmax_t)1 o similar en caso de que alguien intente utilizar estas macros en un long o tipo más grande - MM

BITMASK_CHECK_ALL(x,y) se puede implementar como !~((~(y))|(x)) - Handy999

@ Handy999 Es un poco más fácil ver por qué funciona después de aplicar la ley de De Morgan y reorganizar para obtener !(~(x) & (y)) - Tavian Barnes

A veces vale la pena usar un enum a nombre los bits:

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

Entonces usa el nombres mas tarde. Es decir, escribir

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

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

Aparte de eso, apoyo la solución de Jeremy.

Respondido 18 Oct 13, 16:10

Alternativamente, podrías hacer un clearbits() función en lugar de &= ~. ¿Por qué estás usando una enumeración para esto? Pensé que eran para crear un montón de variables únicas con un valor arbitrario oculto, pero estás asignando un valor definido a cada una. Entonces, ¿cuál es el beneficio frente a simplemente definirlos como variables? - endolito

@endolith: El uso de enums para conjuntos de constantes relacionadas se remonta a un largo camino en la programación de c. Sospecho que con los compiladores modernos la única ventaja sobre const short o lo que sea que estén explícitamente agrupados. Y cuando los quieres para algo otros que las máscaras de bits, obtienes la numeración automática. En c ++, por supuesto, también forman tipos distintos, lo que le proporciona una pequeña comprobación adicional de errores estáticos. - dmckee --- ex-gatito moderador

Obtendrá constantes de enumeración indefinidas si no define una constante para cada uno de los posibles valores de los bits. Cual es el enum ThingFlags valor por ThingError|ThingFlag1, ¿por ejemplo? - luis colorado

Si usa este método, tenga en cuenta que las constantes de enumeración son siempre de tipo con signo int. Esto puede causar todo tipo de errores sutiles debido a la promoción de enteros implícita o las operaciones bit a bit en tipos firmados. thingstate = ThingFlag1 >> 1 por ejemplo, invocará un comportamiento definido por la implementación. thingstate = (ThingFlag1 >> x) << y puede invocar un comportamiento indefinido. Y así. Para estar seguro, siempre transmita a un tipo sin firmar. - Lundín

@Lundin: a partir de C ++ 11, puede establecer el tipo subyacente de una enumeración, por ejemplo: enum My16Bits: unsigned short { ... }; - Tambor Aiken

Desde recorte-c.zipbitops.h:

/*
**  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 parece tener problemas en todos estos es "(1L << (posn))". Todo esto hace 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, esta expresión se evaluará como:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Si posn == 8, se evaluará a:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

En otras palabras, simplemente crea un campo de ceros con un 0 en la posición especificada. La única parte complicada está en la macro BitClr () donde necesitamos establecer un solo bit 1 en un campo de unos. Esto se logra usando el complemento a 0 de la misma expresión indicada por el operador tilde (~).

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

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 ocurre en el preprocesador, por lo que el código generado reflejará el hecho de que los valores son considerados constantes por el compilador, es decir, es tan eficiente usar las macros generalizadas como "reinventar la rueda" cada vez que necesite hacer manipulación de bits.

¿No estás convencido? Aquí hay un código de prueba: utilicé 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] ------------------------------------------- ----------------------

Respondido el 08 de junio de 19 a las 07:06

Hay dos cosas sobre esto: (2) al examinar sus macros, algunos pueden creer incorrectamente que las macros realmente establecen / borran / voltean bits en el argumento, sin embargo, no hay asignación; (1) su test.c no está completo; Sospecho que si ejecutara más casos, encontraría un problema (ejercicio del lector) - Dan

-1 Esto es una extraña ofuscación. Nunca reinvente el lenguaje C ocultando la sintaxis del lenguaje detrás de macros, es mala práctica. Luego, algunas rarezas: primero, 1L está firmado, lo que significa que todas las operaciones de bits se realizarán en un tipo con signo. Todo lo que se pase a estas macros volverá como firmado. No está bien. En segundo lugar, esto funcionará de manera muy ineficiente en CPU más pequeñas, ya que se aplica durante mucho tiempo cuando las operaciones podrían haber estado en el nivel int. En tercer lugar, las macros de función son la raíz de todos los males: no tienes ningún tipo de seguridad. Además, el comentario anterior sobre ninguna cesión es muy válido. - Lundín

Esto fallará si arg is long long. 1L debe ser del tipo más amplio posible, por lo que (uintmax_t)1 . (Podrías salirte con la tuya 1ull) - MM

¿Optimizó para el tamaño del código? En las CPU convencionales de Intel, obtendrá bloqueos de registro parciales al leer AX o EAX después de que esta función regrese, porque escribe los componentes de 8 bits de EAX. (Está bien en las CPU AMD u otras que no cambian el nombre de los registros parciales por separado del registro completo. Haswell / Skylake no cambian el nombre de AL por separado, pero sí cambian el nombre de AH.). - pedro cordes

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

Ejemplo:

value is 0x55;
bitnum : 3rd.

El sistema & se utiliza el operador 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: establece el bit

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

contestado el 22 de mayo de 15 a las 17:05

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

Sin embargo, durante una serie de depuración basada en osciloscopios, me sorprendió descubrir que estos métodos tienen una sobrecarga considerable en los ciclos de la CPU en comparación con escribir un valor directamente en los registros PORTnSET / PORTnCLEAR del micro, lo que marca una diferencia real donde hay bucles estrechos / altos -pines de conmutación de ISR de frecuencia.

Para aquellos que no están familiarizados: en mi ejemplo, el micro tiene un registro de estado de pines general PORTn 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' que significa "por favor haga este bit 1" (SET) o "por favor haga que este bit sea cero" (CLEAR) y un '0' para significar "deje 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 mucho más reacción más rápida y código ensamblado más pequeño.

Respondido el 14 de junio de 12 a las 16:06

Micro era Coldfire MCF52259, usando C en Codewarrior. Mirar el desensamblador / asm es un ejercicio útil, ya que muestra todos los pasos que debe seguir la CPU para realizar incluso la operación más básica. También detectamos otras instrucciones que acaparan la CPU en bucles de tiempo crítico: restringir una variable haciendo var% = max_val cuesta una pila de ciclos de CPU cada vez, mientras que si (var> max_val) var- = max_val usa solo un par de instrucciones. Una buena guía para algunos trucos más está aquí: codeproject.com/Artículos/6154/… - John u

Aún más importante, los registros auxiliares de E / S asignados en memoria proporcionan un mecanismo para actualizaciones atómicas. La lectura / modificación / escritura puede ir muy mal si se interrumpe la secuencia. - ben voigt

Tenga en cuenta que todos los registros de puertos se definirán como volatile y, por lo tanto, el compilador no puede realizar ninguna optimización en el código que involucre tales registros. Por lo tanto, es una buena práctica desensamblar dicho código y ver cómo resultó a nivel de ensamblador. - Lundín

Aquí está mi macro aritmética de bits favorita, que funciona para cualquier tipo de matriz de enteros sin signo de 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 poco:

BITOP(array, bit, |=);

Para aclarar un poco:

BITOP(array, bit, &=~);

Para alternar un poco:

BITOP(array, bit, ^=);

Para probar un poco:

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

etc.

contestado el 09 de mayo de 13 a las 15:05

Es bueno leer, pero uno debe estar consciente de los posibles efectos secundarios. Utilizando BITOP(array, bit++, |=); en un bucle lo más probable es que no haga lo que quiere la persona que llama. - foraidt

En efecto. =) Una variante que podría preferir es separarla en 2 macros, 1 para direccionar el elemento de la matriz y la otra para colocar el bit en su lugar, ala BITCELL(a,b) |= BITMASK(a,b); (ambos toman a como argumento para determinar el tamaño, pero este último nunca evaluaría a ya que aparece solo en sizeof). - R .. GitHub DEJA DE AYUDAR A ICE

@R .. Esta respuesta es muy antigua, pero probablemente preferiría una función a una macro en este caso. - PC ludita

Menor: el 3er (size_t) El elenco parece estar allí solo para asegurar que algunos matemáticas sin firmar con %. Podría (unsigned) allí. - chux - Reincorporar a Monica

El sistema (size_t)(b)/(8*sizeof *(a)) innecesariamente podría estrechar b antes de la división. Solo un problema con matrices de bits muy grandes. Sigue siendo una macro interesante. - chux - Reincorporar a Monica

El enfoque de campo de bits tiene otras ventajas en el ámbito integrado. Puede definir una estructura que se asigne directamente a los bits en un registro de hardware en particular.

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;

Debe conocer el orden de empaquetado de bits; creo que es MSB primero, pero esto puede depender de la implementación. Además, verifique cómo los campos de los controladores del compilador cruzan los límites de bytes.

A continuación, puede leer, escribir y probar los valores individuales como antes.

respondido 06 nov., 08:11

Prácticamente todo lo relacionado con los campos de bits está definido por la implementación. Incluso si logra averiguar todos los detalles sobre cómo su compilador particular los implementa, usarlos en su código seguramente lo hará no portátil. - Lundín

@Lundin: es cierto, pero la manipulación de bits del sistema integrado (particularmente en los registros de hardware, que es a lo que se refiere mi respuesta) nunca será útil para portátiles de todos modos. - Roddy

Quizás no entre CPU completamente diferentes. Pero lo más probable es que desee que sea portátil entre compiladores y entre diferentes proyectos. Y hay una gran cantidad de "manipulación de bits" incrustada que no está relacionada en absoluto con el hardware, como la codificación / decodificación del protocolo de datos. - Lundín

... y si adquiere el hábito de utilizar campos de bits para la programación incrustada, encontrará que su código X86 se ejecuta más rápido y más eficiente. No en simples evaluaciones comparativas donde tiene toda la máquina para aplastar la evaluación comparativa, sino en entornos multitarea del mundo real donde los programas compiten por los recursos. Advantage CISC: cuyo objetivo de diseño original era compensar las CPU más rápido que los buses y la memoria lenta. - usuario1899861

Verifique un poco en una ubicación arbitraria en una variable de tipo arbitrario:

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

Uso de la muestra:

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 Está diseñado para ser rápido (dada su flexibilidad) y sin ramificaciones. Da como resultado un código de máquina SPARC eficiente cuando se compila Sun Studio 8; También lo probé usando MSVC ++ 2008 en amd64. Es posible crear macros similares para configurar y borrar bits. La diferencia clave de esta solución en comparación con muchas otras aquí es que funciona para cualquier ubicación en prácticamente cualquier tipo de variable.

Respondido el 03 de enero de 09 a las 23:01

Más general, para mapas de bits de tamaño arbitrario:

#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)))

Respondido el 15 de junio de 09 a las 08:06

CHAR_BIT ya está definido por limits.h, no necesitas poner el tuyo BITS (y de hecho empeora su código al hacerlo) - MM

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

contestado el 22 de mayo de 15 a las 17:05

Utilizar esta:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}

contestado el 22 de mayo de 15 a las 17:05

Bueno, usa ramificaciones ineficientes. - asdf

@asdf El trabajo del compilador es generar el binario más eficiente, el trabajo del programador es escribir código claro - MM

Esta es una buena demostración de cómo probar, configurar y borrar un bit en particular. Sin embargo, es un muy mal enfoque para alternar un poco. - ben voigt

Si está haciendo muchas tonterías, es posible que desee usar máscaras que harán que todo sea más rápido. Las siguientes funciones son muy rápidas y aún flexibles (permiten manipular 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 que bytes, palabras, dwords, qwords, etc., se asignan correctamente entre sí en la memoria (la razón principal por la que los procesadores little endian son 'mejores' que los procesadores big-endian, ah, siento que se avecina una guerra de llamas en...).

Respondido el 05 de diciembre de 17 a las 11:12

No use una tabla para una función que se pueda implementar con un solo operador. TQuickByteMask [n] es equivalente a (1 < R .. GitHub DEJA DE AYUDAR A ICE

¿Qué sentido tiene esto? ¿Solo hace que el código sea más lento y más difícil de leer? No puedo ver una sola ventaja con eso. 1u << n es más fácil de leer para los programadores de C y, con suerte, se puede traducir en una única instrucción de CPU de tic de reloj. Su división, por otro lado, se traducirá en algo alrededor de 10 ticks, o incluso tan mal como hasta 100 ticks, dependiendo de qué tan mal la arquitectura específica maneje la división. En cuanto a la función de mapa de bits, tendría más sentido tener una tabla de búsqueda que traduzca cada índice de bits en un índice de bytes, para optimizar la velocidad. - Lundín

En cuanto a big / little endian, big endian mapeará enteros y datos sin procesar (por ejemplo, cadenas) de la misma manera: msb de izquierda a derecha a lsb en todo el mapa de bits. Mientras que little endian mapeará enteros de izquierda a derecha como 7-0, 15-8, 23-18, 31-24, pero los datos sin procesar siguen siendo msb de izquierda a derecha a lsb. Entonces, cuán poco endian es mejor para su algoritmo particular está completamente fuera de mi comprensión, parece ser lo contrario. - Lundín

@R .. Una tabla puede ser útil si su plataforma no puede cambiar de manera eficiente, como las antiguas mcu de microchip, pero, por supuesto, la división en la muestra es absolutamente ineficiente. Jeb

Ampliando el bitset responder:

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

contestado el 08 de mayo de 14 a las 05:05

Si desea realizar toda esta operación con la programación C en el Kernel Linux luego sugiero usar API estándar del kernel de Linux.

Ven https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html

set_bit  Atomically set a bit in memory
clear_bit  Clears a bit in memory
change_bit  Toggle a bit in memory
test_and_set_bit  Set a bit and return its old value
test_and_clear_bit  Clear a bit and return its old value
test_and_change_bit  Change a bit and return its old value
test_bit  Determine whether a bit is set

Nota: Aquí toda la operación ocurre en un solo paso. Así que todos estos están garantizados para ser atómico incluso en computadoras SMP y son útiles para mantener la coherencia entre procesadores.

Respondido el 01 de diciembre de 17 a las 16:12

Visual C 2010, y quizás muchos otros compiladores, tienen soporte directo para operaciones booleanas integradas. Un bit tiene dos valores posibles, como un booleano, por lo que podemos usar booleanos en su lugar, incluso si ocupan más espacio que un solo bit en memoria en esta representación. Esto funciona, incluso el sizeof() El operador 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, a tu pregunta, IsGph[i] =1 o el IsGph[i] =0 facilitan el establecimiento y la limpieza de bools.

Para buscar caracteres 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 este código no tiene nada de "especial". Se trata un poco como un número entero, que técnicamente lo es. Un entero de 1 bit que puede contener 2 valores y solo 2 valores.

Una vez utilicé este enfoque para encontrar registros de préstamos duplicados, donde número_préstamo era la clave ISAM, usando el número de préstamo de 6 dígitos como índice en la matriz de bits. Salvajemente rápido, y después de 8 meses, demostró que el sistema de mainframe del que estábamos obteniendo los datos estaba funcionando mal. La simplicidad de las matrices de bits hace que la confianza en su exactitud sea muy alta, en comparación con un enfoque de búsqueda, por ejemplo.

respondido 20 mar '20, 13:03

std :: bitset es de hecho implementado como bits por la mayoría de los compiladores - gallineta

@galinette, de acuerdo. El archivo de encabezado #include es un buen recurso en este sentido. Además, el vector de clase especial para cuando necesite cambiar el tamaño del vector. El C ++ STL, 2nd Edition, Nicolai M. Josuttis los cubre exhaustivamente en las páginas 650 y 281 respectivamente. C ++ 11 agrega algunas capacidades nuevas a std :: bitset, de especial interés para mí es una función hash en contenedores desordenados. ¡Gracias por el aviso! Voy a borrar mi comentario sobre calambres cerebrales. Ya hay suficiente basura en la web. No quiero agregar nada más. - usuario1899861

Esto usa al menos un byte completo de almacenamiento para cada bool. Tal vez incluso 4 bytes para configuraciones C89 que usan int para implementar bool - MM

@MattMcNabb, tienes razón. En C ++, el tamaño del tipo int necesario para implementar un booleano no está especificado por el estándar. Me di cuenta de que esta respuesta era un error hace algún tiempo, pero decidí dejarla aquí ya que la gente aparentemente la encuentra útil. Para aquellos que quieran usar bits, el comentario de galinette es más útil, al igual que mi biblioteca de bits aquí ... stackoverflow.com/a/16534995/1899861 - usuario1899861

@RocketRoy: Probablemente valga la pena cambiar la oración que afirma que este es un ejemplo de "operaciones de bits", entonces. - ben voigt

Supongamos algunas cosas primero
num = 55 Entero para realizar operaciones bit a bit (establecer, obtener, borrar, alternar).
n = 4 Posición de bit basada en 0 para realizar operaciones bit a bit.

¿Cómo conseguir un poco?

  1. Para obtener el nth un poco de desplazamiento numérico a la derecha num, n veces. Luego, realice AND bit a bit. & con 1.
bit = (num >> n) & 1;

¿Cómo funciona?

       0011 0111 (55 in decimal)
    >>         4 (right shift 4 times)
-----------------
       0000 0011
     & 0000 0001 (1 in decimal)
-----------------
    => 0000 0001 (final result)

¿Cómo configurar un poco?

  1. Para establecer un número determinado. Desplazamiento a la izquierda 1 n veces. Luego realice OR bit a bit | operación con num.
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

¿Cómo funciona?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     | 0011 0111 (55 in decimal)
-----------------
    => 0001 0000 (final result)

¿Cómo borrar un poco?

  1. Desplazamiento a la izquierda 1, n veces es decir 1 << n.
  2. Realice un complemento bit a bit con el resultado anterior. De modo que el bit n se desarma y el resto del bit se establece, es decir ~ (1 << n).
  3. Finalmente, realice AND bit a bit & operación con el resultado anterior y num. Los tres pasos anteriores juntos se pueden escribir como num & (~ (1 << n));

Pasos para despejar un poco

num &= (~(1 << n));    // Equivalent to; num = num & (~(1 << n));

¿Cómo funciona?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
     ~ 0001 0000
-----------------
       1110 1111
     & 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

¿Cómo alternar un poco?

Para alternar un poco usamos XOR bit a bit ^ operador. El operador XOR bit a bit se evalúa como 1 si el bit correspondiente de ambos operandos es diferente; de ​​lo contrario, se evalúa como 0.

Lo que significa que para alternar un poco, necesitamos realizar la operación XOR con el bit que desea alternar y 1.

num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);

¿Cómo funciona?

  • Si el bit para alternar es 0 entonces, 0 ^ 1 => 1.
  • Si el bit para alternar es 1 entonces, 1 ^ 1 => 0.
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Lectura recomendada - Ejercicios de operador bit a bit

Respondido el 10 de junio de 19 a las 07:06

Gracias por la explicación detallada. Aquí está el enlace para el problema de práctica de BIT Magic aquí - Chandra Shekhar

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

Respondido el 08 de junio de 19 a las 07:06

Tipo de retorno de check_nth_bit puede ser bool. - xeveroso

@Xeverous sí, depende de la intención de la persona que llama - Sazzad Hissain Khan

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)

Respondido 06 Feb 15, 23:02

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

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

¿Qué problemas suceden cuando number es un tipo más ancho que 1?
x puede ser demasiado grande para el turno 1 << x llevando a comportamiento indefinido (UB). Incluso si x no es demasiado grande, ~ puede que no invierta 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 asegurarse de que 1 sea lo suficientemente ancho:

El código podría usar 1ull o pedante (uintmax_t)1 y deje que el compilador optimice.

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

O emitir, lo que genera problemas de codificación / revisión / mantenimiento para mantener el reparto correcto y actualizado.

number |= (type_of_number)1 << x;

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

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

Como con la mayoría de las manipulaciones de bits, es mejor trabajar con no firmado tipos en lugar de firmado las

Respondido el 27 de Septiembre de 17 a las 19:09

¡Interesante mirada a una vieja pregunta! Ninguno de los dos number |= (type_of_number)1 << x; ni number |= (number*0 + 1) << x; apropiado para establecer el bit de signo de un tipo firmado ... De hecho, tampoco es number |= (1ull << x);. ¿Existe alguna forma portátil de hacerlo por posición? - chqrlie

@chqrlie IMO, la mejor manera de evitar configurar el bit de signo y arriesgar UB o IDB con cambios es usar no firmado tipos. Turno altamente portátil firmado el código es demasiado complicado para ser aceptable. - chux - Reincorporar a Monica

Una versión con plantilla (colocada en un archivo de encabezado) con soporte para cambiar múltiples bits (funciona en microcontroladores AVR por cierto):

namespace bit {
  template <typename T1, typename T2>
  constexpr inline T1 bitmask(T2 bit) 
  {return (T1)1 << bit;}
  template <typename T1, typename T3, typename ...T2>
  constexpr inline T1 bitmask(T3 bit, T2 ...bits) 
  {return ((T1)1 << bit) | bitmask<T1>(bits...);}

  /** Set these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void set (T1 &variable, T2 ...bits) 
  {variable |= bitmask<T1>(bits...);}
  /** Set only these bits (others will be cleared) */
  template <typename T1, typename ...T2>
  constexpr inline void setOnly (T1 &variable, T2 ...bits) 
  {variable = bitmask<T1>(bits...);}
  /** Clear these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void clear (T1 &variable, T2 ...bits) 
  {variable &= ~bitmask<T1>(bits...);}
  /** Flip these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void flip (T1 &variable, T2 ...bits) 
  {variable ^= bitmask<T1>(bits...);}
  /** Check if any of these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isAnySet(const T1 &variable, T2 ...bits) 
  {return variable & bitmask<T1>(bits...);}
  /** Check if all these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) == bitmask<T1>(bits...));}
  /** Check if all these bits are not set */
  template <typename T1, typename ...T2>
  constexpr inline bool isNotSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) != bitmask<T1>(bits...));}
}

Ejemplo de uso:

#include <iostream>
#include <bitset> // for console output of binary values

// and include the code above of course

using namespace std;

int main() {
  uint8_t v = 0b1111'1100;
  bit::set(v, 0);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1,2,3,4,5,6,7);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,7);
  cout << bitset<8>(v) << endl;
}

Por cierto: resulta que constexpr e inline no se utilizan si no se envía el argumento del optimizador (por ejemplo: -O3) al compilador. No dude en probar el código en https://godbolt.org/ y observe la salida de ASM.

Respondido 29 Oct 20, 22:10

Este código está roto. (Además, ¿por qué tienes ; después de las definiciones de su función?) - melpomene

@melpomene El código no está roto, lo probé. ¿Quiere decir que no se compilará o que el resultado es incorrecto? Sobre el extra ';' No recuerdo, esos pueden eliminarse de hecho. - Joakim L. Christiansen

(variable & bits == bits)? - melpomene

Gracias por darte cuenta, se suponía que era ((variable & bits) == bits) - Joakim L. Christiansen

utilizado std::bitseten c ++ 11 - pqnet

Este programa se basa en la solución anterior de @ Jeremy. Si alguien desea jugar rápidamente.

public class BitwiseOperations {

    public static void main(String args[]) {

        setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
        clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
        toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
        checkABit(8,4); // check the 4th bit 1000 -> true 
    }

    public static void setABit(int input, int n) {
        input = input | ( 1 << n-1);
        System.out.println(input);
    }


    public static void clearABit(int input, int n) {
        input = input & ~(1 << n-1);
        System.out.println(input);
    }

    public static void toggleABit(int input, int n) {
        input = input ^ (1 << n-1);
        System.out.println(input);
    }

    public static void checkABit(int input, int n) {
        boolean isSet = ((input >> n-1) & 1) == 1; 
        System.out.println(isSet);
    }
}


Output :
8
0
0
true

Respondido 22 Abr '20, 21:04

Establecer el bit n en x (valor de bit) sin usar -1

A veces, cuando no está seguro de en qué resultará -1 o similar, es posible que desee establecer el bit n sin usar -1:

number = (((number | (1 << n)) ^ (1 << n))) | (x << n);

Explicación: ((number | (1 << n) establece el bit n en 1 (donde | denota OR bit a bit), luego con (...) ^ (1 << n) establecemos el n-ésimo bit en 0, y finalmente con (...) | x << n) establecemos el enésimo bit que era 0, en (valor de bit) x.

Esto también funciona en golang.

Respondido 06 Feb 21, 09:02

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

char bitfield;

// Start at 0th position

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

Or

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

Or

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

Respondido 16 Feb 18, 01:02

value << n puede causar un comportamiento indefinido - MM

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.