Java 使用按位运算符将多个值打包到一个 int 中

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6556961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 07:43:22  来源:igfitidea点击:

use of the bitwise operators to pack multiple values in one int

javabitwise-operatorspackingbit-packing

提问by maxpayne

Low level bit manipulation has never been my strong point. I will appreciate some help in understanding the following use case of bitwise operators.Consider...

低级位操作从来都不是我的强项。在理解以下按位运算符用例方面,我将不胜感激。考虑...

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);

I am not sure what this code is accomplishing and how? Why use the magic number 0x7F ? How is the packing and unpacking accomplished?

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

Source

来源

采纳答案by thomson_matt

As the comment says, we're going to pack the age, gender and height into 15 bits, of the format:

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

AAAAAAAGHHHHHHH

Let's start with this part:

让我们从这部分开始:

(age << 8)

To start with, age has this format:

首先,年龄具有以下格式:

age           = 00000000AAAAAAA

where each A can be 0 or 1.

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

<< 8moves the bits 8 places to the left, and fills in the gaps with zeroes. So you get:

<< 8将位向左移动 8 位,并用零填充空白。所以你得到:

(age << 8)    = AAAAAAA00000000

Similarly:

相似地:

gender        = 00000000000000G
(gender << 7) = 0000000G0000000
height        = 00000000HHHHHHH

Now we want to combine these into one variable. The |operator works by looking at each bit, and returning 1 if the bit is 1 in either of the inputs. So:

现在我们想将这些组合成一个变量。该|运算符通过查看每个位来工作,如果任一输入中的位为 1,则返回 1。所以:

0011 | 0101 = 0111

If a bit is 0 in one input, then you get the bit from the other input. Looking at (age << 8), (gender << 7)and height, you'll see that, if a bit is 1 for one of these, it's 0 for the others. So:

如果一个输入中的某个位为 0,那么您将从另一个输入中获得该位。查看(age << 8),(gender << 7)height,您会看到,如果其中一个位为 1,则其他位为 0。所以:

packed_info = (age << 8) | (gender << 7) | height = AAAAAAAGHHHHHHH

Now we want to unpack the bits. Let's start with the height. We want to get the last 7 bits, and ignore the first 8. To do this, we use the &operator, which returns 1 only if both of the input bits are 1. So:

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

0011 & 0101 = 0001

So:

所以:

packed_info          = AAAAAAAGHHHHHHH
0x7F                 = 000000001111111
(packed_info & 0x7F) = 00000000HHHHHHH = height

To get the age, we can just push everything 8 places to the right, and we're left with 0000000AAAAAAAA. So age = (packed_info >> 8).

要获得年龄,我们可以将所有内容向右推 8 个位置,然后剩下0000000AAAAAAAA. 所以age = (packed_info >> 8)

Finally, to get the gender, we push everything 7 places to the right to get rid of the height. We then only care about the last bit:

最后,为了获得性别,我们将所有东西向右推 7 个位置以去除高度。然后我们只关心最后一点:

packed_info            = AAAAAAAGHHHHHHH
(packed_info >> 7)     = 0000000AAAAAAAG
1                      = 000000000000001
(packed_info >> 7) & 1 = 00000000000000G

回答by Andrew White

This could be a rather long lesson in bit manipulation but first let me point you too the bit masking article on Wikipedia.

这可能是有关位操作的相当长的课程,但首先让我向您指出Wikipedia 上位屏蔽文章

packed_info = (age << 8) | (gender << 7) | height;

Take age and move it's value over 8 bits then take gender and move it over 7 bits and height will occupy the last bits.

取年龄并将其值移至 8 位,然后取性别并将其移至 7 位,高度将占据最后一位。

age    = 0b101
gender = 0b1
height = 0b1100
packed_info = 0b10100000000
            | 0b00010000000
            | 0b00000001100
/* which is */
packed_info = 0b10110001100

Unpacking does the reverse but uses masks like 0x7F (which is 0b 01111111) to trim out the other values in the field.

解包执行相反的操作,但使用 0x7F(即 0b 01111111)等掩码来修剪字段中的其他值。

