使用按位运算符在一个int中打包多个值

max*_*yne 29 java packing bitwise-operators bit-packing

低级别位操作从来都不是我的强项.我将理解一些有助于理解按位运算符的以下用例.考虑...

int age, gender, height, packed_info;

. . .   // Assign values 

// Pack as AAAAAAA G HHHHHHH using shifts and "or"
packed_info = (age << 8) | (gender << 7) | height;

// Unpack with shifts and masking using "and"
height = packed_info & 0x7F;   // This constant is binary ...01111111
gender = (packed_info >> 7) & 1;
age    = (packed_info >> 8);
Run Code Online (Sandbox Code Playgroud)

我不确定这段代码是什么以及如何完成的?为什么使用幻数0x7F?如何完成包装和拆包?

资源

tho*_*att 63

正如评论所说,我们将把年龄,性别和身高打包成15位格式:

AAAAAAAGHHHHHHH
Run Code Online (Sandbox Code Playgroud)

让我们从这部分开始:

(age << 8)
Run Code Online (Sandbox Code Playgroud)

首先,年龄有这种格式:

age           = 00000000AAAAAAA
Run Code Online (Sandbox Code Playgroud)

其中每个A可以是0或1.

<< 8将位移动到左侧8位,并用零填充间隙.所以你得到:

(age << 8)    = AAAAAAA00000000
Run Code Online (Sandbox Code Playgroud)

同理:

gender        = 00000000000000G
(gender << 7) = 0000000G0000000
height        = 00000000HHHHHHH
Run Code Online (Sandbox Code Playgroud)

现在我们想将这些组合成一个变量.该|运营商的工作原理是看着每一位,并返回1,如果该位是其中一个输入1.所以:

0011 | 0101 = 0111
Run Code Online (Sandbox Code Playgroud)

如果一个输入中的位为0,则从另一个输入获得该位.看看(age << 8),(gender << 7)并且height,你会看到,如果其中一个的位为1,则其他的为0.所以:

packed_info = (age << 8) | (gender << 7) | height = AAAAAAAGHHHHHHH
Run Code Online (Sandbox Code Playgroud)

现在我们要解压缩位.让我们从高度开始吧.我们想得到最后7位,并忽略前8位.为此,我们使用&运算符,只有当两个输入位都是1时才返回1.所以:

0011 & 0101 = 0001
Run Code Online (Sandbox Code Playgroud)

所以:

packed_info          = AAAAAAAGHHHHHHH
0x7F                 = 000000001111111
(packed_info & 0x7F) = 00000000HHHHHHH = height
Run Code Online (Sandbox Code Playgroud)

为了达到这个年龄,我们可以将所有8个位置推到右边,我们就离开了0000000AAAAAAAA.所以age = (packed_info >> 8).

最后,为了获得性别,我们将所有7个位置推到右边以摆脱高度.然后我们只关心最后一点:

packed_info            = AAAAAAAGHHHHHHH
(packed_info >> 7)     = 0000000AAAAAAAG
1                      = 000000000000001
(packed_info >> 7) & 1 = 00000000000000G
Run Code Online (Sandbox Code Playgroud)

  • 这是一篇超级好的写作.在我读过的所有内容中,这是首先明确发生了什么的事情. (7认同)

And*_*ite 12

这可能是比特操纵的一个相当长的教训,但首先让我指出维基百科上位掩盖文章.

packed_info = (age << 8) | (gender << 7) | height;
Run Code Online (Sandbox Code Playgroud)

取出年龄并将其值移动超过8位然后取出性别并将其移动超过7位,高度将占据最后一位.

age    = 0b101
gender = 0b1
height = 0b1100
packed_info = 0b10100000000
            | 0b00010000000
            | 0b00000001100
/* which is */
packed_info = 0b10110001100
Run Code Online (Sandbox Code Playgroud)

解包反过来但使用像0x7F(0b 01111111)这样的掩码来修剪字段中的其他值.

gender = (packed_info >> 7) & 1;
Run Code Online (Sandbox Code Playgroud)

会像...一样工作

gender = 0b1011 /* shifted 7 here but still has age on the other side */
       & 0b0001
