Làm cách nào để lấy dữ liệu từng bit từ một giá trị số nguyên trong C?


99

Tôi muốn trích xuất các bit của một số thập phân.

Ví dụ: 7 là nhị phân 0111 và tôi muốn nhận 0 1 1 1 tất cả các bit được lưu trữ trong bool. Làm thế nào tôi có thể làm như vậy?

OK, vòng lặp không phải là một lựa chọn tốt, tôi có thể làm gì khác cho việc này không?

Câu trả lời:


153

Nếu bạn muốn bit thứ k của n, thì làm

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

Ở đây chúng ta tạo một mặt nạ, áp dụng mặt nạ cho n, và sau đó dịch sang phải giá trị mặt nạ để chỉ nhận được bit chúng ta muốn. Chúng tôi có thể viết nó ra đầy đủ hơn như:

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

Bạn có thể đọc thêm về bit-mask tại đây .

Đây là một chương trình:

#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");
}

70
(n >> k) & 1có giá trị như nhau và không yêu cầu tính toán mặt nạ vì mặt nạ không đổi do dịch chuyển trước khi tạo mặt nạ thay vì ngược lại.
Joe

@Joe bạn có thể giải thích điều đó, có lẽ trong một câu trả lời, làm ơn?
Dan Rosenstark

1
@Yar đã mở rộng bình luận của tôi một chút và thêm một câu trả lời mới theo yêu cầu
Joe

Khi sử dụng các bit đã biết cho thông tin (tức là đối với các giao thức mạng, chẳng hạn như Websockets), truyền dữ liệu vào một structcũng có thể hữu ích, vì bạn nhận được tất cả dữ liệu cần thiết chỉ với một thao tác.
Myst

@forefinger, bạn có thể vui lòng đăng ví dụ đầu ra của mã.
Ashish Ahuja

83

Theo yêu cầu, tôi quyết định mở rộng bình luận của mình về câu trả lời của ngón trỏ thành một câu trả lời chính thức. Mặc dù câu trả lời của anh ấy là đúng, nhưng nó không cần thiết phải phức tạp. Hơn nữa, tất cả các câu trả lời hiện tại đều sử dụng dấu ints để đại diện cho các giá trị. Điều này rất nguy hiểm, vì dịch chuyển sang phải của các giá trị âm được xác định bởi việc triển khai (nghĩa là không di động) và dịch chuyển sang trái có thể dẫn đến hành vi không xác định (xem câu hỏi này ).

Bằng cách chuyển sang phải bit mong muốn sang vị trí bit ít quan trọng nhất, có thể thực hiện việc che dấu 1. Không cần tính giá trị mặt nạ mới cho mỗi bit.

(n >> k) & 1

Là một chương trình hoàn chỉnh, tính toán (và sau đó in) một mảng các giá trị bit đơn:

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

Giả sử rằng bạn muốn tính toán tất cả các bit như trong trường hợp này, chứ không phải một bit cụ thể, vòng lặp có thể được thay đổi thêm thành

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

Điều này sửa đổi inputtại chỗ và do đó cho phép sử dụng độ rộng không đổi, dịch chuyển bit đơn, có thể hiệu quả hơn trên một số kiến ​​trúc.


5

Đây là một cách để làm điều đó — còn nhiều cách khác:

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

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

Thật khó hiểu tại sao việc sử dụng vòng lặp lại không được mong muốn, nhưng có thể dễ dàng gỡ bỏ vòng lặp:

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

Hoặc đánh giá các biểu thức hằng trong bốn câu lệnh cuối cùng:

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

3

Đây là một cách rất đơn giản để làm điều đó;

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 cảm ơn bạn đã giúp đỡ. Tôi đã viết lại hàm với các chú thích để sử dụng trong một chương trình. Tăng 8 cho nhiều bit hơn (lên đến 32 cho một số nguyên).

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

Nếu bạn không muốn bất kỳ vòng lặp nào, bạn sẽ phải viết nó ra:

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

Như đã trình bày ở đây, điều này cũng hoạt động trong trình khởi tạo.


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

Sử dụng std::bitset

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

Đó là một phương pháp hữu ích, nhưng có điều gì đó sai trong ví dụ. bitset <n>, n là số bit, vì vậy việc sử dụng sizeof (int) là sai.
Jerry Chou
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.