รับบิตเฉพาะจากไบต์


98

ฉันมีไบต์โดยเฉพาะหนึ่งไบต์จากอาร์เรย์ไบต์ที่เข้ามาผ่าน UDP ที่ส่งจากอุปกรณ์อื่น ไบต์นี้เก็บสถานะเปิด / ปิดของรีเลย์ 8 ตัวในอุปกรณ์

ฉันจะรับค่าของบิตเฉพาะในไบต์ดังกล่าวได้อย่างไร ตามหลักการแล้ววิธีการขยายจะดูสวยงามที่สุดและการคืนบูลจะเหมาะสมกับฉันมากที่สุด

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

คำตอบ:


182

ง่าย. ใช้บิตและเพื่อเปรียบเทียบตัวเลขของคุณกับค่า 2 ^ bitNumber ซึ่งสามารถคำนวณได้อย่างถูกต้องโดยการขยับบิต

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

แก้ไข:หากต้องการเพิ่มรายละเอียดอีกเล็กน้อยเนื่องจากมีคำตอบที่คล้ายกันมากมายโดยไม่มีคำอธิบาย:

บิตและเปรียบเทียบตัวเลขแต่ละตัวแบบทีละบิตโดยใช้ AND เพื่อสร้างตัวเลขที่รวมกันของบิตที่ทั้งบิตแรกและบิตที่สองในตำแหน่งนั้นถูกตั้งค่า นี่คือเมทริกซ์ลอจิกของตรรกะ AND ใน "ตอด" ที่แสดงการทำงานของบิต AND:

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

ในกรณีของคุณเราเปรียบเทียบหมายเลขที่คุณส่งผ่านกับตัวเลขที่มีเพียงบิตที่คุณต้องการค้นหา สมมติว่าคุณกำลังมองหาบิตที่สี่:

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

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

การขยับบิตเพื่อสร้างจำนวนที่เราต้องการเปรียบเทียบนั้นเป็นสิ่งที่ดูเหมือนกัน: ใช้ตัวเลขแสดงเป็นชุดของบิตและเลื่อนบิตเหล่านั้นไปทางซ้ายหรือขวาตามจำนวนที่แน่นอน เนื่องจากสิ่งเหล่านี้เป็นเลขฐานสองดังนั้นแต่ละบิตจึงมีกำลัง - ของสองมากกว่าหนึ่งทางด้านขวาการขยับบิตไปทางซ้ายจึงเท่ากับการเพิ่มจำนวนสองครั้งหนึ่งครั้งสำหรับแต่ละตำแหน่งที่เลื่อนซึ่งเทียบเท่ากับการคูณจำนวนด้วย 2 ^ x. ในตัวอย่างของคุณมองหาบิตที่สี่เราดำเนินการ:

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

ตอนนี้คุณรู้แล้วว่ามันเป็นอย่างไรเกิดอะไรขึ้นในระดับต่ำและทำไมถึงได้ผล


9
เพราะการจัดฟันที่หายไป (ความสำคัญประกอบ) รหัสนี้ไม่ได้รวบรวมนั้นจะต้องได้var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk

54

แม้ว่าจะเป็นการดีที่จะอ่านและทำความเข้าใจคำตอบของ Josh แต่คุณอาจมีความสุขมากขึ้นเมื่อใช้คลาสที่ Microsoft จัดเตรียมไว้เพื่อจุดประสงค์นี้: System.Collections.BitArray มีอยู่ใน. NET Framework ทุกเวอร์ชัน


1
นี่เป็นเรื่องเยี่ยม แต่ฉันเชื่อว่าโซลูชันของ Josh นั้นเร็วกว่าและมีประสิทธิภาพมากกว่า
Samuel Allan

1
@ user2332868: คอมไพเลอร์ JIT จดจำการโทรไปยังฟังก์ชันไลบรารีบางอย่างเป็นพิเศษและสร้างโค้ดที่มีประสิทธิภาพ แต่ฉันไม่รู้ว่าฟังก์ชันเฉพาะเหล่านี้จะได้รับความรักหรือไม่
Ben Voigt

