Obtener un bit específico de byte

97

Tengo un byte, específicamente un byte de una matriz de bytes que vino a través de UDP enviado desde otro dispositivo. Este byte almacena el estado de encendido / apagado de 8 relés en el dispositivo.

¿Cómo obtengo el valor de un bit específico en dicho byte? Idealmente, un método de extensión se vería más elegante y devolver un bool tendría más sentido para mí.

public static bool GetBit(this byte b, int bitNumber)
{
    //black magic goes here
}
keithwarren7
fuente

Respuestas:

178

Fácil. Utilice un AND bit a bit para comparar su número con el valor 2 ^ bitNumber, que puede calcularse de forma económica mediante el desplazamiento de bits.

//your black magic
var bit = (b & (1 << bitNumber-1)) != 0;

EDITAR: Para agregar un poco más de detalle porque hay muchas respuestas similares sin explicación:

Un AND bit a bit compara cada número, bit a bit, usando una unión AND para producir un número que es la combinación de bits donde se establecieron tanto el primer bit como el segundo bit en ese lugar. Aquí está la matriz lógica de la lógica AND en un "nibble" que muestra el funcionamiento de un AND bit a bit:

  0101
& 0011
  ----
  0001 //Only the last bit is set, because only the last bit of both summands were set

En su caso, comparamos el número que pasó con un número que solo tiene el bit que desea buscar. Digamos que estás buscando el cuarto bit:

  11010010
& 00001000
  --------
  00000000 //== 0, so the bit is not set

  11011010
& 00001000
  --------
  00001000 //!= 0, so the bit is set

El cambio de bits, para producir el número con el que queremos comparar, es exactamente lo que parece: tome el número, representado como un conjunto de bits, y mueva esos bits hacia la izquierda o hacia la derecha en un cierto número de lugares. Debido a que estos son números binarios y, por lo tanto, cada bit es una potencia de dos mayor que el que está a su derecha, el desplazamiento de bits a la izquierda equivale a duplicar el número una vez por cada lugar que se desplaza, equivalente a multiplicar el número por 2 ^ x. En su ejemplo, buscando el cuarto bit, realizamos:

       1 (2^0) << (4-1) ==        8 (2^3)
00000001       << (4-1) == 00001000

Ahora sabe cómo se hace, qué está sucediendo en el nivel bajo y por qué funciona.

KeithS
fuente
8
Debido a que faltan llaves (precedencia de operadores), este código no se compila, debe serlo var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk
54

Si bien es bueno leer y comprender la respuesta de Josh, probablemente estará más feliz usando la clase que Microsoft proporcionó para este propósito: System.Collections.BitArray Está disponible en todas las versiones de .NET Framework.

Ben Voigt
fuente
1
Esto es fabuloso, pero creo que la solución de Josh es mucho más rápida y eficiente.
Samuel Allan
1
@ user2332868: el compilador JIT reconoce especialmente las llamadas a ciertas funciones de la biblioteca y genera código eficiente, pero no tengo idea de si estas funciones en particular obtienen el amor.
Ben Voigt
1
Bueno, para estar seguro de que puede hacer lo mismo que Josh pero en puro ensamblaje en línea. Pero, lamentablemente, solo programo en el ensamblado NASM y no sé sobre el ensamblado que C # compila en :(.
Samuel Allan
1
En mi humilde opinión, esto mata el rendimiento para una tarea tan simple. El uso de la alternativa del operador bit a bit también le brinda una herramienta que puede usar en casi cualquier idioma =)
Gaspa79
38

Esta

public static bool GetBit(this byte b, int bitNumber) {
   return (b & (1 << bitNumber)) != 0;
}

debería hacerlo, creo.


fuente
9

otra forma de hacerlo :)