gender = (packed_info >> 7) & 1;

Would work like...

会像...

gender = 0b1011 /* shifted 7 here but still has age on the other side */
       & 0b0001
/* which is */
gender = 0b1

Note that ANDing anything to 1 is the same as "keeping" that bit and ANDing with 0 is the same as "ignoring" that bit.

请注意,将任何内容与 1 进行 AND 运算与“保留”该位相同,而与 0 进行 AND 运算与“忽略”该位相同。

回答by Vlad

You can see the expression x & maskas an operation that removes from xthe bits that are not present (i.e., have value 0) in mask. That means, packed_info & 0x7Fremoves from packed_infoall the bits that are above the seventh bit.

您可以将表达式x & mask视为从x中不存在(即具有值 0)的位中删除的操作mask。这意味着,packed_info & 0x7Fpacked_info第七位以上的所有位中删除。

Example: if packed_infois 1110010100101010in binary, then packed_info & 0x7fwill be

示例:如果packed_info1110010100101010二进制,那么packed_info & 0x7f将是

1110010100101010
0000000001111111
----------------
0000000000101010

So, in heightwe get the lower 7 bits of packed_info.

所以,height我们得到了 的低 7 位packed_info

Next, we are shifting the whole packed_infoby 7, this way we remove the information which we have already read out. So we get (for the value from previous example) 111001010The gender is stored at the next bit, so with the same trick: & 1we are extracting only that bit from the information. The rest of the information is contained at offset 8.

接下来,我们将整体移动packed_info7,这样我们就删除了我们已经读出的信息。所以我们得到(对于上一个例子的值)111001010性别存储在下一位,所以使用相同的技巧:& 1我们只从信息中提取那一位。其余信息包含在偏移量 8 处。

Packing back is not complicated, too: you take age, shift it 8 bits (so you get 1110010100000000from 11100101), shift the genderby 7 (so you get 00000000), and take the height (assuming it would fit lower 7 bits). Then, you are composing all of them together:

打包回来也不复杂:你把age它移 8 位(所以你1110010100000000从得到11100101),把 移gender7(所以你得到00000000),然后取高度(假设它适合低 7 位)。然后,您将所有这些组合在一起:

1110010100000000
0000000000000000
0000000000101010
----------------
1110010100101010

回答by Anarchofascist

The left shift operator means "multiply by two, this many times". In binary, multiplying a number by two is the same as adding a zero to the right side.

左移运算符的意思是“乘以 2,多次”。在二进制中,将一个数乘以 2 与在右侧添加一个零相同。

The right shift operator is the reverse of the left shift operator.

右移运算符与左移运算符相反。

The pipe operator is "or", meaning overlay two binary numbers on top of each other, and where there is a 1 in either number the result in that column is a 1.

管道运算符是“或”,意思是将两个二进制数相互叠加,其中任一数字为 1 时,该列中的结果为 1。

So, let's extract the operation for packed_info:

所以,让我们提取packed_info的操作:

// Create age, shifted left 8 times:
//     AAAAAAA00000000
age_shifted = age << 8;

// Create gender, shifted left 7 times:
//     0000000G0000000
gender_shifted = gender << 7;

// "Or" them all together:
//     AAAAAAA00000000
//     0000000G0000000
//     00000000HHHHHHH
//     ---------------
//     AAAAAAAGHHHHHHH
packed_info = age_shifted | gender_shifted | height;

And the unpacking is the reverse.

而开箱则相反。

// Grab the lowest 7 bits:
//     AAAAAAAGHHHHHHH &
//     000000001111111 =
//     00000000HHHHHHH
height = packed_info & 0x7F;

// right shift the 'height' bits into the bit bucket, and grab the lowest 1 bit:
//     AAAAAAAGHHHHHHH 
//   >> 7 
//     0000000AAAAAAAG &
//     000000000000001 =
//     00000000000000G
gender = (packed_info >> 7) & 1;

// right shift the 'height' and 'gender' bits into the bit bucket, and grab the result:
//     AAAAAAAGHHHHHHH 
//   >> 8
//     00000000AAAAAAA
age    = (packed_info >> 8);