/* which is */
gender = 0b1
Run Code Online (Sandbox Code Playgroud)

注意,将任何东西与1进行AND运算与"保持"该位相同,而使用0进行AND运算与"忽略"该位相同.


Bra*_*ros 6

如果要将日期存储为数字,则可以通过将年份乘以10000,月份乘以100并加上日期来实现。诸如2011年7月2日这样的日期将被编码为数字20110702:

    year * 10000 + month * 100 + day -> yyyymmdd
    2011 * 10000 + 7 * 100 + 2 -> 20110702
Run Code Online (Sandbox Code Playgroud)

可以说,我们使用yyyymmdd掩码对日期进行了编码。我们可以将此操作描述为

  • 将第4年的位置向左移动,
  • 将第2个月的位置向左移动,
  • 保持原样。
  • 然后将三个值组合在一起。

这与年龄,性别和身高编码相同,只是作者以二进制的方式思考。

查看这些值可能具有的范围:

    age: 0 to 127 years
    gender: M or F
    height: 0 to 127 inches
Run Code Online (Sandbox Code Playgroud)

如果将这些值转换为二进制,我们将有:

    age: 0 to 1111111b (7 binary digits, or bits)
    gender: 0 or 1 (1 bit)
    height: 0 to 1111111b (7 bits also)
Run Code Online (Sandbox Code Playgroud)

考虑到这一点,我们可以使用掩码aaaaaaaghhhhhhh编码年龄-性别-高度数据,仅是在这里我们谈论的是二进制数字,而不是十进制数字。

所以,

  • 向左移动年龄8
  • 将性别向左移动7 ,然后
  • 保持高度不变。
  • 然后将所有三个值组合在一起。

对于二进制,Shift-Left运算符(<<)将值向左移动n个位置。“或”运算符(在许多语言中为“ |”)将值组合在一起。因此:

    (age << 8) | (gender << 7) | height
Run Code Online (Sandbox Code Playgroud)

现在,如何“解码”这些值?

用二进制比用十进制更容易:

  • 你“掩盖”了身高,
  • 将性别向右移动7位,并同时将其遮住,最后
  • 将年龄8位右移。

Shift-Right运算符(>>)将值向右移动n个位置(无论从最右边的位置“移出”的数字丢失了)。“ And”二进制运算符(在许多语言中为“&”)可屏蔽位。为此,它需要一个掩码,指示要保留的位和要销毁的位(保留1位)。因此:

    height = value & 1111111b (preserve the 7 rightmost bits)
    gender = (value >> 1) & 1 (preserve just one bit)
    age = (value >> 8)
Run Code Online (Sandbox Code Playgroud)

由于在大多数语言中,十六进制的1111111b为0x7f,因此才有了该魔术数字。使用127(十进制为1111111b)将具有相同的效果。

  • 感谢您提供详细信息。它真的很有用。 (2认同)

小智 5

同样的要求我已经遇到过很多次了。借助按位与运算符,这非常容易。只需用二 (2) 的递增幂来限定您的值即可。要存储多个值,请将它们的相对数(2 的幂)相加并获得 SUM。此 SUM 将合并您选择的值。如何 ?

只需对每个值执行按位与操作,对于未选择的值将给出零 (0),对于选择的值将给出非零。

解释如下:

1) 价值观(是、否、也许)

2) 2 的幂赋值(2)

YES   =    2^0    =    1    =    00000001
NO    =    2^1    =    2    = 00000010
MAYBE =    2^2    =    4    = 00000100
Run Code Online (Sandbox Code Playgroud)

3)我选择“是”和“可能”,因此选择“SUM”:

SUM    =    1    +    4    =    5

SUM    =    00000001    +    00000100    =    00000101 
Run Code Online (Sandbox Code Playgroud)

该值将存储 YES 和 MAYBE。如何?

1    &    5    =    1    ( non zero )

2    &    5    =    0    ( zero )

4    &    5    =    4    ( non zero )
Run Code Online (Sandbox Code Playgroud)

因此 SUM 包括

1    =    2^0    =    YES
4    =    2^2    =    MAYBE.
Run Code Online (Sandbox Code Playgroud)

更详细的解释和实现请访问我的博客