原碼、反碼、補碼與位運算
一、原碼、反碼、補碼
1、原碼
- 定義:原碼是一種簡單的機器數表示法。對于一個有符號整數,最高位為符號位, 0 表示正數, 1 表示負數,其余位表示數值的絕對值。
- 示例:以 8 位二進制為例, +5 的原碼是 00000101 , -5 的原碼是 10000101 。
- 問題:原碼在進行加減法運算時比較復雜,因為要根據符號位判斷是加法還是減法,并且對于 0 有兩種表示形式( +0 : 00000000 , -0 : 10000000 ),這給計算機處理帶來不便。
2、反碼
- 定義:正數的反碼與原碼相同;負數的反碼是在原碼的基礎上,符號位不變,其余各位取反。
- 示例:還是以 8 位二進制為例, +5 的反碼是 00000101 , -5 的原碼是 10000101 ,其反碼則為 11111010 。
- 作用:反碼主要是作為從原碼到補碼的過渡。雖然它解決了 0 的表示不唯一問題( +0 和 -0 的反碼都是 00000000 ),但在加減法運算上仍不夠理想。
3、補碼
-
定義:正數的補碼與原碼相同;負數的補碼是在反碼的基礎上,末位加 1 。
-
示例:同樣 8 位二進制, +5 的補碼是 00000101 , -5 的反碼是 11111010 ,其補碼則為 11111011 。
-
優勢:補碼在計算機中得到廣泛應用,它解決了原碼和反碼在運算上的不足。在補碼系統中, 0 只有一種表示形式( 00000000 )。而且使用補碼進行加減法運算時,不需要額外判斷符號位,可以直接將符號位和數值位一起參與運算,簡化了運算規則,提高了運算效率。例如,計算 5 + (-3) , 5 的補碼是 00000101 , -3 的補碼是 11111101 ,兩者相加:
00000101
- 11111101
100000010
由于是 8 位二進制,最高位的 1 溢出被舍棄,結果為 00000010 ,即 2 ,運算結果正確。
- 原碼是最直觀的有符號數表示方法,但運算復雜且 0 的表示不唯一。
- 反碼是從原碼到補碼的過渡,一定程度上解決了 0 的表示問題,但運算仍不夠簡便。
- 補碼克服了原碼和反碼的缺點,成為計算機中表示有符號整數的標準方式,極大地簡化了運算邏輯,提高了計算機處理有符號整數運算的效率。
二、位運算
1. 位與( & )
- 運算規則:對兩個整數的二進制表示中每一位進行比較,只有當兩個對應位都為 1 時,結果位才為 1 ,否則為 0 。
- 示例代碼:
#include <iostream>int main() {int num1 = 10; // 二進制: 1010int num2 = 6; // 二進制: 0110int result = num1 & num2;std::cout << num1 << " & " << num2 << " = " << result << std::endl;// 計算過程:// 1010// & 0110// ------// 0010 結果為2return 0;
}
- 應用場景:
- 掩碼操作:通過與一個特定的掩碼值進行位與操作,可以提取或保留某些位。例如,要獲取一個字節(8位)的低4位,可以與掩碼 0x0F (二進制 00001111 )進行位與操作。
- 判斷奇偶性:與 1 進行位與操作,如果結果為 1 ,則該數為奇數;如果結果為 0 ,則為偶數。因為奇數的二進制最低位是 1 ,偶數的二進制最低位是 0 。
2. 位或( | )
- 運算規則:對兩個整數的二進制表示中每一位進行比較,只要兩個對應位中有一個為 1 ,結果位就為 1 ,只有當兩個對應位都為 0 時,結果位才為 0 。
- 示例代碼:
#include <iostream>int main() {int num1 = 10; // 二進制: 1010int num2 = 6; // 二進制: 0110int result = num1 | num2;std::cout << num1 << " | " << num2 << " = " << result << std::endl;// 計算過程:// 1010// | 0110// ------// 1110 結果為14return 0;
}
應用場景:
- 設置標志位:如果要將一個整數的某些位設置為 1 ,可以與一個相應位為 1 的掩碼進行位或操作。例如,要將一個整數的第3位和第5位設置為 1 ,可以與掩碼 0x28 (二進制 00101000 )進行位或操作。
- 合并數據:在處理一些標志集合時,可以通過位或操作將不同的標志合并到一個整數中。
3. 異或( ^ )
- 運算規則:對兩個整數的二進制表示中每一位進行比較,當兩個對應位不同時,結果位為 1 ,當兩個對應位相同時,結果位為 0 。
- 示例代碼:
#include <iostream>int main() {int num1 = 10; // 二進制: 1010int num2 = 6; // 二進制: 0110int result = num1 ^ num2;std::cout << num1 << " ^ " << num2 << " = " << result << std::endl;// 計算過程:// 1010// ^ 0110// ------// 1100 結果為12return 0;
}
應用場景:
- 數據加密與解密:簡單的異或加密算法中,通過將數據與一個密鑰進行異或操作來加密數據,解密時再次與相同的密鑰異或即可還原數據。
- 交換兩個數:不使用臨時變量交換兩個整數的值。例如:
#include <iostream>int main() {int a = 5;int b = 7;a = a ^ b;b = a ^ b;a = a ^ b;std::cout << "a = " << a << ", b = " << b << std::endl;return 0;
}
- 利用了異或操作的特性, a ^ b ^ b 等于 a , a ^ b ^ a 等于 b ,從而實現了兩個數的交換。
4. 按位取反( ~ )
- 運算規則:對一個整數的二進制表示中的每一位進行取反操作,即將 0 變為 1 , 1 變為 0 。
- 示例代碼:
#include <iostream>int main() {int num = 5; // 二進制: 0000 0101int result = ~num;std::cout << "~" << num << " = " << result << std::endl; // 計算過程:// 原: 0000 0101// 取反: 1111 1010 // 在有符號整數中,這是 -6 的補碼表示return 0;
}
應用場景:
- 創建掩碼:通過對特定值進行按位取反可以生成用于位操作的掩碼。例如,要創建一個除了第 3 位為 0 其余位都為 1 的掩碼,可以對 1 << 3 (即 0000 1000 )進行按位取反,得到 1111 0111 。
- 在特定算法中反轉位模式:某些加密算法或數據處理算法中可能需要對數據的位模式進行反轉。
5. 左移( << )
- 運算規則:將一個整數的二進制表示向左移動指定的位數,右邊空出的位用 0 填充。每左移一位,相當于該數乘以 2 (在不溢出的情況下)。
- 示例代碼:
#include <iostream>int main() {int num = 5; // 二進制: 0000 0101int result = num << 2;std::cout << num << " << 2 = " << result << std::endl; // 計算過程:// 原: 0000 0101// 左移2位: 0001 0100 結果為20return 0;
}
應用場景:
- 快速乘法:當需要將一個數乘以 2 的冪次方時,使用左移操作比乘法操作更高效。例如, a * 8 可以寫成 a << 3 ,因為 8 = 2^3 。
- 設置標志位:通過左移操作可以將 1 移動到特定位置來設置標志位。例如, 1 << 5 得到 0010 0000 ,可用于設置第 5 位的標志。
6. 右移( >> )
- 運算規則:將一個整數的二進制表示向右移動指定的位數。對于無符號整數,左邊空出的位用 0 填充;對于有符號整數,如果原數為正數,左邊空出的位用 0 填充,如果原數為負數,左邊空出的位用符號位(即最高位)填充,這稱為算術右移。每右移一位,相當于該數除以 2 (向下取整)。
- 示例代碼(無符號整數):
#include <iostream>int main() {unsigned int num = 20; // 二進制: 0001 0100unsigned int result = num >> 2;std::cout << num << " >> 2 = " << result << std::endl; // 計算過程:// 原: 0001 0100// 右移2位: 0000 0101 結果為5return 0;
}
- 示例代碼(有符號整數):
#include <iostream>int main() {int num = -20; // 二進制補碼: 1110 1100int result = num >> 2;std::cout << num << " >> 2 = " << result << std::endl; // 計算過程:// 原: 1110 1100// 右移2位: 1111 1011 結果為 -5return 0;
}
應用場景:
- 快速除法:當需要將一個數除以 2 的冪次方時,使用右移操作比除法操作更高效。例如, a / 4 可以寫成 a >> 2 ,因為 4 = 2^2 。
- 提取特定部分:可以通過右移操作將需要的位移動到最低位,然后通過與操作提取。例如,要獲取一個整數二進制表示的低 4 位,可以先右移 n 位( n 為需要丟棄的高位數),然后與 0x0F 進行與操作。