回答by Branco Medeiros

If you were going to store a date as a number, maybe you would accomplish it by multiplying the year by 10000, the month by 100 and adding the day. A date such as July, 2, 2011 would be encoded as the number 20110702:

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

    year * 10000 + month * 100 + day -> yyyymmdd
    2011 * 10000 + 7 * 100 + 2 -> 20110702

We can say that we encoded the date in a yyyymmddmask. We could describe this operation as

我们可以说我们将日期编码在yyyymmdd掩码中。我们可以将这个操作描述为

  • Shift the year 4 positions to the left,
  • shift the month 2 positions to the left and
  • leave the day as is.
  • Then combine the three values together.
  • 将第 4 年的位置向左移动,
  • 将第 2 个月的位置向左移动并
  • 离开这一天。
  • 然后将三个值组合在一起。

This is the same thing that is happenning with the age, gender and height encoding, only that the author is thinking in binary.

这与年龄、性别和身高编码发生的事情是一样的,只是作者以二进制方式思考。

See the ranges that those values may have:

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

    age: 0 to 127 years
    gender: M or F
    height: 0 to 127 inches

If we translate those values to binary, we would have this:

如果我们将这些值转换为二进制,我们会得到这样的结果:

    age: 0 to 1111111b (7 binary digits, or bits)
    gender: 0 or 1 (1 bit)
    height: 0 to 1111111b (7 bits also)

With this in mind, we can encode the age-gender-height data with the mask aaaaaaaghhhhhhh, only that here we are talking about binarydigits, not decimaldigits.

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

So,

所以,

  • Shift the age 8 bitsto the left,
  • shift the gender 7 bitsto the left and
  • leave the height as is.
  • Then combine all three values together.
  • 将年龄向左移动 8
  • 将性别向左移动 7
  • 保持高度不变。
  • 然后将所有三个值组合在一起。

In binary, the Shift-Left operator (<<) moves a value npositions to the left. The "Or" operator ("|" in many languages) combines values together. Therefore:

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

    (age << 8) | (gender << 7) | height

Now, how to "decode" those values?

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

It's easier in binary than in decimal:

二进制比十进制更容易:

  • You "mask away" the height,
  • shift the gender 7 bits to the right and mask that away also, and finally
  • shift the age 8 bits to the right.
  • 你“掩盖”了高度,
  • 将性别 7 位向右移动并屏蔽掉,最后
  • 将年龄 8 位右移。

The Shift-Right operator (>>) moves a value n positions to the right (whatever digits shifted "out" of the rightmost position are lost). The "And" binary operator ("&" in many languages) masks bits. To do that it needs a mask, indicating which bits to preserve and which bits to destroy (1 bits are preserved). Therefore:

Shift-Right 运算符 (>>) 将值向右移动 n 个位置(从最右边位置“移出”的任何数字都将丢失)。“与”二元运算符(在许多语言中为“&”)屏蔽位。为此,它需要一个掩码,指示要保留哪些位以及要销毁哪些位(保留 1 位)。所以:

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

Since 1111111b in hex is 0x7f in most languages, that's the reason for that magic number. You would have the same effect by using 127 (which is 1111111b in decimal).

由于在大多数语言中十六进制的 1111111b 是 0x7f,这就是这个幻数的原因。使用 127(十进制为 1111111b)会产生相同的效果。

回答by Alexei Tenitski

A more condense answer:

更简洁的答案:

AAAAAAA G HHHHHHH

啊啊啊啊啊啊啊啊啊啊啊

Packing:

包装:

packed = age << 8 | gender << 7 | height

Alternatively you can just sum components if ie when used in MySQL SUM aggregate function

或者,如果 ie 在 MySQL SUM 聚合函数中使用时,您可以只对组件求和

packed = age << 8 + gender << 7 + height

Unpacking:

开箱:

age = packed >> 8 // no mask required
gender = packed >> 7 & ((1 << 1) - 1) // applying mask (for gender it is just 1)
height = packed & ((1 << 7) - 1) // applying mask



Another (longer) example:

另一个(更长的)例子:

