Tên để lưu trữ / đóng gói nhiều trạng thái boolean thành một số là gì?


55

Đây là một kiểu nén đơn giản trong đó bạn sử dụng một biến số để lưu trữ nhiều trạng thái nhị phân / nhị phân, sử dụng nhân đôi và thực tế là mỗi số nhân đôi là 1 + tổng của tất cả các trạng thái trước đó.

Tôi chắc chắn đó phải là một kỹ thuật cũ, nổi tiếng, tôi muốn biết nó được gọi là gì để tham khảo nó đúng cách. Tôi đã thực hiện một số tìm kiếm trên mọi cách tôi có thể nghĩ ra để mô tả nó, nhưng không tìm thấy gì ngoài một số bài viết trên blog mà các tác giả bài viết dường như đã tự tìm ra điều này và không biết nên gọi nó là gì ( ví dụ 1 , ví dụ 2 ).

Ví dụ, đây là một triển khai rất đơn giản nhằm minh họa khái niệm:

packStatesIntoNumber () {
  let num = 0
  if (this.stateA) num += 1
  if (this.stateB) num += 2
  if (this.stateC) num += 4
  if (this.stateD) num += 8
  if (this.stateE) num += 16
  if (this.stateF) num += 32
  return num
}

unpackStatesFromNumber (num) {
  assert(num < 64)
  this.stateF = num >= 32; if (this.stateF) num -= 32
  this.stateE = num >= 16; if (this.stateE) num -= 16
  this.stateD = num >= 8; if (this.stateD) num -= 8
  this.stateC = num >= 4; if (this.stateC) num -= 4
  this.stateB = num >= 2; if (this.stateB) num -= 2
  this.stateA = num >= 1; if (this.stateA) num -= 1
}

Bạn cũng có thể sử dụng các toán tử bitwise, phân tích cú pháp số 2 cơ bản, enums ... Có nhiều cách hiệu quả hơn để thực hiện nó, tôi quan tâm đến tên của cách tiếp cận nói chung hơn.


8
Trong C #, có enums, và chúng có thể có một Flagsthuộc tính. Họ có thể làm cho mã của bạn đơn giản hơn nhiều.
Bernhard Hiller

12
Tôi sẽ gọi đây là "mô phỏng các trường bit". Nó gần như luôn luôn là một ý tưởng tồi trừ khi hiệu quả không gian là cực kỳ quan trọng.
Kilian Foth

7
@KilianFoth A boolthường được lưu trữ dưới dạng số nguyên 32 bit bên trong. Như vậy, việc đóng gói có thể tạo ra sự khác biệt của hệ số 32. Điều đó thực sự rất nhiều. Ý tôi là, các lập trình viên của chúng tôi luôn sẵn sàng vứt bỏ một nửa tài nguyên của chúng tôi, nhưng tôi thường miễn cưỡng vứt bỏ 97% trong số đó. Các yếu tố lãng phí như vậy có thể dễ dàng tạo ra sự khác biệt giữa việc có thể chạy các trường hợp sử dụng quan trọng và hết bộ nhớ.
cmaster

3
Trong lịch sử, cách mặt nạ bit thông thường được sử dụng để khai báo, đặt và truy xuất giá trị. Sử dụng ca là kỳ quặc và không thực sự là minh họa tốt nhất của phương pháp này.
JimmyJames

3
@cmaster Lý do các bool được lưu trữ theo cách đó là vì chia sẻ một vị trí bộ nhớ duy nhất (32 hoặc 64 bit trên các máy ngày nay) có thể rất tệ cho hiệu năng bộ đệm trừ khi bạn chú ý nhiều đến mã ngôn ngữ máy. Nếu bạn có số lượng bit thực sự lớn thì có lẽ nó đáng giá, nhưng nếu không, tốt hơn hết là bạn không nên tối ưu hóa trước và chỉ đóng gói các bit khi bạn sẵn sàng truyền vào mạng hoặc đĩa.
Bill K

Câu trả lời:


107