return ((b >> bitNumber) & 1) != 0;
Pierroz
fuente
1
Esto no funcionará: byte b = 1; return ((b >> 1) & 1)! = 0 (es igual a 0)
Rafael Diego Nicoletti
1
Hmmm ... no te entiendo. Si el byte b = 1, el bit en la posición 0 es 1, dado por (b >> 0) & 1, y el bit en cualquier posición mayor o igual que 1 es 0, dado por (b >> n) & 1 donde n> = 1 también
PierrOz
No creo que él haga eso en absoluto @DavideAndrea, el comentario publicado por Rafael asume que el bit más a la derecha es el bit 1, pero el código de PierrOz es para cuando el bit más a la derecha es el bit 0. Si b era 2, entonces ((2 >> 1)&1)es 1y ((2 >> 0)&1)es 0porque 2 es00000010
Cameron Aavik
7

Esto funciona más rápido que 0,1 milisegundos.

return (b >> bitNumber) & 1;
Asim Olmez
fuente
Este es el método más rápido.
Adam Calvet Bohl
6

Usando la clase BitArray y haciendo un método de extensión como OP sugiere:

public static bool GetBit(this byte b, int bitNumber)
{
    System.Collections.BitArray ba = new BitArray(new byte[]{b});
    return ba.Get(bitNumber);
}
Peatón imprudente
fuente
9
No, no cree ni deseche un BitArray para cada prueba de bits.
Ben Voigt
@BenVoigt, es un método de extensión en un byte por solicitud de OP. ¿Dónde recomiendas almacenar la instancia de BitArray?
Jay Walker
2
Usted rechaza la solicitud y dice, no lo llame como un método en el byte, llámelo en BitArray. Quizás la variable byte pueda desaparecer por completo.
Ben Voigt
Si está buscando obtener el enésimo bit de un byte, lo más probable es que esté buscando rendimiento, y esto lo mata. De lo contrario, siempre podría hacer otras cosas innecesarias como Convert.ToString (num, 2) [bitNumber] y obtenerlo también.
Gaspa79
5

prueba esto:

return (b & (1 << bitNumber))>0;
Aliostad
fuente
3

El método consiste en utilizar otro byte junto con un AND bit a bit para enmascarar el bit de destino.

Usé la convención de mis clases aquí donde "0" es el bit más significativo y "7" es el mínimo.

public static class ByteExtensions
{
    // Assume 0 is the MSB andd 7 is the LSB.
    public static bool GetBit(this byte byt, int index)
    {
        if (index < 0 || index > 7)
            throw new ArgumentOutOfRangeException();

        int shift = 7 - index;

        // Get a single bit in the proper position.
        byte bitMask = (byte)(1 << shift);

        // Mask out the appropriate bit.
        byte masked = (byte)(byt & bitMask);

        // If masked != 0, then the masked out bit is 1.
        // Otherwise, masked will be 0.
        return masked != 0;
    }
}
Zafiro
fuente
3

Pruebe el siguiente código. La diferencia con otras publicaciones es que puede establecer / obtener múltiples bits usando una máscara ( field). La máscara para el cuarto bit puede ser 1 << 3, o 0x10, por ejemplo.

    public int SetBits(this int target, int field, bool value)
    {
        if (value) //set value
        {
            return target | field;
        }
        else //clear value
        {
            return target & (~field);
        }
    }

    public bool GetBits(this int target, int field)
    {
        return (target & field) > 0;
    }

** Ejemplo **

        bool is_ok = 0x01AF.GetBits(0x10); //false
        int res = 0x01AF.SetBits(0x10, true);
        is_ok = res.GetBits(0x10);  // true
John Alexiou
fuente
2
[Flags]
enum Relays : byte
{
    relay0 = 1 << 0,
    relay1 = 1 << 1,
    relay2 = 1 << 2,
    relay3 = 1 << 3,
    relay4 = 1 << 4,
    relay5 = 1 << 5,
    relay6 = 1 << 6,
    relay7 = 1 << 7
}

public static bool GetRelay(byte b, Relays relay)
{
    return (Relays)b.HasFlag(relay);
}
jabukufo
fuente