Say you have an IP address you want to pack, however it is a fictional IP address eg 132.513.151.319. Note that some components greater then 256 which requires more then 8 bits unlike real ip addresses.

假设您有一个要打包的 IP 地址,但它是一个虚构的 IP 地址,例如 132.513.151.319。请注意,某些大于 256 的组件需要超过 8 位,这与真实 IP 地址不同。

First we need to figure out what offset we need to use to be able to store the max number. Lets say with our fictional IPs no component can be bigger then 999 that means we need 10 bits of storage per component (allows numbers up to 1014).

首先,我们需要弄清楚我们需要使用什么偏移量才能存储最大数量。假设使用我们的虚构 IP,没有任何组件可以大于 999,这意味着我们需要每个组件 10 位的存储空间(允许数字高达 1014)。

packed = (comp1 << 0 * 10) | (comp1 << 1 * 10) | (comp1 << 2 * 10) | (comp1 << 3 * 10)

Which gives dec 342682502276or bin 100111111001001011110000000010010000100

哪个给dec 342682502276bin 100111111001001011110000000010010000100

Now lets unpack the value

现在让我们解压价值

comp1 = (packed >> 0 * 10) & ((1 << 10) - 1) // 132
comp2 = (packed >> 1 * 10) & ((1 << 10) - 1) // 513
comp3 = (packed >> 2 * 10) & ((1 << 10) - 1) // 151
comp4 = (packed >> 3 * 10) & ((1 << 10) - 1) // 319

Where (1 << 10) - 1is a binary mask we use to hide bits on the left beyond the 10 right most bits we are interested in.

(1 << 10) - 1我们用来隐藏我们感兴趣的最右边 10 个位之外的左边位的二进制掩码在哪里。

Same example using MySQL query

使用 MySQL 查询的相同示例

SELECT

(@offset := 10) AS `No of bits required for each component`,
(@packed := (132 << 0 * @offset) | 
            (513 << 1 * @offset) | 
            (151 << 2 * @offset) | 
            (319 << 3 * @offset)) AS `Packed value (132.513.151.319)`,

BIN(@packed) AS `Packed value (bin)`,

(@packed >> 0 * @offset) & ((1 << @offset) - 1) `Component 1`,
(@packed >> 1 * @offset) & ((1 << @offset) - 1) `Component 2`,
(@packed >> 2 * @offset) & ((1 << @offset) - 1) `Component 3`,
(@packed >> 3 * @offset) & ((1 << @offset) - 1) `Component 4`;

回答by user5212481

Same requirement I have faced many times. It is very easy with the help of Bitwise AND operator. Just qualify your values with increasing powers of two(2). To store multiple values, ADD their relative number ( power of 2 ) and get the SUM. This SUM will consolidate your selected values. HOW ?

同样的要求我遇到过很多次。在按位与运算符的帮助下,这很容易。只需通过增加二(2)的幂来限定您的值。要存储多个值,请将它们的相对数(2 的幂)相加并得到 SUM。此 SUM 将合并您选择的值。如何 ?

Just do Bitwise AND with every value and it will give zero (0) for values which were not selected and non-zero for which are selected.

只需对每个值执行按位与运算,它就会为未选择的值提供零 (0),并为已选择的值提供非零值。

Here is the explanation:

这是解释:

1) Values ( YES, NO, MAYBE )

1)值(是,否,可能)

2) Assignment to power of two(2)

2) 赋值给二 (2) 的幂

YES   =    2^0    =    1    =    00000001
NO    =    2^1    =    2    = 00000010
MAYBE =    2^2    =    4    = 00000100

3) I choose YES and MAYBE hence SUM:

3) 我选择 YES 并且 MAYBE 因此选择 SUM:

SUM    =    1    +    4    =    5

SUM    =    00000001    +    00000100    =    00000101 

This value will store both YES as well as MAYBE. HOW?

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

1    &    5    =    1    ( non zero )

2    &    5    =    0    ( zero )

4    &    5    =    4    ( non zero )

Hence SUM consists of

因此 SUM 包括

1    =    2^0    =    YES
4    =    2^2    =    MAYBE.

For more detailed explanation and implementation visit my blog

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