ชื่อสำหรับการจัดเก็บ / การบรรจุสถานะบูลีนจำนวนมากเป็นหนึ่งหมายเลขคืออะไร?


55

เป็นการบีบอัดอย่างง่ายที่คุณใช้ตัวแปรตัวเลขหนึ่งตัวเพื่อเก็บสถานะบูลีน / ไบนารีจำนวนมากโดยใช้การเสแสร้งและความจริงที่ว่าทุกการเพิ่มทวีคูณเป็น 1 + ผลรวมของก่อนหน้านี้ทั้งหมด

ฉันแน่ใจว่ามันต้องเป็นเทคนิคที่เก่าแก่และเป็นที่รู้จักกันดี ผมได้ดำเนินการค้นหาหลายวิธีที่ฉันสามารถคิดที่จะอธิบายมันทุกครั้ง แต่พบว่าไม่มีอะไรเกินกว่าบางบทความบล็อกที่เขียนบทความดูเหมือนจะได้คิดว่าตัวเองนี้และไม่ทราบว่าจะเรียกมันว่าอย่างใดอย่างหนึ่ง ( เช่น 1 , ตัวอย่างที่ 2 )

ตัวอย่างเช่นนี่เป็นการใช้งานที่ง่ายมากซึ่งมีจุดประสงค์เพื่อแสดงแนวคิด:

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
}

คุณยังสามารถใช้ตัวดำเนินการระดับบิตการแยกหมายเลข 2 ฐาน enums ... มีหลายวิธีที่มีประสิทธิภาพมากขึ้นในการใช้งานฉันสนใจในชื่อของวิธีการทั่วไปมากขึ้น


8
ใน C # มีenumsและพวกเขาสามารถมีFlagsคุณลักษณะ พวกเขาสามารถทำให้โค้ดของคุณง่ายยิ่งขึ้น
Bernhard Hiller

12
ฉันจะเรียกสิ่งนี้ว่า "การจำลองฟิลด์บิต" มันเป็นความคิดที่ไม่ดีเกือบทุกครั้งเว้นแต่ว่าประสิทธิภาพของพื้นที่นั้นสำคัญ
Kilian Foth

7
@KilianFoth A boolโดยทั่วไปจะถูกเก็บเป็นจำนวนเต็ม 32 บิตภายใน ดังนั้นการบรรจุหีบห่อสามารถสร้างความแตกต่างของตัวประกอบ 32 ซึ่งเป็นจำนวนมากจริงๆ ฉันหมายความว่าโปรแกรมเมอร์ของเราพร้อมเสมอที่จะละทิ้งทรัพยากรของเราครึ่งหนึ่ง แต่โดยทั่วไปฉันลังเลที่จะทิ้ง 97% ของพวกเขา ปัจจัยเสียดังกล่าวสามารถสร้างความแตกต่างระหว่างความสามารถในการเรียกใช้กรณีการใช้งานที่สำคัญและหน่วยความจำไม่เพียงพอ
cmaster

3
ในอดีตหน้ากากมาสก์ทางบิตโดยทั่วไปจะใช้ในการประกาศตั้งค่าและดึงค่า การใช้กะเป็นสิ่งที่แปลกและไม่ใช่ตัวอย่างที่ดีที่สุดของแนวทาง
JimmyJames

3
@cmaster เหตุผลที่บูลส์ถูกจัดเก็บในลักษณะนั้นเป็นเพราะการแชร์ตำแหน่งหน่วยความจำเดียว (32 หรือ 64 บิตบนเครื่องในปัจจุบัน) อาจไม่ดีสำหรับประสิทธิภาพของแคชเว้นแต่ว่าคุณจะสนใจรหัสภาษาของเครื่องมาก หากคุณมีจำนวนบิตขนาดใหญ่อย่างแท้จริงอาจเป็นสิ่งที่คุ้มค่า แต่ถ้าไม่ใช่คุณน่าจะดีกว่าที่จะไม่ทำการปรับแต่งล่วงหน้าและเพียงบรรจุบิตเมื่อคุณพร้อมที่จะส่งไปยังเครือข่ายหรือดิสก์
Bill K

คำตอบ:


107

โดยทั่วไปจะเรียกว่าฟิลด์บิตและอีกคำหนึ่งที่คุณมักได้ยินคือบิตมาสก์ซึ่งใช้เพื่อรับหรือตั้งค่าบิตแต่ละรายการหรือฟิลด์บิตทั้งหมดพร้อมกัน

