Dapatkan bit spesifik dari byte


97

Saya memiliki satu byte, khususnya satu byte dari array byte yang masuk melalui UDP yang dikirim dari perangkat lain. Byte ini menyimpan status hidup / mati dari 8 relai di perangkat.

Bagaimana cara mendapatkan nilai bit tertentu dalam byte tersebut? Idealnya metode ekstensi akan terlihat paling elegan dan mengembalikan bool paling masuk akal bagi saya.

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

Jawaban:


179

Mudah. Gunakan bitwise AND untuk membandingkan bilangan Anda dengan nilai 2 ^ bitNumber, yang dapat dihitung secara murah dengan bit-shifting.

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

EDIT: Untuk menambahkan sedikit lebih banyak detail karena ada banyak jawaban serupa tanpa penjelasan:

Sebuah bitwise AND membandingkan setiap angka, bit demi bit, menggunakan gabungan AND untuk menghasilkan sebuah angka yang merupakan kombinasi dari bit dimana bit pertama dan bit kedua di tempat itu ditetapkan. Berikut adalah matriks logika logika AND dalam "nibble" yang menunjukkan operasi dari bitwise AND:

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

Dalam kasus Anda, kami membandingkan angka yang Anda berikan dengan angka yang hanya memiliki bit yang ingin Anda cari. Katakanlah Anda sedang mencari bit keempat:

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

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

Pergeseran bit, untuk menghasilkan bilangan yang ingin kita bandingkan, persis seperti namanya: ambil angkanya, direpresentasikan sebagai sekumpulan bit, dan geser bit tersebut ke kiri atau kanan dengan sejumlah tempat. Karena ini adalah bilangan biner dan setiap bit adalah satu pangkat dua yang lebih besar daripada yang ke kanan, menggeser bit ke kiri setara dengan menggandakan angka sekali untuk setiap tempat yang digeser, setara dengan mengalikan angka dengan 2 ^ x. Dalam contoh Anda, mencari bit keempat, kami melakukan:

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

Sekarang Anda tahu bagaimana hal itu dilakukan, apa yang terjadi di tingkat rendah, dan mengapa berhasil.


8
Karena tanda kurung tidak ada (prioritas operator), kode ini tidak dapat dikompilasi, harus var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk

54

Meskipun baik untuk membaca dan memahami jawaban Josh, Anda mungkin akan lebih senang menggunakan kelas yang disediakan Microsoft untuk tujuan ini: System.Collections.BitArray Tersedia di semua versi .NET Framework.


1
Ini luar biasa tapi saya yakin solusi Josh jauh lebih cepat dan lebih efisien.
Samuel Allan

1
@ user2332868: kompilator JIT secara khusus mengenali panggilan ke fungsi perpustakaan tertentu dan menghasilkan kode yang efisien, tetapi saya tidak tahu apakah fungsi khusus ini disukai.
Ben Voigt

1
baik untuk memastikan Anda dapat melakukan hal yang sama seperti Josh tetapi dalam perakitan inline murni. Tapi sayangnya saya hanya memprogram di perakitan NASM dan tidak tahu tentang perakitan C # dikompilasi menjadi :(.
Samuel Allan

1
IMHO ini membunuh kinerja untuk tugas yang begitu sederhana. Menggunakan alternatif operator bitwise juga memberi Anda alat yang dapat digunakan di hampir semua bahasa =)
Gaspa79

38

Ini

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

harus melakukannya, saya pikir.


9

cara lain untuk melakukannya :)

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

1
Ini tidak akan berhasil: byte b = 1; return ((b >> 1) & 1)! = 0 (itu sama dengan 0)
Rafael Diego Nicoletti

1
Hmmm ... Saya tidak mengerti. Jika byte b = 1, bit pada posisi 0 adalah 1, diberikan oleh (b >> 0) & 1, dan bit pada posisi apapun lebih besar atau sama dari 1 adalah 0, diberikan oleh (b >> n) & 1 dimana n> = 1 juga
PierrOz

Saya tidak percaya dia melakukan itu sama sekali @DavideAndrea, komentar yang diposting oleh Rafael mengasumsikan bit paling kanan adalah bit 1, tetapi kode PierrOz adalah untuk ketika bit paling tepat adalah bit 0. Jika b adalah 2, maka ((2 >> 1)&1)adalah 1dan ((2 >> 0)&1)adalah 0karena 2 adalah00000010
Cameron Aavik


6

Menggunakan kelas BitArray dan membuat metode ekstensi seperti yang disarankan OP:

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

9
Tidak, mohon jangan membuat dan membuang BitArray untuk setiap pengujian bit.
Ben Voigt

@BenVoigt, ini adalah metode ekstensi pada satu byte per permintaan OP. Di mana Anda merekomendasikan untuk menyimpan instance BitArray?
Jay Walker

2
Anda menolak permintaan tersebut dan berkata, jangan menyebutnya seperti metode pada byte, panggil di BitArray. Mungkin variabel byte bisa benar-benar hilang.
Ben Voigt

Jika Anda ingin mendapatkan bit nth dari sebuah byte kemungkinan besar Anda mencari kinerja, dan ini membunuhnya. Jika tidak, Anda selalu dapat melakukan hal-hal tidak perlu lainnya seperti Convert.ToString (num, 2) [bitNumber] dan mendapatkannya juga.
Gaspa79


3

Metodenya adalah menggunakan byte lain bersama dengan bitwise AND untuk menutupi bit target.

Saya menggunakan konvensi dari kelas saya di sini di mana "0" adalah bit yang paling signifikan dan "7" adalah yang paling kecil.

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

Coba kode di bawah ini. Perbedaannya dengan posting lain adalah Anda dapat mengatur / mendapatkan banyak bit menggunakan mask ( field). Mask untuk bit ke-4 bisa berupa 1 << 3, atau 0x10, misalnya.

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

** Contoh **

        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);
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.