Ottieni un bit specifico da byte


97

Ho un byte, in particolare un byte da un array di byte che è arrivato tramite UDP inviato da un altro dispositivo. Questo byte memorizza lo stato di attivazione / disattivazione di 8 relè nel dispositivo.

Come ottengo il valore di un bit specifico in detto byte? Idealmente un metodo di estensione sarebbe il più elegante e restituire un bool avrebbe più senso per me.

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

Risposte:


178

Facile. Usa un AND bit per bit per confrontare il tuo numero con il valore 2 ^ bitNumber, che può essere calcolato a buon mercato spostando il bit.

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

EDIT: Per aggiungere un po 'più di dettagli perché ci sono molte risposte simili senza spiegazione:

Un AND bit per bit confronta ogni numero, bit per bit, utilizzando un join AND per produrre un numero che è la combinazione di bit in cui sono stati impostati sia il primo che il secondo bit in quella posizione. Ecco la matrice logica della logica AND in un "nibble" che mostra il funzionamento di un AND bit per bit:

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

Nel tuo caso, confrontiamo il numero che hai passato con un numero che ha solo il bit che vuoi cercare impostato. Diciamo che stai cercando il quarto bit:

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

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

Lo spostamento di bit, per produrre il numero con cui vogliamo confrontare, è esattamente quello che sembra: prendi il numero, rappresentato come un insieme di bit, e sposta quei bit a sinistra oa destra di un certo numero di posizioni. Poiché questi sono numeri binari e quindi ogni bit è una maggiore potenza di due rispetto a quello alla sua destra, lo spostamento di bit a sinistra equivale a raddoppiare il numero una volta per ogni posizione che viene spostata, equivalente a moltiplicare il numero per 2 ^ x. Nel tuo esempio, cercando il quarto bit, eseguiamo:

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

Ora sai come si fa, cosa sta succedendo a basso livello e perché funziona.


8
A causa della mancanza di parentesi graffe (precedenza dell'operatore) questo codice non viene compilato, deve essere var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk

54

Sebbene sia utile leggere e comprendere la risposta di Josh, probabilmente sarai più felice di utilizzare la classe fornita da Microsoft per questo scopo: System.Collections.BitArray È disponibile in tutte le versioni di .NET Framework.


1
È favoloso, ma credo che la soluzione di Josh sia molto più veloce ed efficiente.
Samuel Allan

1
@ user2332868: il compilatore JIT riconosce in modo speciale le chiamate a determinate funzioni di libreria e genera codice efficiente, ma non ho idea se queste particolari funzioni ottengano l'amore.
Ben Voigt

1
bene per essere sicuri di poter fare lo stesso di Josh ma in puro assemblaggio in linea. Ma purtroppo programma solo in assembly NASM e non so dell'assembly C # si compila in :(.
Samuel Allan

1
IMHO questo uccide le prestazioni per un compito così semplice. L'uso dell'operatore alternativo bit per bit ti dà anche uno strumento che puoi usare in quasi tutte le lingue =)
Gaspa79

38

Questo

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

dovrebbe farlo, credo.


9

un altro modo per farlo :)

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

1
Questo non funzionerà: byte b = 1; return ((b >> 1) & 1)! = 0 (è uguale a 0)
Rafael Diego Nicoletti

1
Hmmm ... non ti capisco. Se byte b = 1, il bit nella posizione 0 è 1, dato da (b >> 0) e 1, e il bit in qualsiasi posizione maggiore o uguale a 1 è 0, dato da (b >> n) & 1 dove n> = 1 anche
PierrOz

Non credo che lo faccia per niente @DavideAndrea, il commento pubblicato da Rafael presume che il bit più a destra sia il bit 1, ma il codice di PierrOz è per quando il bit più a destra è il bit 0. Se b era 2, allora ((2 >> 1)&1)è 1e ((2 >> 0)&1)è 0perché 2 è00000010
Cameron Aavik


6

Utilizzando la classe BitArray e creando un metodo di estensione come suggerito da OP:

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

9
No, per favore non creare e buttare via un BitArray per ogni test di bit.
Ben Voigt

@ BenVoigt, è un metodo di estensione su un byte per richiesta OP. Dove consigli di archiviare l'istanza BitArray?
Jay Walker

2
Respingi la richiesta e dici, non chiamarlo come un metodo sul byte, chiamalo su BitArray. Forse la variabile byte può scomparire completamente.
Ben Voigt

Se stai cercando di ottenere l'ennesimo bit di byte, molto probabilmente stai cercando prestazioni, e questo lo uccide. Altrimenti potresti sempre fare altre cose non necessarie come Convert.ToString (num, 2) [bitNumber] e ottenerlo.
Gaspa79


3

Il metodo consiste nell'usare un altro byte insieme a un AND bit per bit per mascherare il bit di destinazione.

Ho usato le convenzioni delle mie classi qui dove "0" è il bit più significativo e "7" è il minimo.

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

Prova il codice qui sotto. La differenza con altri post è che puoi impostare / ottenere più bit usando una maschera ( field). La maschera per il 4 ° bit può essere, ad esempio, 1 << 3 o 0x10.

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

** Esempio **

        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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.