Uzyskaj określony bit z bajtu

97

Mam bajt, a konkretnie jeden bajt z tablicy bajtów, która przyszła za pośrednictwem protokołu UDP wysłanego z innego urządzenia. Ten bajt przechowuje stan włączenia / wyłączenia 8 przekaźników w urządzeniu.

Jak uzyskać wartość określonego bitu we wspomnianym bajcie? Idealnie byłoby, gdyby metoda rozszerzenia wyglądała najbardziej elegancko, a zwrócenie wartości bool miałoby dla mnie największy sens.

public static bool GetBit(this byte b, int bitNumber)
{
    //black magic goes here
}
keithwarren7
źródło

Odpowiedzi:

179

Łatwo. Użyj bitowego AND, aby porównać swoją liczbę z wartością 2 ^ bitNumber, którą można tanio obliczyć przez przesunięcie bitów.

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

EDYTUJ: Aby dodać trochę więcej szczegółów, ponieważ istnieje wiele podobnych odpowiedzi bez wyjaśnienia:

Bitowe AND porównuje każdą liczbę, bit po bicie, używając sprzężenia AND w celu uzyskania liczby będącej kombinacją bitów, w której ustawiono zarówno pierwszy, jak i drugi bit w tym miejscu. Oto macierz logiczna logiki AND w „nibble”, która przedstawia operację bitowego AND:

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

W twoim przypadku porównujemy podaną liczbę z liczbą zawierającą tylko ten bit, którego szukasz. Powiedzmy, że szukasz czwartego bitu:

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

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

Przesuwanie bitów w celu uzyskania liczby, z którą chcemy porównać, jest dokładnie tym, co brzmi: weź liczbę, reprezentowaną jako zestaw bitów i przesuń te bity w lewo lub w prawo o określoną liczbę miejsc. Ponieważ są to liczby binarne, a więc każdy bit jest o jedną potęgę dwójki większy niż ten po prawej stronie, przesunięcie bitu w lewo jest równoważne dwukrotnemu podwojeniu liczby dla każdego przesuniętego miejsca, co jest równoznaczne z pomnożeniem liczby przez 2 ^ x. W Twoim przykładzie, szukając czwartego bitu, wykonujemy:

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

Teraz wiesz, jak to się robi, co się dzieje na niskim poziomie i dlaczego to działa.

KeithS
źródło
8
Z powodu brakujących nawiasów klamrowych (pierwszeństwo operatorów) ten kod nie jest kompilowany, musi być var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk
54

Chociaż dobrze jest przeczytać i zrozumieć odpowiedź Josha, prawdopodobnie będziesz szczęśliwszy, używając do tego celu klasy firmy Microsoft: System.Collections.BitArray Jest dostępna we wszystkich wersjach .NET Framework.

Ben Voigt
źródło
1
To wspaniałe, ale uważam, że rozwiązanie Josha jest znacznie szybsze i bardziej wydajne.
Samuel Allan
1
@ user2332868: kompilator JIT specjalnie rozpoznaje wywołania niektórych funkcji biblioteki i generuje wydajny kod, ale nie mam pojęcia, czy te konkretne funkcje są lubiane.
Ben Voigt,
1
cóż, aby mieć pewność, że możesz zrobić to samo co Josh, ale w czystym montażu na linii. Ale niestety programuję tylko w asemblerze NASM i nie wiem o kompilacji C # assemblera do :(.
Samuel Allan
1
IMHO to zabija wydajność w tak prostym zadaniu. Korzystanie z alternatywnego operatora bitowego daje również narzędzie, którego możesz używać w prawie każdym języku =)
Gaspa79 Kwietnia
38

To

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

powinienem to zrobić, myślę.


źródło
9

inny sposób na zrobienie tego :)

return ((b >> bitNumber) & 1) != 0;
PierrOz
źródło
1
To nie zadziała: bajt b = 1; return ((b >> 1) & 1)! = 0 (równa się 0)
Rafael Diego Nicoletti
1
Hmmm ... Nie rozumiem cię. Jeśli bajt b = 1, bit na pozycji 0 to 1, określony przez (b >> 0) & 1, a bit na dowolnej pozycji większej lub równej 1 to 0, określony przez (b >> n) & 1, gdzie n> = 1 również
PierrOz
Nie wierzę, że w ogóle to robi @DavideAndrea, komentarz opublikowany przez Rafaela zakłada, że ​​najbardziej prawy bit to bit 1, ale kod PierrOza dotyczy tego, gdy najbardziej prawy bit to bit 0. Jeśli b wynosi 2, to ((2 >> 1)&1)jest 1i ((2 >> 0)&1)to 0dlatego, że 2 to00000010
Cameron Aavik
8

Działa to szybciej niż 0,1 milisekundy.

return (b >> bitNumber) & 1;
Asim Olmez
źródło
To najszybsza metoda.
Adam Calvet Bohl
6

Korzystanie z klasy BitArray i tworzenie metody rozszerzającej zgodnie z sugestią OP:

public static bool GetBit(this byte b, int bitNumber)
{
    System.Collections.BitArray ba = new BitArray(new byte[]{b});
    return ba.Get(bitNumber);
}
Jay Walker
źródło
9
Nie, proszę nie tworzyć i wyrzucać BitArray dla każdego testu bitowego.
Ben Voigt
@BenVoigt, jest to metoda rozszerzająca bajt na żądanie OP. Gdzie zaleca się przechowywanie instancji BitArray?
Jay Walker
2
Odpychasz żądanie i mówisz, nie wywołuj go jak metody na bajcie, wywołaj go na tablicy BitArray. Może zmienna bajtowa może całkowicie zniknąć.
Ben Voigt,
Jeśli chcesz uzyskać n-ty bit bajtu, najprawdopodobniej szukasz wydajności, a to zabija. W przeciwnym razie zawsze możesz zrobić inne niepotrzebne rzeczy, takie jak Convert.ToString (num, 2) [bitNumber] i też je zdobyć.
Gaspa79
5

Spróbuj tego:

return (b & (1 << bitNumber))>0;
Aliostad
źródło
3

Metoda polega na użyciu innego bajtu wraz z bitowym AND, aby zamaskować bit docelowy.

Użyłem konwencji z moich zajęć, gdzie „0” jest najbardziej znaczącym bitem, a „7” najmniejszym.

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;
    }
}
Safa
źródło
3

Wypróbuj poniższy kod. Różnica w stosunku do innych postów polega na tym, że możesz ustawić / pobrać wiele bitów za pomocą maski ( field). Maska dla czwartego bitu może na przykład wynosić 1 << 3 lub 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;
    }

** Przykład **

        bool is_ok = 0x01AF.GetBits(0x10); //false
        int res = 0x01AF.SetBits(0x10, true);
        is_ok = res.GetBits(0x10);  // true
John Alexiou
źródło
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
źródło