ภาษาการเขียนโปรแกรมจำนวนมากมีโครงสร้างเสริมเพื่อช่วยในเรื่องนี้ ในฐานะที่เป็น @BernhardHiller บันทึกในความคิดเห็น C # มีenums กับธง ; Java มีคลาสEnumSet


4
ฉันจะตีความ "bit field" เป็นการใช้คุณสมบัติภาษาที่อนุญาตให้แต่ละบิตถูกกำหนดให้กับเขตข้อมูลของโครงสร้างแทนที่จะทำด้วยตนเองด้วยตัวดำเนินการระดับบิต
ปีเตอร์กรีน

22
@ PeterGreen นั้นจะแตกต่างจากการตีความมาตรฐาน
Eric

1
"บิตแมป" หรือ "บิตแมป" ในขณะที่ทั่วไปสำหรับชุดระเบียนและการประมวลผลอาร์เรย์สามารถใช้ในกรณีนี้ เมื่อแยกอิลิเมนต์ทั่วไปจากหลายชุดค่าสามารถถูกแบ่งแยกเพื่อระบุคอมโพเนนต์ของโมเดลรวม เรายังบอกว่านี่เป็นเลขฐานแปดหลัก บิตมาสก์ (มาสก์ใด ๆ ) มีแนวโน้มที่จะเป็นตัวกรอง (สำหรับพอร์ต IO และการลงทะเบียนทิศทางข้อมูล)
mckenzm

1
C # มีBitArrayเช่นกันซึ่งอนุญาตให้เก็บจำนวนบิตและจัดทำดัชนีโดยพลการ (ในขณะที่แฟล็ก จำกัด เฉพาะประเภทจำนวนเต็มและตั้งใจจะใช้เป็นมาสก์)
Luaan

ทรู; ฉันเพิ่งพูดถึงโครงสร้างทั้งสองที่ฉันคุ้นเคยมากที่สุด อาจมีหลายสิบโดยเฉพาะอย่างยิ่งในภาษาอื่น ๆ
Glorfindel

20

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

ฉันคิดว่านี่ชัดเจนจริงๆ แต่แปลกเมื่อฉัน google มันดูเหมือนว่าจะเป็นคำที่ใช้กันอย่างแพร่หลาย แต่ไม่ได้กำหนดอย่างเป็นทางการ (Wikipedia ดูเหมือนจะเปลี่ยนเส้นทางไปยังเขตข้อมูลบิตซึ่งเป็นวิธีการบรรจุบิต แต่ไม่ใช่ชื่อสำหรับ กระบวนการ). การค้นหาคำจำกัดความที่ดูเหมือนจะนำไปสู่หน้านี้:

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

ซึ่งไม่ดีสำหรับวัตถุประสงค์ SO แต่มันเป็นคำจำกัดความ / คำอธิบายที่ดีที่สุดที่ฉันสามารถหาได้รวมถึงคำอธิบายสั้น ๆ นี้: "การบรรจุบิตเป็นแนวคิดง่าย ๆ : ใช้บิตน้อยที่สุดในการเก็บชิ้นส่วนของข้อมูล"


คุณสามารถให้ข้อมูลอ้างอิงบางส่วนได้หรือไม่? ระยะเวลาที่น่าสนใจ
Greg Burghardt

13
การบรรจุบิตนั้นถูกต้องทางเทคนิค แต่ยังหมายถึงสิ่งทั่วไปมากกว่าเพียงแค่สถานะบูลีน - การจัดเก็บข้อมูลโดยทั่วไปในจำนวนบิตที่เล็กที่สุดเท่าที่จะเป็นไปได้ ยกตัวอย่างเช่นการใช้ประโยชน์จากมันอีกอาจหมายถึงการบีบอัดcharอาร์เรย์โดยการวางสองchars intเป็นหนึ่ง
Izkata

@ GregBurghardt คุณรู้ว่ามันน่าสนใจ ฉันไม่ได้คิดเกี่ยวกับมันเมื่อฉันโพสต์เพราะคำนั้นแพร่หลายมากในยุค 80/90 เมื่อฉันเรียนรู้การเขียนโปรแกรมใน C และแอสเซมบลี - ตอนนี้แม้ว่าการค้นหา google พบว่ามีหลายคนกล่าวถึงไม่มีหน้า Wikipedia สำหรับมัน . คำตอบแรกใน google มีคำจำกัดความนี้: "การบรรจุบิตเป็นแนวคิดง่าย ๆ : ใช้บิตน้อยที่สุดในการเก็บชิ้นส่วนของข้อมูล" kinematicsoup.com/news/2016/9/6/…
Bill K

