Obtenha um bit específico do byte


97

Eu tenho um byte, especificamente um byte de uma matriz de bytes que veio via UDP enviada de outro dispositivo. Este byte armazena o estado ligado / desligado de 8 relés no dispositivo.

Como obtenho o valor de um bit específico no referido byte? Idealmente, um método de extensão teria uma aparência mais elegante e retornar um bool faria mais sentido para mim.

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

Respostas:


178

Fácil. Use um E bit a bit para comparar seu número com o valor 2 ^ bitNumber, que pode ser calculado de forma barata por deslocamento de bits.

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

EDIT: Para adicionar um pouco mais de detalhes porque existem muitas respostas semelhantes sem explicação:

Um AND bit a bit compara cada número, bit a bit, usando uma junção AND para produzir um número que é a combinação de bits em que o primeiro e o segundo bit naquele local foram definidos. Aqui está a matriz lógica da lógica AND em um "nibble" que mostra a operação de um AND bit a bit:

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

No seu caso, comparamos o número que você passou com um número que possui apenas o bit que você deseja procurar definido. Digamos que você esteja procurando o quarto bit:

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

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

A alteração de bits, para produzir o número com o qual queremos comparar, é exatamente o que parece: pegue o número, representado como um conjunto de bits, e desloque esses bits para a esquerda ou para a direita por um certo número de casas. Como esses são números binários e cada bit é uma potência de dois maior do que aquele à sua direita, o deslocamento de bits para a esquerda equivale a dobrar o número uma vez para cada casa que é deslocada, equivalente a multiplicar o número por 2 ^ x. No seu exemplo, procurando o quarto bit, executamos:

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

Agora você sabe como isso é feito, o que está acontecendo no nível inferior e por que funciona.


8
Por causa das chaves ausentes (precedência do operador), este código não compila, deve ser var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk

54

Embora seja bom ler e entender a resposta de Josh, você provavelmente ficará mais feliz usando a classe fornecida pela Microsoft para esse propósito: System.Collections.BitArray Está disponível em todas as versões do .NET Framework.


1
Isso é fabuloso, mas acredito que a solução de Josh é muito mais rápida e eficiente.
Samuel Allan

1
@ user2332868: o compilador JIT reconhece especialmente chamadas para certas funções de biblioteca e gera um código eficiente, mas não tenho ideia se essas funções em particular recebem o amor.
Ben Voigt,

1
bem para ter certeza de que você pode fazer o mesmo que Josh, mas em montagem embutida pura. Mas, infelizmente, eu apenas programo em assembly NASM e não sei sobre o assembly que C # compila em :(.
Samuel Allan

1
IMHO isso mata o desempenho para uma tarefa tão simples. Usar a alternativa do operador bit a bit também oferece uma ferramenta que você pode usar em quase qualquer idioma =)
Gaspa79

38

este

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

deveria fazer isso, eu acho.


9

outra maneira de fazer isso :)

return ((b >> bitNumber) & 1) != 0;

1
Isso não funcionará: byte b = 1; return ((b >> 1) & 1)! = 0 (é igual a 0)
Rafael Diego Nicoletti

1
Hmmm ... eu não entendo você. Se o byte b = 1, o bit na posição 0 é 1, dado por (b >> 0) & 1, e o bit em qualquer posição maior ou igual a 1 é 0, dado por (b >> n) & 1, onde n> = 1 também
PierrOz

Não acredito que ele faça isso @DavideAndrea, o comentário postado por Rafael assume que o bit mais certo é o bit 1, mas o código de PierrOz é para quando o bit mais certo é o bit 0. Se b era 2, então ((2 >> 1)&1)é 1e ((2 >> 0)&1)é 0porque 2 é00000010
Cameron Aavik


6

Usando a classe BitArray e criando um método de extensão como o OP sugere:

public static bool GetBit(this byte b, int bitNumber)
{
    System.Collections.BitArray ba = new BitArray(new byte[]{b});
    return ba.Get(bitNumber);
}

9
Não, por favor, não crie e jogue fora um BitArray para cada teste de bit.
Ben Voigt

@BenVoigt, é um método de extensão em um byte por solicitação OP. Onde você recomenda armazenar a instância do BitArray?
Jay Walker

2
Você recua contra a solicitação e diz: não chame-o como um método no byte, chame-o no BitArray. Talvez a variável byte possa desaparecer completamente.
Ben Voigt

Se você está procurando obter o enésimo bit de um byte, provavelmente está procurando desempenho, e isso o mata. Caso contrário, você sempre pode fazer outras coisas desnecessárias como Convert.ToString (num, 2) [bitNumber] e obtê-lo também.
Gaspa79,


3

O método é usar outro byte junto com um AND bit a bit para mascarar o bit de destino.

Usei a convenção de minhas aulas aqui, onde "0" é o bit mais significativo e "7" é o menos.

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

3

Experimente o código abaixo. A diferença com outras postagens é que você pode definir / obter vários bits usando uma máscara ( field). A máscara para o 4º bit pode ser 1 << 3 ou 0x10, por exemplo.

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

** Exemplo **

        bool is_ok = 0x01AF.GetBits(0x10); //false
        int res = 0x01AF.SetBits(0x10, true);
        is_ok = res.GetBits(0x10);  // true

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);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.