2013-10-03 21 views
5

Tôi có một số vector<char> và tôi muốn có thể lấy số nguyên không dấu từ một dải bit trong vectơ. Ví dụ.Nhận số nguyên từ bit bên trong `std :: vector <char>`

visualisation of bitvalues

Và tôi dường như không thể để có thể viết các thao tác đúng để có được những kết quả mong muốn. Thuật toán định của tôi đi như thế này:

  • & byte đầu tiên với (0xff >> unused bits in byte on the left)
  • << kết quả trái số byte đầu ra * số bit trong một byte
  • | này với sản lượng thức
  • Đối với mỗi byte tiếp theo:
    • << còn lại bởi (byte width - index) * bits per byte
    • | byte này với sản lượng thức
  • | byte cuối cùng (không chuyển) với sản lượng thức
  • >> đầu ra cuối cùng của số bit không sử dụng trong các byte trên quyền

Và đây là nỗ lực của tôi trong việc mã hóa nó, không cung cấp kết quả chính xác:

#include <vector> 
#include <iostream> 
#include <cstdint> 
#include <bitset> 

template<class byte_type = char> 
class BitValues { 
    private: 
    std::vector<byte_type> bytes; 
    public: 
     static const auto bits_per_byte = 8; 
     BitValues(std::vector<byte_type> bytes) : bytes(bytes) { 
     } 
     template<class return_type> 
     return_type get_bits(int start, int end) { 
      auto byte_start = (start - (start % bits_per_byte))/bits_per_byte; 
      auto byte_end = (end - (end % bits_per_byte))/bits_per_byte; 
      auto byte_width = byte_end - byte_start; 
      return_type value = 0; 

      unsigned char first = bytes[byte_start]; 
      first &= (0xff >> start % 8); 
      return_type first_wide = first; 
      first_wide <<= byte_width; 
      value |= first_wide; 

      for(auto byte_i = byte_start + 1; byte_i <= byte_end; byte_i++) { 
       auto byte_offset = (byte_width - byte_i) * bits_per_byte; 
       unsigned char next_thin = bytes[byte_i]; 
       return_type next_byte = next_thin; 
       next_byte <<= byte_offset; 
       value |= next_byte; 
      } 
      value >>= (((byte_end + 1) * bits_per_byte) - end) % bits_per_byte; 

      return value; 
     } 
}; 

int main() { 
    BitValues<char> bits(std::vector<char>({'\x78', '\xDA', '\x05', '\x5F', '\x8A', '\xF1', '\x0F', '\xA0'})); 
    std::cout << bits.get_bits<unsigned>(15, 29) << "\n"; 
    return 0; 
} 

(Đang hoạt động: http://coliru.stacked-crooked.com/a/261d32875fcf2dc0)

Tôi dường như không thể quấn đầu xung quanh các thao tác bit này, và tôi thấy việc gỡ lỗi rất khó! Nếu bất cứ ai có thể sửa mã trên, hoặc giúp tôi bằng bất kỳ cách nào, nó sẽ được nhiều người đánh giá cao!

Edit:

  • byte của tôi là 8 bit dài
  • Các nguyên trở lại có thể là 8,16,32 hoặc 64 bit wside
  • Các số nguyên được lưu trữ trong big endian

Trả lời

1

Bạn đã tạo hai lỗi chính. Đầu tiên là ở đây:

first_wide <<= byte_width; 

Bạn phải dịch theo số lượng bit, chứ không phải số byte. đang sửa lại là:

first_wide <<= byte_width * bits_per_byte; 

Sai lầm thứ hai là ở đây:

auto byte_offset = (byte_width - byte_i) * bits_per_byte; 

Nó phải là

auto byte_offset = (byte_end - byte_i) * bits_per_byte; 

Giá trị trong ngoặc đơn cần phải được số byte để thay đổi quyền bởi, đó cũng là số byte byte_i cách xa kết thúc. Giá trị byte_width - byte_i không có ý nghĩa ngữ nghĩa (một là đồng bằng, giá trị kia là chỉ mục)

Phần còn lại của mã là tốt. Mặc dù, thuật toán này có hai vấn đề với nó.

Trước tiên, khi sử dụng loại kết quả của bạn để tích lũy bit, bạn cho rằng bạn có chỗ ở bên trái để dự phòng. Đây không phải là trường hợp nếu có bit thiết lập gần ranh giới phải và sự lựa chọn phạm vi gây ra các bit được chuyển ra ngoài. Ví dụ, hãy thử chạy

bits.get_bits<uint16_t>(11, 27); 

Bạn sẽ nhận được kết quả 42 tương ứng với chuỗi chút 00000000 00101010 Kết quả đúng là 53.290 với chuỗi chút 11010000 00101010. Chú ý cách 4 bit ngoài cùng bên phải đã bị xóa.Điều này là do bạn bắt đầu bằng cách vượt qua biến số value của bạn, khiến bốn bit đó bị dịch chuyển khỏi biến. Khi chuyển trở lại vào cuối, kết quả này trong các bit được zeroed out.

Vấn đề thứ hai phải làm với sự dịch chuyển đúng ở cuối. Nếu bit ngoài cùng bên phải của biến số value xảy ra là 1 trước khi dịch chuyển phải ở cuối và thông số mẫu là loại đã ký, thì thay đổi đúng được thực hiện là sự dịch chuyển đúng 'số học', làm cho các bit trên quyền được điền 1 lần, để lại cho bạn giá trị âm không chính xác.

Ví dụ, hãy thử chạy:

bits.get_bits<int16_t>(5, 21); 

Kết quả dự kiến ​​nên 6976 với chuỗi chút 00011011 01000000, nhưng việc thực hiện hiện trả -1216 với chuỗi chút 11111011 01000000.

Tôi đã đặt thực hiện của tôi dưới đây mà xây dựng các chuỗi bit từ bên phải sang bên trái, đặt bit ở các vị trí chính xác của họ bắt đầu với vì vậy mà hai vấn đề trên đều tránh:

template<class ReturnType> 
ReturnType get_bits(int start, int end) { 
    int max_bits = kBitsPerByte * sizeof(ReturnType); 
    if (end - start > max_bits) { 
    start = end - max_bits; 
    } 

    int inclusive_end = end - 1; 
    int byte_start = start/kBitsPerByte; 
    int byte_end = inclusive_end/kBitsPerByte; 

    // Put in the partial-byte on the right 
    uint8_t first = bytes_[byte_end]; 
    int bit_offset = (inclusive_end % kBitsPerByte); 
    first >>= 7 - bit_offset; 
    bit_offset += 1; 
    ReturnType ret = 0 | first; 

    // Add the rest of the bytes 
    for (int i = byte_end - 1; i >= byte_start; i--) { 
    ReturnType tmp = (uint8_t) bytes_[i]; 
    tmp <<= bit_offset; 
    ret |= tmp; 
    bit_offset += kBitsPerByte; 
    } 

    // Mask out the partial byte on the left 
    int shift_amt = (end - start); 
    if (shift_amt < max_bits) { 
    ReturnType mask = (1 << shift_amt) - 1; 
    ret &= mask; 
    } 
} 
+0

này hoạt động tuyệt vời cho các số nguyên không dấu cảm ơn bạn! Tôi chỉ ở phút điều tra các số nguyên đã ký - Tôi không * hoàn toàn * chắc chắn rằng kết quả mong muốn của tôi cho 'get_bits (14, 22)' là vào phút! Tôi sẽ sớm quay lại với một bản cập nhật về điều đó, hoặc nếu tôi thấy đây là hành vi mong muốn, một dấu chọn cho bạn :) – Ell

