one’s & two’s complement number system

在計算機的底層是用0、1來表示資料,因此人類的符號系統要透過電腦表示就需要做編碼轉換,ASCII、Unicode等都是做此用途,而在(整數)數字系統的編碼,比較常見的有

  • ASCII (將數字用ASCII表示)
    • -200 => 2D 32 30 30
  • BCD
    • 189 => 01 08 09 (packed BCD)
  • one’s complement (assume 8-bit size)
    • 0 => 0000 0000b
    • -0 => 1111 1111b (unsigned = 255)
    • -1=> 1111 1110b (unsigned = 254)
    • 127 => 0111 1111b
    • -127=> 1000 0000b (unsigned = 128)
  • two’s complement (assume 8-bit size)
    • 0 => 0000 0000b
    • -1 => 1111 1111b
    • 127 => 0111 1111b
    • -127 => 1000 0001b

unsigned integer的表示就是很單純的2進位轉換,但當考慮到負數的時候,就有不同的表示法,最直覺的想法就是加一個sign bit

例如:
127 = 0111 1111b
-127 = 1111 1111b
2 = 0000 0010b
-2 = 1000 0010b

但是這樣子的表示法在處理數字運算的時候,硬體的實作上比較麻煩且沒有一致性
因此產生了one’s complement / two’s complement表示法(signed number representation)

one’s complement是對positive number bit pattern做negate運算(1->0, 0->1),two’s complement是在one’s complement的基礎上再offset 1 (negate 後 +1)

one’s complement: 1->0, 0->1 負號數字是 個別bit取1的補數
two’s complement: M + (-M) = 2^N 負號數字是取2^N的補數

現代的計算機在整數表示是以two’s complement,one’s complement system在早期的PDP-1、CDC6600等機器上使用,他的問題主要是整數數字0有兩種可能的表示法

one's complement calculations:
-1 + 1 ==> 1111b 1111b ==> -0 (true for test zero)
 6 - 19 => 6 + (-19) => 0000b 0110b + ~(0001b 0011b)
  0000 0110
+ 1110 1100
--------------------
  1111 0010  negative -> ~(0000 1101) = -13

看起來似乎one’s complement很單純就是按照一般bit加法運算的規則,並且相減就當成 加negate number,但事實上並不是這樣,有carry bit時需要特別處理

 one's complement calculations with carry bit: 
   6 + (-0) =>
  0000 0110
+ 1111 1111
--------------------
1 00000101    5 with carry bit  (expect 6)

在1’s complement運算,碰到carry bit,需要執行 end-around borrow,就是把1補到最後一個bit

  1 00000101    5 with carry bit 
=
    00000101
+          1
--------------------
    00000110 = 6

再看

   19 - 3 =>
  0001 0011
+ 1111 1100
--------------------
1 0000 1111 = 15 with carry bit (expect 16) => should wrap around
  0000 1111
+         1
--------------------
  0001 0000 = 16

在硬體上就是一般的加法器再拉一個carry bit做加法就可以達成

跟two’s complement比,運算要另外處理carry bit,並且浪費了 negative zero的表示空間,two’s complement巧妙地做了offset 1解決上述兩個問題,addition, subtraction, multiplication 運算上跟unsigned integer方法一樣,
carry一律discard

127 = 01111 1111b
-127 = ~(0111 1111b) + 1
     =   1000 0000b + 1
     =   1000 0001b
   15 - 5
  0000 1111
+ 1111 1011
-------------------
1 0000 1010 = 10 with carry

  -2 + (-3)
  1111 1110
+ 1111 1101
-------------------
1 1111 1011 = -5 with carry

two’s complement 的操作也可以這樣理解:

可以想像成 (以1 byte為例) 0 到 + 127 , -128 到 -1 的有限數字循環,
0000 0000   0
0000 0001   1
...
0111 1111   127
1000 0000   -128
...
1111 1110   -2
1111 1111   -1
每一個數字 +1 就會進到下一個數字
-N 代表退後 N步,也就是往前 256 - N步
  1 0000 0000 - N 
= 1 + (1111 1111) - N 
= 1 + (1111 1111 - N) 
= 1 + ~N

因此 減N可以理解成往前 1 + ~N 步 也就是 加 1 + ~N
special case:
-N (負N) = 0 – N = 0 + 1 + ~N = 1 + ~N 也就是negate 再加1

This entry was posted in C Language. Bookmark the permalink.

Leave a Reply