Nó thường được gọi là trường bit và một thuật ngữ khác bạn thường nghe là mặt nạ bit , được sử dụng để nhận hoặc đặt các giá trị bit riêng lẻ hoặc toàn bộ trường bit cùng một lúc.

Nhiều ngôn ngữ lập trình có cấu trúc phụ trợ để giúp với điều này. Như @BernhardHiller ghi chú trong các bình luận, C # có enum với cờ ; Java có lớp EnumSet .


4
Tôi sẽ hiểu "trường bit" là sử dụng một tính năng ngôn ngữ cho phép các bit riêng lẻ được gán cho các trường của một cấu trúc thay vì thực hiện thủ công với các toán tử bitwise.
Peter Green

22
@PeterGreen Điều đó sẽ khác với cách giải thích tiêu chuẩn.
Eric

1
"Ánh xạ bit" hoặc "Bit đã ánh xạ", trong khi phổ biến cho các bản ghi và xử lý mảng, cũng có thể được áp dụng trong trường hợp này. Khi trích xuất các phần tử phổ biến từ nhiều bộ, giá trị có thể được phân tách để xác định các thành phần của mô hình được liên kết. Chúng tôi thậm chí nói điều này của các chữ số bát phân bát phân. Mặt nạ bit (bất kỳ mặt nạ nào) có xu hướng là các bộ lọc (như đối với các cổng IO và các thanh ghi hướng dữ liệu).
mckenzm

1
C # cũng có BitArray, cho phép lưu trữ một lượng bit tùy ý và lập chỉ mục cho chúng (trong khi các cờ được giới hạn ở một kiểu số nguyên và được sử dụng làm mặt nạ).
Luaan

Thật; Tôi chỉ đề cập đến hai cấu trúc tôi quen thuộc nhất. Có lẽ có hàng tá ngoài kia, đặc biệt là trong các ngôn ngữ khác.
Glorfindel

20

Thật kỳ lạ, khá nhiều thuật ngữ khác nhau ở đây nhưng tôi không thấy từ nào xuất hiện ngay lập tức (và nó có trong tiêu đề câu hỏi của bạn!) - Bit Đóng gói là những gì tôi luôn nghe thấy nó.

Tôi đã nghĩ rằng điều này thực sự rõ ràng nhưng thật kỳ lạ khi tôi google nó dường như là một thuật ngữ được sử dụng rộng rãi nhưng không được xác định chính thức (Wikipedia dường như chuyển hướng đến trường bit là một cách để đóng gói bit, nhưng không phải là một tên cho quá trình). Tìm kiếm định nghĩa dường như dẫn đến trang này:

http://www.kinapesoup.com/news/2016/9/6/data-compression-bit-packing-101

Điều này không tốt cho mục đích SO nhưng đó là định nghĩa / mô tả tốt nhất tôi có thể tìm thấy bao gồm mô tả ngắn gọn này: "Đóng gói bit là một khái niệm đơn giản: Sử dụng càng ít bit càng tốt để lưu trữ một phần dữ liệu."


Bạn có thể cung cấp một số tài liệu tham khảo? Thuật ngữ thú vị.
Greg Burghardt

13
Việc đóng gói bit là đúng về mặt kỹ thuật nhưng cũng đề cập đến một điều tổng quát hơn là chỉ các trạng thái boolean - lưu trữ dữ liệu nói chung với số lượng bit nhỏ nhất có thể. Ví dụ, một cách sử dụng khác của nó có thể có nghĩa là nén một charmảng bằng cách đặt hai chars thành một int.
Izkata

@GregBurghardt Bạn biết đấy, thật thú vị. Tôi đã không nghĩ về nó khi tôi đăng bởi vì thuật ngữ này rất phổ biến vào những năm 80/90 khi tôi học lập trình bằng C và lắp ráp - bây giờ mặc dù một tìm kiếm google tìm thấy NHIỀU đề cập, không có trang Wikipedia rõ ràng cho nó . Câu trả lời đầu tiên trong google có định nghĩa này: "Đóng gói bit là một khái niệm đơn giản: Sử dụng càng ít bit càng tốt để lưu trữ một phần dữ liệu." kinapesoup.com/news/2016/9/6/ từ
Bill K

