2011-07-02 80 views
23

低級位操作從來就不是我的強項。我會感謝理解按位operators.Consider下列情況下使用一些幫助...使用位運算符在一個int中打包多個值

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?打包和拆包是如何完成的?

Source

+11

我認爲這是值得一問這個問題之前閱讀二進制數表示和位運算符。 –

+7

在評論中的圖片幾乎說這一切:AAAAAAA G HHHHHHH –

回答

54

正如評論說,我們要的格式打包的年齡,性別和身高爲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 
+0

感謝您的答案.. – maxpayne

+5

這是一個超級好的寫作。在我讀過的所有東西中,這是第一件清楚發生了什麼的事情。 – DrHall

+0

很好的解釋。 – ZZeyaNN

9

這可能是位操作相當長的教訓,但首先讓我指出你太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是一樣的「保持」該位,並取與0是一樣的「無視」該位。

1

您可以看到表達式x & mask是從x中刪除mask中不存在的位(即值爲0)的操作。這意味着,packed_info & 0x7Fpacked_info中刪除高於第七位的所有位。

例如:如果packed_info是二進制1110010100101010,然後packed_info & 0x7f

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

所以,在height我們得到的packed_info低7位。

接下來,我們將整個packed_info轉移7,這樣我們刪除了我們已經讀出的信息。所以我們得到(對於前面例子中的值)111001010性別存儲在下一位,所以使用相同的技巧:& 1我們只從信息中提取那一位。中其餘的信息包含在偏移8

包裝背面並不複雜,太:你把age,卻將其8位(所以你從111001011110010100000000),該gender 7移動(所以你得到00000000 ),並取高度(假設它適合低7位)。然後,一起撰寫所有的人:

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

左移運算符表示「乘以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); 
4

如果你要的日期存儲爲一個數字,也許你會通過100年的10000個,每月倍增,增加一天完成它。一個日期,如2011年7月2,將被編碼爲數字20110702:

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

我們可以說,我們的編碼在YYYYMMDD面具的日期。我們可以在一年中每月形容這個操作,作爲

  • 移4位到左側,
  • 移2個位置的左側和
  • 離開一天是。
  • 然後將這三個值組合在一起。

這與發生在年齡,性別和身高編碼方面的情況是一樣的,只是作者在二進制中思考。

見範圍,這些值可能有:

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) 

考慮到這一點,我們可以編碼歲-gender-height數據與掩碼aaaaaaaghhhhhhh,只是說這裏我們說的是二進制數字,而不是十進制數字。

所以,

  • 移8個向左,
  • 移性別7 向左和
  • 離開高度是。
  • 然後將所有三個值組合在一起。

在二進制中,左移位運算符(< <)移動的值Ñ位置到左邊。 「Or」運算符(許多語言中的「|」)將值組合在一起。因此:

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

現在,如何 「解碼」 這些價值觀?

它在二進制比十進制簡單:

  • 你「屏蔽掉」的高度,
  • 轉變性別的7位到右側,掩蓋掉也,最後
  • 移年齡在8位。

Shift-Right運算符(>>)向右移動值n位置(無論位於最右邊位置的「out」是否丟失)。 「And」二元運算符(多種語言中的「&」)掩碼位。要做到這一點,它需要一個掩碼,指示要保留哪些位和要破壞哪些位(保留1位)。因此:

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

由於1111111b十六進制0x7F的是在大多數語言中,這對於幻數的原因。通過使用127(十進制中的1111111b)可以獲得相同的效果。

+1

感謝您的詳細信息..它真的很有用。 – maxpayne

2

更凝結答案:

AAAAAAA摹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的組件需要8位以上的真實IP地址。

首先,我們需要弄清楚我們需要使用什麼補償來存儲最大數量。 可以說我們虛構的IP沒有組件可以大於999,這意味着我們需要每個組件10位存儲空間(允許數字高達1014)。

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

其中給出dec 342682502276bin 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`; 
0

我多次面對同樣的要求。在Bitwise AND運算符的幫助下很容易。只需以兩個(2)的增加的權力來限定你的價值。要存儲多個值,請添加其相對編號(2的冪)並獲取SUM。該SUM將整合您選擇的值。怎麼樣 ?

只需按位與每個值進行操作,它將爲未選擇的值和非零值選擇的值賦予零(0)。

下面是說明:

1)的值(YES,NO,也許)

2)分配到兩個(2)

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

3)I選擇YES功率和MAYBE因此SUM:

SUM = 1 + 4 = 5 

SUM = 00000001 + 00000100 = 00000101 

此值將同時存儲YES以及也許。怎麼樣?

1 & 5 = 1 (non zero) 

2 & 5 = 0 (zero) 

4 & 5 = 4 (non zero) 

因此SUM由

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

對於更詳細的解釋和執行訪問我blog