1
เพื่อให้แน่ใจว่าคุณสามารถทำได้เช่นเดียวกับ Josh แต่ในการประกอบแบบอินไลน์ล้วน แต่น่าเศร้าที่ฉันตั้งโปรแกรมเฉพาะในแอสเซมบลี NASM และไม่รู้เกี่ยวกับแอสเซมบลี C # ที่รวบรวมไว้ใน :(
ซามูเอลอัลลัน

1
IMHO ฆ่าประสิทธิภาพสำหรับงานง่ายๆเช่นนี้ การใช้ตัวดำเนินการทางเลือกบิตยังช่วยให้คุณมีเครื่องมือที่คุณสามารถใช้ได้ในเกือบทุกภาษา =)
Gaspa79

38

นี้

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

ควรทำฉันคิดว่า


10

อีกวิธีในการทำ :)

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

1
สิ่งนี้จะไม่ทำงาน: byte b = 1; ผลตอบแทน ((b >> 1) & 1)! = 0 (มันเท่ากับ 0)
Rafael Diego Nicoletti

1
อืม ... ฉันไม่เข้าใจคุณ ถ้าไบต์ b = 1 บิตที่ตำแหน่ง 0 คือ 1 กำหนดโดย (b >> 0) & 1 และบิตที่ตำแหน่งใด ๆ ที่มากกว่าหรือเท่ากับ 1 เป็น 0 กำหนดโดย (b >> n) & 1 โดยที่ n> = 1 เช่นกัน
PierrOz

ฉันไม่เชื่อว่าเขาทำอย่างนั้นเลย @DavideAndrea ความคิดเห็นที่โพสต์โดย Rafael ถือว่าบิตที่ถูกต้องที่สุดคือบิต 1 แต่รหัสของ PierrOz มีไว้สำหรับเมื่อบิตที่ถูกต้องที่สุดคือบิต 0 ถ้า b เป็น 2 ก็((2 >> 1)&1)จะเป็น1และ((2 >> 0)&1)เป็น0เพราะ 2 คือ00000010
Cameron Aavik


7

ใช้คลาสBitArrayและสร้างวิธีการขยายตามที่ OP แนะนำ:

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

11
ไม่โปรดอย่าสร้างและทิ้ง BitArray สำหรับการทดสอบแต่ละบิต
Ben Voigt

@BenVoigt เป็นวิธีการขยายไบต์ต่อคำขอ OP คุณแนะนำให้จัดเก็บอินสแตนซ์ BitArray ไว้ที่ใด
Jay Walker

2
คุณผลักกลับคำขอและพูดว่าอย่าเรียกมันว่าเป็นวิธีการในไบต์เรียกมันบน BitArray บางทีตัวแปรไบต์อาจหายไปโดยสิ้นเชิง
Ben Voigt

หากคุณต้องการรับบิตที่ n คุณมักจะมองหาประสิทธิภาพและสิ่งนี้จะฆ่ามัน มิฉะนั้นคุณสามารถทำสิ่งที่ไม่จำเป็นอื่น ๆ เช่น Convert ToString (num, 2) [bitNumber] และรับได้เช่นกัน
Gaspa79


4

วิธีการคือการใช้ไบต์อื่นพร้อมกับบิตและเพื่อปกปิดบิตเป้าหมาย

ฉันใช้การประชุมจากชั้นเรียนของฉันที่นี่โดยที่ "0" เป็นบิตที่สำคัญที่สุดและ "7" เป็นค่าน้อยที่สุด

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

4

ลองใช้รหัสด้านล่าง ความแตกต่างกับโพสต์อื่น ๆ คือคุณสามารถตั้งค่า / รับหลายบิตโดยใช้มาสก์ ( field) มาสก์สำหรับบิตที่ 4 สามารถเป็น 1 << 3 หรือ 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;
    }

** ตัวอย่าง **

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

3
[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);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.