นั่นคือเมื่อฉันได้เรียนรู้เกี่ยวกับการบรรจุบิตด้วยเช่นกันถึงแม้ว่าคุณจะได้รับความบ้าคลั่งมากกว่าเพียงแค่ปรับ 0 ที่ไม่ได้ใช้ในสิ่งที่เรียกว่าค่าจำนวนเต็ม หลายปีก่อนฉันวิ่งเข้าไปในระบบที่เก็บพารามิเตอร์หนึ่งตัวในรูปแบบ 8 บิต IIRC 5 บิตสำหรับ mantissa ที่ไม่ได้ลงชื่อ (ค่าทั้งหมดเป็นค่าบวกไม่จำเป็นต้องเก็บเครื่องหมายอย่างชัดเจน) และอีก 3 สำหรับฐาน 10 เลขชี้กำลัง ในขณะที่ฉันคิดว่ามันเป็นฮาร์ดแวร์แบบดั้งเดิมที่ไม่มีเส้นทางไปข้างหน้า แต่ด้วยการเรียนรู้ของเครื่องจักรเมื่อไม่นานมานี้เริ่มทำสิ่งที่มี int4 vs int8 ฉันสามารถเห็นปริมาณงานบางอย่างลดลงจาก FP16
Dan Neely

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

14

มีหลายคำที่ใช้อธิบายเรื่องนี้

บิตส่วนใหญ่มักเรียกว่า "บิตแฟล็ก" หรือ "บิตฟิลด์"
(อย่างไรก็ตามเป็นที่น่าสังเกตว่าบางครั้ง "เขตข้อมูลบิต" หมายถึงคุณลักษณะเฉพาะของภาษา C และ C ++ ซึ่งเกี่ยวข้องกัน แต่ไม่เหมือนกันทั้งหมด)

จำนวนเต็มนั้นถูกอ้างถึงอย่างหลากหลายว่า "บิตอาเรย์", "บิตเซ็ต" หรือ "บิตเวกเตอร์" ขึ้นอยู่กับการใช้งานและสถานการณ์

ไม่ว่าจะด้วยวิธีใดการแตกบิตจากชุดบิต / เวกเตอร์ / อาร์เรย์ทำได้โดยการเลื่อนและปิดบัง
(เช่นใช้หน้ากากเล็กน้อย )


สำหรับตัวอย่างของแต่ละคำที่ใช้งานอยู่:

  • บทความของ Wikipedia ในหัวข้อเรื่องมีชื่อว่าBit arrayซึ่งตั้งข้อสังเกตว่าเป็น "แผนที่บิตชุดบิตบิตสตริงบิตหรือบิตเวกเตอร์"
  • C ++ ใช้ std::bitset
  • Java ใช้ BitSet
  • ใช้ C # BitArray
  • StackOverflow มีแท็กbitvector, bitarrayและbitset
  • ใน PyPi มีbitarrayโครงการและBitVectorโครงการอยู่

มันไม่เกี่ยวข้องกับคำถามจริงๆ แต่ฉันอยากจะบอกว่า: โปรดอย่าใช้การบวกและการลบเพื่อตั้งค่าและบิตที่ชัดเจนเนื่องจากวิธีการเหล่านั้นมีแนวโน้มที่จะเกิดข้อผิดพลาด
(เช่นถ้าคุณทำnum += 1สองครั้งผลลัพธ์จะเท่ากับnum += 2)

ต้องการใช้การดำเนินการระดับบิตที่เหมาะสมแทนหากภาษาที่คุณเลือกให้:

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ฯลฯ ไม่จำเป็นต้องกลายพันธุ์numในขณะที่คุณกำลังแยกค่า
Roger Lipscombe

3
@RogerLipscombe จุดดีผมไม่ได้จริงๆอ่านผ่านสิ่งที่รหัสได้ทำเพียงแค่ปฏิกิริยาต่อการใช้และ+ -ตอนนี้ฉันไปได้ดีขึ้นแล้วใช้!= 0แทนไตรภาคซึ่งฉันรู้สึกว่ากระชับในขณะที่ยังคงถูกเปิดเผย
Pharap
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.