在計算機中是以二進制的形式存儲的,即機器數其特色是:
原碼:
相似於機器碼,是有符號位的。
反碼:
正數的反碼等於其原碼自己。
負數的反碼等於其原碼的符號位保持不變,其他位取反。
補碼:
正數的補碼=正數的反碼=正數的原碼
負數的補碼=其反碼+1
例如:8位的±3ide
有符號數 | 數值 | 原碼 | 反碼 | 補碼 |
---|---|---|---|---|
正數 | +3 | 0000 0011 | 0000 0011 | 0000 0011 |
負數 | -3 | 1000 0011 | 1111 1100 | 1111 1101 |
對於計算機而言,加減乘除是最基本的運算,所以要設計的要儘可能的簡單,若是讓計算機來辯解符號位,就會增長設計的複雜度,所以工程師設計出了一個方法:讓符號位也參加算術的運算, 好比:3-3設計爲3 + (-3),於是對於機器而言只有加法沒有減法運算;因而就要用到反碼和補碼。編碼
數字 | 二進制 |
---|---|
127 | 0111 1111 |
126 | 0111 1110 |
... | ...... |
1 | 0000 0001 |
0 | 0000 0000 |
-1 | 1000 0001 |
... | ...... |
-127 | 1111 1111 |
-128 | 1000 000 |
floor(x/y) 爲地板除法,即所得實數向下取整 或 向上取整-1 floor(2/3) = 0 or [0.667] - 1 = 1 - 1 = 0 floor(-2/3) = -1 or [-0.667] - 1 = 0 - 1 = -1 若是兩個整數除以M所獲得的餘數相等,則稱該兩個整數對於mo模M是相等的。 如:5 % 3 = 2 ;8 % 3 = 2,則5 和8對於模3是相等的。 用數學公式表示爲:5 ≡ 8 (mod 3) 正數同餘數:很好理解 5 % 3 = 2 負數同餘數:X % Y = X - Y * (floor(X / Y)) 如:-2 % 3 = -2 - 3 * floor(-2/3) = -2 - 3 * (-1) = 1 如何判斷一個整數和一個負數是否同餘數呢(假設分母爲M)? 正數A = M - |負數B|,則異號的A和B對於M同餘數。如-2和1對於3同餘數。 同餘數具備如下性質: 1. 反自身性: X ≡ X % M,即自身的餘數與自身同餘數 2. 線性性質: x1 ≡ y1 % M && x2 ≡ y2 % M,則 x1 ± x2 ≡ (y1 ± y2) % M x1 * x2 ≡ (y1 * y2) % M 反碼:實際上是其一個同餘數 如:1的反碼:0000 0001->0000 0001 將其當作原碼爲:1 -1的反碼:1000 0001->1111 1110 將其當作原碼(去掉符號位):2^7 - 1 -1 = 126 則負數與其反碼(去掉符號位對應的機器碼)爲同餘數,即找到了負數的同餘數 模爲正數+|負數|,如126 + |-1| = 127 2 - 1 ≡ (2 + 126) % 127 在反碼的基礎上+1,是增長了模的大小,即增長了轉一圈的大小, 所以用補碼所能表示的範圍爲-128,128,因爲0的存在,因此正數往前移動一個單位 即補碼所能表示的範圍爲:[-128,127]。
左移和右移是在補碼上進行操做的,通常會有運算位移和邏輯位移。 運算位移:符號位不變 邏輯爲宜:符號位發生變化,用0填充空的位 例如:
int main() { int a = 3; int b = -3; // 數字 原碼 反碼 補碼 右移 // 3 0000 0011 0000 0011 0000 0011 0000 0001 // 去掉最右邊的(1) // -3 1000 0011 1111 1100 1111 1101 1111 1110 // 去掉最右邊的 // 補碼->原碼:1111 1110 -1 獲得反碼:1111 1101 取反獲得原碼:1000 0010(-2) printf("a = %d and a >> 1 = %d\n", a, a >> 1); printf("b = %d and b >> 1 = %d\n", b, b >> 1); return 0; }
按位與:& # 同爲1則爲1,反之爲0 按位或:| # 只要有一個爲1,則爲1,反之爲0 按位異或:^ # 只要不一樣(一個1和一個0)則爲真,相同爲0
int main() { int num1 = 3; int num2 = 5; int num3 = -3; int num4 = -5; printf("正數的位操做\n"); printf("%d & %d = %d\n", num1, num2, num1 & num2); // 1 printf("%d | %d = %d\n", num1, num2, num1 | num2); // 7 printf("%d ^ %d = %d\n", num1, num2, num1 ^ num2); // 6 printf("負數的位操做\n"); printf("%d & %d = %d\n", num3, num4, num3 & num4); // -7 printf("%d | %d = %d\n", num3, num4, num3 | num4); // -1 printf("%d ^ %d = %d\n", num3, num4, num3 ^ num4); // 6 printf("負數和正數的位操做\n"); printf("%d & %d = %d\n", num1, num4, num1 & num4); // 3 printf("%d | %d = %d\n", num1, num4, num1 | num4); // -5 printf("%d ^ %d = %d\n", num1, num4, num1 ^ num4); // -8 return 0; }
解析:
原碼 反碼 補碼
num1 0000 0011 0000 0011 0000 0011
num2 0000 0101 0000 0101 0000 0101
num3 1000 0011 1111 1100 1111 1101
num4 1000 0101 1111 1010 1111 1011設計
num1 & num2 = 3 & 5 = 0000 0001 補碼=反碼=原碼=1
num1 | num2) = 3 | 5 = 0000 0111 補碼=反碼=原碼=7
num1 ^ num2) = 3 ^ 5 = 0000 0110 補碼=反碼=原碼=63d
num3 & num4) = -3 & -5 = 1111 1001 -> 1111 1000 -> 1000 0111 = -7
num3 | num4) = -3 | -5 = 1111 1111 -> 1111 1110 -> 1000 0001 = -1
num3 ^ num4) = -3 ^ -5 = 0000 0110 -> 補碼=反碼=原碼=6
num1 & num4) = 3 & -5 = 0000 0011 -> 補碼=反碼=原碼=3
num1 | num4) = 3 | -5 = 1111 1011 -> 1111 1010 -> 1000 0101 = -5
num1 ^ num4) = 3 ^ -5 = 1111 1000 -> 1111 0111 -> 1000 1000 = -8code
int main() { // 進行3次的按位異或操做,並將結果分別保存到第一 第二 第一個操做變量中 int a = 3; int b = 4; printf("交換以前:a = %d and b = %d\n", a, b); a = a ^ b; b = a ^ b; a = a ^ b; printf("交換以後:a = %d and b = %d\n", a, b); return 0; }
可是用加減法也能夠
缺點:可能會溢出(當兩個較大的數相加時)blog
int main() { // 進行1次加法,兩次減法操做,並將結果分別保存到第一 第二 第一個操做變量中 int a = 3; int b = 4; printf("交換以前:a = %d and b = %d\n", a, b); a = a + b; b = a - b; a = a - b; printf("交換以後:a = %d and b = %d\n", a, b); return 0; }
6.2 應用-統計一個整數在內存存放的二進制中有多少個1內存
int main() { // 由於任何一個數的二進制的補碼的最後一位是1,則其和1進行按位與運算則爲1,反之爲0,進行1..31次位移 int i; int num; int count = 0; scanf("%d", &num); for (i = 0; i < 32; i++) // 由於num佔四個字節,32個比特位 { if (1 == ((num >> i) & 1)) { count++; } } printf("%d\n", count); return 0; }
或者數學
int main() { int num; scanf("%d", &num); int count = 0;//計數 while (num) { count++; num = num & (num - 1); } printf("二進制中1的個數 = %d\n", count); return 0; }