ฉันจะรับข้อมูลบิตต่อบิตจากค่าจำนวนเต็มใน C ได้อย่างไร


102

ฉันต้องการแยกบิตของตัวเลขทศนิยม

ตัวอย่างเช่น 7 คือไบนารี 0111 และฉันต้องการรับ 0 1 1 1 บิตทั้งหมดที่เก็บไว้ในบูล ฉันจะทำเช่นนั้นได้อย่างไร?

ตกลงการวนซ้ำไม่ใช่ตัวเลือกที่ดีฉันจะทำอย่างอื่นได้ไหม

คำตอบ:


159

ถ้าคุณต้องการบิต k ของ n ให้ทำ

(n & ( 1 << k )) >> k

ที่นี่เราสร้างมาสก์ใช้มาสก์กับ n แล้วเลื่อนค่ามาสก์ไปทางขวาเพื่อให้ได้บิตที่เราต้องการ เราสามารถเขียนมันออกมาได้ครบถ้วนมากขึ้นเป็น:

    int mask =  1 << k;
    int masked_n = n & mask;
    int thebit = masked_n >> k;

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับบิตกำบังที่นี่

นี่คือโปรแกรม:

#include <stdio.h>
#include <stdlib.h>

int *get_bits(int n, int bitswanted){
  int *bits = malloc(sizeof(int) * bitswanted);

  int k;
  for(k=0; k<bitswanted; k++){
    int mask =  1 << k;
    int masked_n = n & mask;
    int thebit = masked_n >> k;
    bits[k] = thebit;
  }

  return bits;
}

int main(){
  int n=7;

  int  bitswanted = 5;

  int *bits = get_bits(n, bitswanted);

  printf("%d = ", n);

  int i;
  for(i=bitswanted-1; i>=0;i--){
    printf("%d ", bits[i]);
  }

  printf("\n");
}

72
(n >> k) & 1มีความถูกต้องเท่าเทียมกันและไม่ต้องใช้การคำนวณมาสก์เนื่องจากมาสก์มีค่าคงที่เนื่องจากการขยับก่อนการมาสก์แทนที่จะใช้วิธีอื่น
โจ

@ โจคุณช่วยอธิบายได้ไหมบางทีเป็นคำตอบได้ไหม?
Dan Rosenstark

1
@Yar ขยายความคิดเห็นของฉันเล็กน้อยและเพิ่มคำตอบใหม่ตามที่ร้องขอ
โจ

เมื่อใช้บิตที่รู้จักสำหรับข้อมูล (เช่นสำหรับโปรโตคอลเครือข่ายเช่น Websockets) การแคสต์ข้อมูลลงใน a structก็มีประโยชน์เช่นกันเนื่องจากคุณได้รับข้อมูลที่ต้องการทั้งหมดด้วยการดำเนินการเพียงครั้งเดียว
Myst

@forefinger คุณช่วยโพสต์ตัวอย่างของโค้ดได้ไหม
Ashish Ahuja

87

ตามที่ร้องขอฉันตัดสินใจที่จะขยายความคิดเห็นของฉันเกี่ยวกับคำตอบของนิ้วชี้เพื่อเป็นคำตอบที่ครบถ้วนสมบูรณ์ แม้ว่าคำตอบของเขาจะถูกต้อง แต่ก็ซับซ้อนโดยไม่จำเป็น นอกจากนี้คำตอบปัจจุบันทั้งหมดยังใช้intเครื่องหมาย s เพื่อแสดงค่า สิ่งนี้เป็นอันตรายเนื่องจากการเปลี่ยนค่าเชิงลบไปทางขวาถูกกำหนดให้ใช้งานได้ (เช่นไม่สามารถพกพาได้) และการเลื่อนไปทางซ้ายอาจนำไปสู่พฤติกรรมที่ไม่ได้กำหนด (ดูคำถามนี้ )

1โดยทางด้านขวาขยับบิตที่ต้องการเข้ามาในตำแหน่งบิตอย่างมีนัยสำคัญน้อยกำบังสามารถทำได้ด้วย ไม่จำเป็นต้องคำนวณค่ามาสก์ใหม่สำหรับแต่ละบิต

(n >> k) & 1

ในฐานะโปรแกรมที่สมบูรณ์การคำนวณ (และการพิมพ์ในภายหลัง) อาร์เรย์ของค่าบิตเดียว:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    unsigned
        input = 0b0111u,
        n_bits = 4u,
        *bits = (unsigned*)malloc(sizeof(unsigned) * n_bits),
        bit = 0;

    for(bit = 0; bit < n_bits; ++bit)
        bits[bit] = (input >> bit) & 1;

    for(bit = n_bits; bit--;)
        printf("%u", bits[bit]);
    printf("\n");

    free(bits);
}

