将许多布尔状态存储/打包为一个数字的名称是什么?


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为底的数字解析,枚举...有很多更有效的方法来实现它,我对这种方法的名称更感兴趣。


8
在C#中,存在enums,并且它们可以具有Flags属性。它们可以使您的代码简单得多。
伯恩哈德·希勒

12
我将其称为“模拟位字段”。除非空间效率至关重要,否则这几乎总是一个坏主意。
凯莉安·佛斯

7
@KilianFoth A bool通常在内部存储为32位整数。这样,打包可以使差异相差32。这确实很多。我的意思是,我们程序员总是准备扔掉一半的资源,但是我通常不愿意扔掉97%的资源。这样的浪费因素很容易在能够运行重要用例和内存不足之间产生区别。
cmaster

3
从历史上看,位掩码通常用于声明,设置和检索值的方式。使用班次是奇怪的,并不是真正的最佳方法。
JimmyJames

3
@cmaster以这种方式存储布尔变量的原因是因为共享单个内存位置(在当今的计算机上为32位或64位)可能对缓存性能非常不利,除非您非常注意机器语言代码。如果您确实有大量的位,那可能是值得的,但如果不是这样,您最好不进行预优化,而在准备传输到网络或磁盘时打包这些位。
比尔K

Answers:


107

它通常被称为位域,您经常会听到的另一个术语是位掩码,用于一次获取或设置单个位值或整个位域。

许多编程语言都有辅助结构来帮助实现这一目标。正如@BernhardHiller在注释中指出的那样,C#的枚举带有标志;Java具有EnumSet类。


4
我将“位字段”解释为使用一种语言功能,该功能允许将单个位分配给结构的字段,而不是使用按位运算符手动进行操作。
格林

22
@PeterGreen这将不同于标准解释。
埃里克(Eric)

1
在记录集和数组处理中很常见的是“位映射”或“位映射”,在这种情况下也可以应用。从多个集合中提取公共元素时,可以将值分解以标识联合模型的组件。我们甚至说八进制文件模式数字。位掩码(任何掩码)往往是过滤器(对于IO端口和数据方向寄存器而言)。
mckenzm

1
C#还具有BitArray,它允许存储任意数量的位并对其进行索引(而标志被限制为整数类型并打算用作掩码)。
a安

真正; 我刚刚提到了我最熟悉的两个结构。可能有数十种语言,尤其是其他语言的语言。
Glorfindel

20

奇怪,这里有很多不同的术语,但是我没有想到立即想到的(这是在您的问题的标题中!)-我一直听到它是术语“位包装”。

我曾以为这确实很明显,但奇怪的是,当我在Google上搜索时,这似乎是一个被广泛使用但未正式定义的术语(维基百科似乎重定向到位字段,这是一种进行位打包的方法,但没有名称的处理)。搜索定义似乎导致此页面:

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

对于SO而言,这不是很好,但它是包括以下简短描述在内的最佳定义/说明:“位打包是一个简单的概念:使用尽可能少的位来存储一条数据。”


你能提供一些参考吗?有趣的词。
格雷格·伯格哈特

13
从技术上讲,位打包是正确的,但它还指的是比布尔状态更笼统的事物-通常以尽可能少的位数存储数据。例如,它的另一种用法可能意味着char将两个chars放入一个数组来压缩数组int
伊兹卡塔'18

@GregBurghardt您知道,这很有趣。发布时我没有考虑它,因为该术语在我学习C和汇编语言的编程时在80年代/ 90年代如此盛行-现在,尽管Google搜索发现很多提及,但没有确定的Wikipedia页面。google中的第一个答案具有以下定义:“位打包是一个简单的概念:使用尽可能少的位来存储一条数据。” kinematicsoup.com/news/2016/9/6/...
比尔ķ

那也是我了解位打包的时候,尽管您可以比标称整数值的简单地重新使用未使用的0更加疯狂。几年前,我遇到了一个以8位浮点数存储其参数之一的系统。IIRC无符号尾数为5位(所有值均为正,无需显式存储符号),基数为10的指数为3位。当时我以为这是传统的硬件故障,没有前进的道路,但是随着机器学习最近开始使用int4 vs int8做事,我可以看到一些工作负载从FP16下降了。
Dan Neely

1
@DanNeely GPU也普遍支持这种情况-在精度,内存和计算之间进行交易非常重要。基于GPU的计算也很好地利用了这一点。
罗安

14

有许多不同的术语用来描述这一点。

最常见的位称为“位标志”或“位字段”。
(但是,值得注意的是,“位字段”有时是指C和C ++语言的特定功能,虽然相关但不完全相同。)

根据用途和情况,整数本身被不同地称为“位数组”,“位集”或“位向量”。

无论哪种方式,都是通过移位和屏蔽从位集/向量/数组中提取位的。
(即使用位掩码。)


对于正在使用的每个术语的一些示例:


它与问题并非真正相关,但我想说:请不要使用加法和减法来设置和清除位,因为这些方法容易出错。
(即,如果您执行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。提取值时无需进行变异。
罗杰·利普斯科姆

3
@RogerLipscombe好点,我不是真的在阅读代码的内容,只是对+and 的使用做出了反应-。我现在做得更好了,用它!= 0代替了三元数,我觉得它更简洁,但仍会引起毛病。
法拉普
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.