位運算總結

2021-08-10 22:29:47 字數 4503 閱讀 5386

位運算,相比普通的**最大的優點就是其帶來的高效性,也因此可以常在底層原始碼中看見它們的蹤影。

首先還是先來回顧下位操作的基礎知識。(除非特別說明,否則以下都以 2 進製為例)

與運算子 "&" 是雙目運算子。只有對應的兩個二進位均為 1 時,結果位才為 1,否則為 0。例如:

9 & 5 = 00001001

& 00000101

= 00000001

= 1

或運算子 "|" 是雙目運算子。只要對應的兩個二進位有乙個為 1 時,結果位就為 1。例如:

9 | 5 = 00001001

| 00000101

= 00001101

= 13

非運算子 "~" 為單目運算子。其功能是對參與運算的各二進位求反。例如:

~ 9 = ~ 00001001

= 11110110

= -10

異或運算子 "^" 是雙目運算子。其功能是對參與運算的二進位相異或,即當兩二進位相異時,結果為 1,相同就為 0。例如:

9 ^ 5 = 00001001

^ 00000101

= 00001100

= 12

例 1例 2

x01100011

10010101

x << 4

00110000

01010000

x >> 4(邏輯右移)

00000110

00001001

x >> 4(算術右移)

00000110

11111001

左移動就是向左移動 k 位,丟棄最高的 k 位,並在右端補 k 個 0,也就是常說的當前值乘以 2 的 k 次方。

右移動的原理也是相同的,右移 k 位就是當前數除以 2 的 k 次方。唯一不同的是分為邏輯右移和算術右移。

邏輯右移就是無符號移位,右移幾位,就在左端補幾個 0。

算術右移動是有符號移位,和邏輯右移不同的是,算術右移是在左端補 k 個最高有效位的值,如此看著有些奇特,但對有符號整數資料的運算非常有用。我們知道有符號的數,首位位元組,是用來表示數字的正負(1 為負)。負數採用補碼形式來儲存,比如 - 26(11100110),算術右移 1 位之後 - 13(11110011)。如若不是補最高有效位的值 1 而是補作 0 的話,右移之後就變成正數了。

i 取反再與 i 相加,相當於把所有二進位制位設為 1,其十進位制結果為 - 1。

-~n == n + 1,~n 為其取反,負號 '-' 再對其取反並加 1。

~-n == n - 1,思路就是找到最低位的第乙個 1,對其取反並把該位後的所有位也取反,即01001000變為01000111

思路就是取反並加 1,也即~n + 1或者(n ^ -1) + 1

利用 ^ 運算子的性質,即得x = a ^ b ^ x

當 n 為 2 的冪時,(x + n - 1) & ~(n - 1)會找到第乙個大於 x 的數,且它正好是 n 的整數倍。

/* version 1 */

intcount_1_bits

(int n)

return count;

}/* version 2 */

intcount_1_bits

(int n)

關於第二個版本,分析如下:(摘自 matrix67 - 位運算,並作稍微修改)

以十進位制數 211 為例,其二進位制為 11010011,

| 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 |   <— 原數

+---+---+---+---+---+---+---+---+

| 1

0 | 0

1 | 0

0 | 1

0 | <— 第一次運算後

+-------+-------+-------+-------+

| 001

1 | 001

0 | <— 第二次運算後

+---------------+---------------+

| 000

0010

1 | <— 第三次運算後,得數為 5

+-------------------------------+

整個程式是乙個分治的思想。第一次我們把每相鄰的兩位加起來,得到每兩位裡 1 的個數,比如前兩位 10 就表示原數的前兩位有 2 個 1。第二次我們繼續兩兩相加,10+01=11,00+10=10,得到的結果是 00110010,它表示原數前 4 位有 3 個 1,末 4 位有 2 個 1。最後一次我們把 0011 和 0010 加起來,得到的就是整個二進位制中 1 的個數。

x = x ^ (x >> 1);

x = x ^ (x >> 2);

x = x ^ (x >> 4);

x = x ^ (x >> 8);

x = x ^ (x >> 16);

cout

<< (x & 1) << endl; // 輸出 1 為奇數

以下分析摘自 matrix67 - 位運算,並作稍微修改,

以十進位制數 1314520 為例,其二進位制為 0001 0100 0000 1110 1101 1000。

第一次異或操作的結果如下:

0001

0100

0000

1110

1101

1000

^ 0000

1010

0000

0111

0110

1100

= 0001

1110

0000

1001

1011

0100

得到的結果是乙個新的二進位制數,其中右起第 i 位上的數表示原數中第 i 和 i+1 位上有奇數個 1 還是偶數個 1。比如,最右邊那個 0 表示原數末兩位有偶數個 1,右起第 3 位上的 1 就表示原數的這個位置和前乙個位置中有奇數個 1。

對這個數進行第二次異或的結果如下:

0001

1110

0000

1001

1011

0100

^ 0000

0111

1000

0010

0110

1101

= 0001

1001

1000

1011

1101

1001

結果裡的每個 1 表示原數的該位置及其前面三個位置中共有奇數個 1,每個 0 就表示原數對應的四個位置上共偶數個 1。

一直做到第五次異或結束後,得到的二進位制數的最末位就表示整個 32 位數裡 1 的奇偶性。

/* 判斷是否是奇數 */

bool

is_odd

(int n)

/* 此方法對 a 和 b 相等的情況不適用 */

a ^= b;

b ^= a; // 相當於 b = b ^ ( a ^ b );

a ^= b;

/* 注意:以下的數字 31 是針對 int 大小為 32 而言 */

intabs

(int n)

其中n >> 31取得 n 的正負號。

/* 注意:以下的數字 31 是針對 int 大小為 32 而言 */

intmax

(int a, int b)

如果a >= b(a - b) >> 31為 0,否則為 - 1。

/* 若 x,y 都為 0,輸出真;若只有乙個為 0,不會報錯但執行結果是錯的,因為 0 沒有正負之分 */

bool

is_same_sign

(int x, int y)

bool

is_power_of_two

(int n)

如果是 2 的冪,n - 1就是把 n 的二進位制的最低的那個 1 取反為 0,並把後面的 0 全部取反為 1。

/* 其中 m 為 2 的冪次方,並對 m 取餘 */

intmod

(int n, int m)

位運算總結

位運算是指按二進位制進行的運算。在系統軟體中,常常需要處理二進位制位的問題。c語言提供了6個位操作 運算子。這些運算子只能用於整型運算元,即只能用於帶符號或無符號的char,short,int與long型別。c語言提供的位運算子列表 運算子 含義 描述 按位與 如果兩個相應的二進位制位都為1,則該位...

位運算總結

位運算是指按二進位制進行的運算。在系統軟體中,常常需要處理二進位制位的問題。c語言提供了6個位操作 運算子。這些運算子只能用於整型運算元,即只能用於帶符號或無符號的char,short,int與long型別。c語言提供的位運算子列表 運算子 含義 描述 按位與 如果兩個相應的二進位制位都為1,則該位...

位運算總結

一 優先順序 高 低 算術運算子 關係運算子 賦值 二 移位運算 要點 1 它們都是雙目運算子,兩個運算分量都是整形,結果也是整形。2 左移 右邊空出的位上補0,左邊的位將從字頭擠掉,其值相當於乘2。3 右移 右邊的位被擠掉。對於左邊移出的空位,如果是正數則空位補0,若為負數,可能補0或補1,這取決...