+0

Dường như mã này không hoạt động đối với 'bits.get_bits (0, 32) ; '- nó trả về không thay vì mong đợi' 519053860746' – Ell

+0

Bạn nói đúng. Lỗi này là do cách kết quả được che dấu ở cuối. Việc dịch chuyển trái di chuyển bit ra khỏi tầm quan trọng gây ra một bitmask của 0. Tôi đã thêm một sửa chữa. – Cookyt

0

Sự cố thú vị. Tôi đã thực hiện tương tự, đối với một số hệ thống hoạt động.

  • Char của bạn rộng 8 bit? Hoặc 16? Làm thế nào lớn là số nguyên của bạn? 32 hoặc 64?
  • Bỏ qua sự phức tạp của véc tơ trong một phút.
  • Hãy nghĩ về nó như một mảng bit.
  • Bạn có bao nhiêu bit? Bạn có 8 * số ký tự
  • Bạn cần tính toán số bắt đầu, số bit để trích xuất, kết thúc char, số bit ở đó và số ký tự ở giữa.
  • Bạn sẽ cần Bitwise-và & cho char phần đầu tiên
  • bạn sẽ cần Bitwise-và & cho char phần cuối cùng
  • bạn sẽ cần trái ca < < (hoặc phải ca >>), tùy thuộc vào thứ tự nào bạn bắt đầu từ
  • số cuối của số nguyên là gì?

Tại một số điểm bạn sẽ tính toán một chỉ số vào mảng của bạn đó là bitindex/char_bit_width, bạn đã cho giá trị 171 như bitindex của bạn, và 8 như char_bit_width của bạn, vì vậy bạn sẽ kết thúc với những giá trị hữu ích tính:

  • 171/8 = 23 // vị trí của byte đầu tiên
  • 171% 8 = 3 // bit trong đầu char/byte
  • 8-171% 8 = 5 // bit trong char cuối cùng/byte
  • sizeof (số nguyên) = 4
  • sizeof (số nguyên) + ((171% 8)> 0 1: 0) // có bao nhiêu vị trí mảng để kiểm tra

Một số lắp ráp yêu cầu ...

0

Có một điều bạn chắc chắn Tôi nhớ: cách bạn lập chỉ mục các bit trong vectơ khác với những gì bạn đã đưa ra trong bài toán. I E. với thuật toán bạn đã phác thảo, thứ tự của các bit sẽ giống như 7 6 5 4 3 2 1 0 | 15 14 13 12 11 10 9 8 | 23 22 21 .... Thành thật mà nói, tôi đã không đọc qua toàn bộ thuật toán của bạn, nhưng điều này đã bị bỏ lỡ trong bước đầu tiên.

Các vấn đề liên quan