Nhận một bit cụ thể từ byte


97

Tôi có một byte, cụ thể là một byte từ một mảng byte đến qua UDP được gửi từ một thiết bị khác. Byte này lưu trữ trạng thái bật / tắt của 8 rơle trong thiết bị.

Làm cách nào để lấy giá trị của một bit cụ thể trong byte đã nói? Lý tưởng nhất là một phương pháp mở rộng sẽ trông thanh lịch nhất và trả về bool sẽ có ý nghĩa nhất đối với tôi.

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

Câu trả lời:


178

Dễ dàng. Sử dụng bitwise AND để so sánh số của bạn với giá trị 2 ^ bitNumber, có thể được tính toán rẻ bằng cách dịch chuyển bit.

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

CHỈNH SỬA: Để thêm một chút chi tiết vì có rất nhiều câu trả lời tương tự mà không có lời giải thích:

Một bitwise AND so sánh từng số, từng bit, sử dụng phép nối AND để tạo ra một số là sự kết hợp của các bit nơi đặt cả bit đầu tiên và bit thứ hai ở vị trí đó. Đây là ma trận logic của logic AND trong một "nibble" hiển thị hoạt động của AND bitwise:

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

Trong trường hợp của bạn, chúng tôi so sánh số bạn đã chuyển với một số chỉ có bit mà bạn muốn tìm. Giả sử bạn đang tìm kiếm bit thứ tư:

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

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

Dịch chuyển bit, để tạo ra số mà chúng ta muốn so sánh, chính xác như âm thanh của nó: lấy số, được biểu diễn dưới dạng một tập hợp các bit và dịch chuyển các bit đó sang trái hoặc phải theo một số vị trí nhất định. Bởi vì đây là các số nhị phân và vì vậy mỗi bit là một lũy thừa lớn hơn hai so với bit ở bên phải, việc dịch chuyển bit sang trái tương đương với việc nhân đôi số một lần cho mỗi vị trí được dịch chuyển, tương đương với việc nhân số với 2 ^ x. Trong ví dụ của bạn, tìm kiếm bit thứ tư, chúng tôi thực hiện:

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

Bây giờ bạn biết nó được thực hiện như thế nào, điều gì đang xảy ra ở cấp độ thấp và tại sao nó hoạt động.


8
Vì thiếu dấu ngoặc nhọn (ưu tiên toán tử) mã này không biên dịch được, nó phải là var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk

54

Mặc dù thật tốt khi đọc và hiểu câu trả lời của Josh, bạn có thể sẽ thấy vui hơn khi sử dụng lớp mà Microsoft cung cấp cho mục đích này: System.Collections.BitArray Nó có sẵn trong tất cả các phiên bản .NET Framework.


1
Điều này thật tuyệt vời nhưng tôi tin rằng giải pháp của Josh nhanh hơn và hiệu quả hơn nhiều.
Samuel Allan

1
@ user2332868: trình biên dịch JIT đặc biệt nhận dạng các lệnh gọi đến các hàm thư viện nhất định và tạo mã hiệu quả, nhưng tôi không biết liệu các hàm cụ thể này có được yêu thích hay không.
Ben Voigt

1
để chắc chắn rằng bạn có thể làm giống như Josh nhưng trong lắp ráp nội tuyến thuần túy. Nhưng đáng buồn là tôi chỉ lập trình trong hợp ngữ NASM và không biết về hợp ngữ C # biên dịch thành :(.
Samuel Allan

1
IMHO điều này giết chết hiệu suất cho một nhiệm vụ đơn giản như vậy. Sử dụng phương án điều hành Bitwise cũng cung cấp cho bạn một công cụ mà bạn có thể sử dụng nó trong ngôn ngữ gần như bất kỳ =)
Gaspa79

38

Điều này

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

nên làm điều đó, tôi nghĩ.


9

một cách khác để làm điều đó :)

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

1
Điều này sẽ không hoạt động: byte b = 1; ! return ((b >> 1) & 1) = 0 (nó tương đương với 0)
Rafael Diego Nicoletti

1
Hmmm ... Tôi không hiểu bạn. Nếu byte b = 1, bit ở vị trí 0 là 1, được cho bởi (b >> 0) & 1, và bit ở bất kỳ vị trí nào lớn hơn hoặc bằng 1 là 0, được cho bởi (b >> n) & 1 trong đó n> = 1 cũng như
PierrOz

Tôi không tin rằng mình làm thế nào cả @DavideAndrea, những bình luận được đăng bởi Rafael giả định phải nhất bit là bit 1, nhưng mã PierrOz là khi các bit đúng nhất là bit 0. Nếu b là 2, sau đó ((2 >> 1)&1)1((2 >> 0)&1)được 0vì 2 là00000010
Cameron Aavik


6

Sử dụng lớp BitArray và tạo một phương thức mở rộng như OP đề xuất:

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

9
Không, vui lòng không tạo và vứt bỏ BitArray cho mỗi lần kiểm tra bit.
Ben Voigt

@BenVoigt, đó là một phương thức mở rộng trên một byte cho mỗi yêu cầu OP. Bạn khuyên bạn nên lưu trữ phiên bản BitArray ở đâu?
Jay Walker

2
Bạn đẩy lùi yêu cầu và nói, đừng gọi nó như một phương thức trên byte, hãy gọi nó trên BitArray. Có thể biến byte hoàn toàn có thể biến mất.
Ben Voigt

Nếu bạn đang tìm kiếm bit thứ n của byte, bạn rất có thể đang tìm kiếm hiệu suất và điều này sẽ giết chết nó. Nếu không, bạn luôn có thể làm những việc không cần thiết khác như Convert.ToString (num, 2) [bitNumber] và cũng có được nó.
Gaspa79


3

Phương pháp là sử dụng một byte khác cùng với bitwise AND để che đi bit đích.

Tôi đã sử dụng quy ước từ các lớp của mình ở đây, trong đó "0" là bit quan trọng nhất và "7" là ít nhất.

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

Hãy thử mã bên dưới. Sự khác biệt với các bài viết khác là bạn có thể đặt / lấy nhiều bit bằng cách sử dụng mask ( field). Mặt nạ cho bit thứ 4 có thể là 1 << 3 hoặc 0x10, chẳng hạn.

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

** Thí dụ **

        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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.