đó là khi tôi đã học về cách đóng gói bit, mặc dù bạn có thể nhận được nhiều thứ điên rồ hơn là chỉ đơn giản là sử dụng lại 0 không sử dụng trong giá trị thường là giá trị nguyên. Vài năm trước, tôi đã chạy vào một hệ thống lưu trữ một trong các tham số của nó dưới dạng float 8 bit. IIRC 5 bit cho một mantissa không dấu (tất cả các giá trị là dương không cần lưu trữ dấu hiệu rõ ràng) và 3 bit nữa cho số mũ 10 cơ sở. Vào thời điểm tôi cho rằng đó là một phần cứng kế thừa không có con đường phía trước, nhưng với việc học máy gần đây đã bắt đầu làm việc với int4 so với int8, tôi có thể thấy một số khối lượng công việc giảm xuống từ FP16.
Dan Neely

1
@DanNeely Loại điều này cũng thường được GPU hỗ trợ - giao dịch giữa độ chính xác, bộ nhớ và tính toán là khá quan trọng ở đó. Điều này đã được khai thác khá tốt với tính toán dựa trên GPU.
Luaan

14

Có nhiều thuật ngữ khác nhau được sử dụng để mô tả điều này.

Thông thường nhất các bit được gọi là "cờ bit" hoặc "trường bit".
(Tuy nhiên, đáng chú ý là "các trường bit" đôi khi đề cập đến một tính năng cụ thể của ngôn ngữ C và C ++, có liên quan nhưng không hoàn toàn giống nhau.)

Bản thân số nguyên được gọi khác nhau là "mảng bit", "tập bit" hoặc "vectơ bit", tùy thuộc vào cách sử dụng và hoàn cảnh.

Dù bằng cách nào, việc trích xuất các bit từ tập bit / vector / mảng được thực hiện thông qua dịch chuyển và mặt nạ.
(tức là sử dụng mặt nạ bit .)


Đối với một số ví dụ về mỗi thuật ngữ đang sử dụng:

  • Bài viết của Wikipedia về chủ đề này có tiêu đề Mảng bit , lưu ý rằng nó "còn được gọi là bit map, tập bit, chuỗi bit hoặc vectơ bit"
  • C ++ sử dụng std::bitset
  • Sử dụng Java BitSet
  • C # sử dụng BitArray
  • StackOverflow có các thẻ bitvector, bitarraybitset
  • Trên PyPi có một bitarraydự án và một BitVectordự án

Nó không thực sự phù hợp với câu hỏi nhưng tôi muốn nói: vui lòng không sử dụng phép cộng và phép trừ để đặt và xóa các bit vì các phương thức đó dễ bị lỗi.
(tức là nếu bạn làm num += 1hai lần, kết quả tương đương với num += 2.)

Thay vào đó, thích sử dụng các thao tác bit thích hợp hơn, nếu ngôn ngữ bạn chọn cung cấp cho chúng:

packStatesIntoNumber ()
{
  let num = 0
  if (this.stateA) num |= 1
  if (this.stateB) num |= 2
  if (this.stateC) num |= 4
  if (this.stateD) num |= 8
  if (this.stateE) num |= 16
  if (this.stateF) num |= 32
  return num
}

unpackStatesFromNumber (num)
{
  this.stateF = ((num & 32) != 0);
  this.stateE = ((num & 16) != 0);
  this.stateD = ((num & 8) != 0);
  this.stateC = ((num & 4) != 0);
  this.stateB = ((num & 2) != 0);
  this.stateA = ((num & 1) != 0);
}

1
this.stateF = (num & 32) ? true : false, v.v. Không cần phải thay đổi numtrong khi bạn trích xuất các giá trị.
Roger Lipscombe

3
@RogerLipscombe Điểm hay, tôi đã không thực sự đọc qua những gì mã đang làm, chỉ phản ứng với việc sử dụng +-. Bây giờ tôi đã đi tốt hơn và được sử dụng != 0thay vì một con chim nhạn, mà tôi cảm thấy ngắn gọn hơn trong khi vẫn còn được tiết lộ.
Pharap
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.