สมมติว่าคุณต้องการคำนวณบิตทั้งหมดเช่นในกรณีนี้และไม่ใช่เฉพาะการวนซ้ำสามารถเปลี่ยนเป็น

for(bit = 0; bit < n_bits; ++bit, input >>= 1)
    bits[bit] = input & 1;

สิ่งนี้ปรับเปลี่ยนinputในสถานที่และด้วยเหตุนี้จึงช่วยให้สามารถใช้ความกว้างคงที่กะบิตเดียวซึ่งอาจมีประสิทธิภาพมากกว่าในบางสถาปัตยกรรม


5

นี่เป็นวิธีหนึ่งที่ทำได้ - มีวิธีอื่น ๆ อีกมากมาย:

bool b[4];
int v = 7;  // number to dissect

for (int j = 0;  j < 4;  ++j)
   b [j] =  0 != (v & (1 << j));

ยากที่จะเข้าใจว่าเหตุใดจึงไม่ต้องการใช้ลูป แต่มันง่ายพอที่จะคลายการวนซ้ำ:

bool b[4];
int v = 7;  // number to dissect

b [0] =  0 != (v & (1 << 0));
b [1] =  0 != (v & (1 << 1));
b [2] =  0 != (v & (1 << 2));
b [3] =  0 != (v & (1 << 3));

หรือการประเมินนิพจน์คงที่ในสี่คำสั่งสุดท้าย:

b [0] =  0 != (v & 1);
b [1] =  0 != (v & 2);
b [2] =  0 != (v & 4);
b [3] =  0 != (v & 8);

3

นี่เป็นวิธีที่ง่ายมากที่จะทำ

int main()
{
    int s=7,l=1;
    vector <bool> v;
    v.clear();
    while (l <= 4)
    {
        v.push_back(s%2);
        s /= 2;
        l++;
    }
    for (l=(v.size()-1); l >= 0; l--)
    {
        cout<<v[l]<<" ";
    }
    return 0;
}

1

@prateek ขอบคุณสำหรับความช่วยเหลือ ฉันเขียนฟังก์ชันใหม่พร้อมความคิดเห็นเพื่อใช้ในโปรแกรม เพิ่ม 8 สำหรับบิตเพิ่มเติม (สูงสุด 32 สำหรับจำนวนเต็ม)

std::vector <bool> bits_from_int (int integer)    // discern which bits of PLC codes are true
{
    std::vector <bool> bool_bits;

    // continously divide the integer by 2, if there is no remainder, the bit is 1, else it's 0
    for (int i = 0; i < 8; i++)
    {
        bool_bits.push_back (integer%2);    // remainder of dividing by 2
        integer /= 2;    // integer equals itself divided by 2
    }

    return bool_bits;
}

1

หากคุณไม่ต้องการวนซ้ำคุณจะต้องเขียนมันออกมา:

#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    int num = 7;

    #if 0
        bool arr[4] = { (num&1) ?true: false, (num&2) ?true: false, (num&4) ?true: false, (num&8) ?true: false };
    #else
        #define BTB(v,i) ((v) & (1u << (i))) ? true : false
        bool arr[4] = { BTB(num,0), BTB(num,1), BTB(num,2), BTB(num,3)};
        #undef BTB
    #endif

    printf("%d %d %d %d\n", arr[3], arr[2], arr[1], arr[0]);

    return 0;
}

ดังที่แสดงไว้ที่นี่สิ่งนี้ยังใช้งานได้ในตัวเริ่มต้น


0
#include <stdio.h>

int main(void)
{
    int number = 7; /* signed */
    int vbool[8 * sizeof(int)];
    int i;
        for (i = 0; i < 8 * sizeof(int); i++)
        {
            vbool[i] = number<<i < 0;   
            printf("%d", vbool[i]);
        }
    return 0;
}

0

การใช้ std::bitset

int value = 123;
std::bitset<sizeof(int)> bits(value);
std::cout <<bits.to_string();

เป็นวิธีที่มีประโยชน์ แต่มีบางอย่างผิดปกติในตัวอย่าง bitset <n>, n คือจำนวนบิตดังนั้นการใช้ sizeof (int) จึงไม่ถูกต้อง
Jerry Chou
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.