1. 程式人生 > >位運算主要知識

位運算主要知識

什麼是位運算? 

程式中的所有數在計算機記憶體中都是以二進位制的形式儲存的。位運算說穿了,就是直接對整數在記憶體中的二進位制位進行操作。 
C++提供了6種位運算子來進行位運算操作:
&      按位與
|      按位或
^      按位異或
~      按位取反
<<     左移(左邊消失,右邊補0)
>>     右移(右邊消失,左邊補符號位)
位運算的運算元是整數型別或字元型.

按位與&運算

將參與運算的兩運算元各自對應的二進位制位進行與操作。例如:6的二進位制是110,11的二進位制是1011,那麼6 & 11的結果就是2 
    110
&  1011 ------------    0010  -->  2
& 運算常常用來將某變數的某些位清0,而保留其它位不變。例如,需要將int型變數n的低8位全置成0,而其餘位不變,則用:
       n = n & 0xFFFFFF00
& 也常用於二進位制取位操作,例如一個數 & 1的結果就是取二進位制的最末位。如果要判斷n的第8位(從右往左,從1開始數)是否是1,則用:
             if (n & 0x80 == 0x80)  語句
附註:int型是32個二進位制位,16進位制整數每個數字代表4個二進位制位,故16進位制int型常量最多是8位。

按位或|運算

 |運算通常用於二進位制特定位上的強制置1,例如一個數或 1的結果就是把二進位制最末位強行變成1。
      110
|    1011 ------------      1111 -->  15

按位異或^運算

0^0=0  0^1=1  1^1=0
^運算通常用於對二進位制的特定一位進行取反操作.例如n^0xff就使得n的最後8位取反。
    110
^  1011 -----------    1101   -->  13
^運算的特點是:如果a^b==c,則有a^c==b和c^b==a
^可用於簡單加密,參見顧森BLOG

左移<<運算

 a << b就表示把a轉為二進位制後左移b位(在後面添b個0)。例如100的二進位制為1100100,而110010000轉成十進位制是400,那麼100 << 2 = 400。可以看出,a << b的值實際上就是a乘以2的b次方
,因為在二進位制數後添一個0就相當於該數乘以2(這樣做要求保證高位的1不被移出)。
通常認為a << 1比a * 2更快,因為前者是更底層一些的操作。因此程式中乘以2的操作請儘量用左移一位來代替。
定義常量時可以用<<運算。你可以方便地用(1 << 16) - 1來表示65535。很多演算法和資料結構要求資料規模必須是2的冪,此時可以用<<來定義MAXN等常量。

右移>>運算

a >> b表示二進位制右移b位(去掉末b位)。
當a是正整數時,a>>b等價於a/(2的b次方)
當a是負整數時,a>>b並不等價與a/(2的b次方),而是等於a/(2的b次方)上取整。
如a=-9
cout<<a/2; //輸出-4.
cout<<(a>>1); //輸出-5.

我們也經常用>> 1來代替div 2,比如二分查詢、堆的插入操作等等。
用>>代替除法運算可以使程式效率大大提高。最大公約數的二進位制演算法用除以2操作來代替慢得出奇的%運算,效率可以提高60%。
二進位制求最大公約數原理。
若a<b         gcd(a,b)=gcd(b,a)
若a、b都是偶數,則gcd(a,b)=2*gcd(a/2,b/2)
若a是奇數、b是偶數,則gcd(a,b)=gcd(a,b/2)
若a、b都是奇數,則gcd(a,b)=gcd((a-b)/2,b)

位運算的簡單應用 



整數型別的儲存 

計算機用0x0000到0x7FFF依次表示0到32767的數,剩下的0x8000到0xFFFF依次表示-32768到-1的數。32位有符號整數的儲存方式也是類似的。稍加註意你會發現,二進位制的第一位是用來表示正負號的,0表示正,1表示負。這裡有一個問題:0本來既不是正數,也不是負數,但它佔用了0x0000的位置,因此有符號的整數類型範圍中正數個數比負數少一個。對一個有符號的數進行~運算後,最高位的變化將導致正負顛倒,並且數的絕對值會差1。也就是說,~ a實際上等於-a-1。這種整數儲存方式叫做“補碼”。
換言之,~a+1 = -a,那麼a & -a得到什麼?
得到a的右數第1位為1的數,這個操作可用來列舉a中為1的位,這在位操作中有較多應用。