低级别位操作从来都不是我的强项。我将理解一些有助于理解按位运算符的以下用例.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);
我不确定这段代码是完成的以及如何完成?为什么使用幻数0x7F?如何完成包装和拆包?
答案 0 :(得分:58)
正如评论所说,我们打算将年龄,性别和身高打包成15位格式:
AAAAAAAGHHHHHHH
让我们从这一部分开始:
(age << 8)
首先,年龄有这种格式:
age = 00000000AAAAAAA
其中每个A可以是0或1。
<< 8
将位移到左侧8位,并用零填充间隙。所以你得到:
(age << 8) = AAAAAAA00000000
类似地:
gender = 00000000000000G
(gender << 7) = 0000000G0000000
height = 00000000HHHHHHH
现在我们想将这些组合成一个变量。 |
运算符通过查看每个位来工作,如果任一输入中的位为1,则返回1。所以:
0011 | 0101 = 0111
如果一个输入中的位为0,则从另一个输入获得该位。查看(age << 8)
,(gender << 7)
和height
,您会看到,如果其中一个位为1,则其他位为0。所以:
packed_info = (age << 8) | (gender << 7) | height = AAAAAAAGHHHHHHH
现在我们想解开这些比特。让我们从高度开始吧。我们想得到最后7位,并忽略前8位。为此,我们使用&
运算符,只有当两个输入位都是1时才返回1.所以:
0011 & 0101 = 0001
所以:
packed_info = AAAAAAAGHHHHHHH
0x7F = 000000001111111
(packed_info & 0x7F) = 00000000HHHHHHH = height
为了获得年龄,我们可以将所有8个位置向右推,我们只剩下0000000AAAAAAAA
。所以age = (packed_info >> 8)
。
最后,为了获得性别,我们将所有7个位置向右推,以摆脱高度。然后我们只关心最后一点:
packed_info = AAAAAAAGHHHHHHH
(packed_info >> 7) = 0000000AAAAAAAG
1 = 000000000000001
(packed_info >> 7) & 1 = 00000000000000G
答案 1 :(得分:10)
这可能是比特操纵的一个相当长的教训,但首先让我指出bit masking article on Wikipedia。
packed_info = (age << 8) | (gender << 7) | height;
取年龄并将其值超过8位然后取出性别并将其移动超过7位,高度将占据最后一位。
age = 0b101
gender = 0b1
height = 0b1100
packed_info = 0b10100000000
| 0b00010000000
| 0b00000001100
/* which is */
packed_info = 0b10110001100
解包反过来但使用像0x7F(0b 01111111)这样的掩码来修剪字段中的其他值。
gender = (packed_info >> 7) & 1;
会像...一样......
gender = 0b1011 /* shifted 7 here but still has age on the other side */
& 0b0001
/* which is */
gender = 0b1
请注意,将任何内容与1进行AND运算与“保持”该位相同,而使用0进行AND运算与“忽略”该位相同。
答案 2 :(得分:5)
如果您要将日期存储为数字,也许您可以通过将年份乘以10000,将月份乘以100并添加日期来完成此操作。 2011年7月2日等日期将编码为20110702:
year * 10000 + month * 100 + day -> yyyymmdd
2011 * 10000 + 7 * 100 + 2 -> 20110702
我们可以说我们用 yyyymmdd 掩码编码日期。我们可以将此操作描述为
这与年龄,性别和身高编码相同,只是作者以二进制思考。
查看这些值可能具有的范围:
age: 0 to 127 years
gender: M or F
height: 0 to 127 inches
如果我们将这些值翻译为二进制,我们就会这样:
age: 0 to 1111111b (7 binary digits, or bits)
gender: 0 or 1 (1 bit)
height: 0 to 1111111b (7 bits also)
考虑到这一点,我们可以使用掩码 aaaaaaahhhhhhhh 对年龄 - 性别 - 身高数据进行编码,只是在这里我们讨论的是二进制数字,而不是十进制数字。
所以,
在二进制中,Shift-Left运算符(&lt;&lt;)将值 n 位置向左移动。 “Or”运算符(许多语言中的“|”)将值组合在一起。因此:
(age << 8) | (gender << 7) | height
现在,如何“解码”这些值?
二进制比十进制更容易:
Shift-Right运算符(&gt;&gt;)向右移动n个位置(无论从最右边位置“移出”的数字都丢失)。 “和”二进制运算符(在许多语言中为“&amp;”)掩盖位。为此,它需要一个掩码,指示要保留哪些位以及要销毁哪些位(保留1位)。因此:
height = value & 1111111b (preserve the 7 rightmost bits)
gender = (value >> 1) & 1 (preserve just one bit)
age = (value >> 8)
由于十六进制中的1111111b在大多数语言中都是0x7f,这就是这个神奇数字的原因。使用127(十进制为1111111b)可以产生相同的效果。
答案 3 :(得分:3)
更简洁的回答:
AAAAAAA G HHHHHHH
包装:
packed = age << 8 | gender << 7 | height
或者,如果在MySQL SUM聚合函数中使用
,则可以对组件求和packed = age << 8 + gender << 7 + height
启封:
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
另一个(更长)的例子:
假设您有一个要打包的IP地址,但它是一个虚构的IP地址,例如 132.513.151.319。请注意,某些组件大于256,与实际IP地址不同,需要8位以上。
首先,我们需要弄清楚我们需要使用哪种偏移来存储最大数量。 让我们说我们虚构的IP没有任何组件可以大于999,这意味着每个组件需要10位存储(允许数字高达1014)。
packed = (comp1 << 0 * 10) | (comp1 << 1 * 10) | (comp1 << 2 * 10) | (comp1 << 3 * 10)
提供dec 342682502276
或bin 100111111001001011110000000010010000100
现在让我们解压缩值
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
其中(1 << 10) - 1
是二进制掩码,我们用来隐藏左边的位,超出我们感兴趣的10个最右边的位。
使用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`;
答案 4 :(得分:2)
左移运算符意味着“乘以这两次”。在二进制中,将数字乘以2与向右侧添加零相同。
右移运算符与左移运算符相反。
管道运算符是“或”,意味着将两个二进制数叠加在一起,并且在任一数字中都有1,该列中的结果为1。
所以,让我们解压出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;
拆包是相反的。
// 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);
答案 5 :(得分:1)
您可以将表达式x & mask
视为从x
中删除mask
中不存在的位(即,值为0)的操作。这意味着,packed_info & 0x7F
从packed_info
中删除了高于第七位的所有位。
示例:如果packed_info
为二进制的1110010100101010
,则packed_info & 0x7f
将为
1110010100101010
0000000001111111
----------------
0000000000101010
因此,在height
中,我们得到packed_info
的低7位。
接下来,我们将整个packed_info
移动7,这样我们就会删除已经读出的信息。所以我们得到(对于上一个示例中的值)111001010
性别存储在下一位,因此使用相同的技巧:& 1
我们只从信息中提取该位。其余信息包含在偏移量8处。
回头也不复杂:你取age
,将它移8位(所以你从1110010100000000
得到11100101
),将gender
换成7(所以你得到00000000
),然后取高度(假设它适合低7位)。然后,你将所有这些组合在一起:
1110010100000000
0000000000000000
0000000000101010
----------------
1110010100101010
答案 6 :(得分:1)
我曾多次遇到同样的要求。借助Bitwise AND运算符非常容易。只需增加两(2)的幂即可使你的价值合格。要存储多个值,请添加它们的相对数(2的幂)并获得SUM。此SUM将合并您选择的值。怎么样 ?
对每个值执行按位AND,对于未选择的值,将给出零(0),为其选择非零。
以下是解释:
1)值(YES,NO,MAYBE)
2)赋予权力2(2)
YES = 2^0 = 1 = 00000001
NO = 2^1 = 2 = 00000010
MAYBE = 2^2 = 4 = 00000100
3)我选择YES和MAYBE因此SUM:
SUM = 1 + 4 = 5
SUM = 00000001 + 00000100 = 00000101
此值将同时存储YES和MAYBE。怎么样?
1 & 5 = 1 ( non zero )
2 & 5 = 0 ( zero )
4 & 5 = 4 ( non zero )
因此,SUM由
组成1 = 2^0 = YES
4 = 2^2 = MAYBE.
有关更详细的说明和实施,请访问我的blog