原碼-反碼-補碼的理解

第一 機器碼和真值

在計算機中是以二進制的形式存儲的,即機器數其特色是:
  • 是有符號的(最高位表示,1:負數;0:整數)
  • 其他位表示真正的數字(即真值)

    第二 原碼 反碼 補碼

    原碼:
    相似於機器碼,是有符號位的。
    反碼:
    正數的反碼等於其原碼自己。
    負數的反碼等於其原碼的符號位保持不變,其他位取反。
    補碼:
    正數的補碼=正數的反碼=正數的原碼
    負數的補碼=其反碼+1
    例如:8位的±3ide

有符號數 數值 原碼 反碼 補碼
正數 +3 0000 0011 0000 0011 0000 0011
負數 -3 1000 0011 1111 1100 1111 1101

第三 反碼和補碼的做用

對於計算機而言,加減乘除是最基本的運算,所以要設計的要儘可能的簡單,若是讓計算機來辯解符號位,就會增長設計的複雜度,所以工程師設計出了一個方法:讓符號位也參加算術的運算, 好比:3-3設計爲3 + (-3),於是對於機器而言只有加法沒有減法運算;因而就要用到反碼和補碼。編碼

  • 若是使用反碼進行運算:
    3 - 3 = 3 + (-3)
    = 0000 0011 + 1000 0011 #原碼
    = 0000 0011 + 1111 1100 #反碼
    = 1111 1111 #反碼
    = 1000 0000 #原碼
    = -0
    當用反碼進行計算時,真值部分是對的,可是符號位帶有±,對於0而言是沒有正負的,若是用正負是沒有意義的。且會有兩個二進制來編碼0.
  • 使用補碼進行計算
    3 - 3 = 3 + (-3)
    = 0000 0011 + 1000 0011 #原碼
    = 0000 0011 + 1111 1100 #反碼
    = 0000 0011 + 1111 1101 #補碼
    = 0000 0000 #補碼
    = 0000 0000 #原碼
    = 0
    這樣0用0000 0000來表示,則-0則不存在啦,所以下列二進制分別表示8位的127:-128
數字 二進制
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

6.1 應用-不借助中間變量交換兩個數

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;
}
相關文章
相關標籤/搜索