1. 程式人生 > >數的機器碼錶示——徹底弄清什麼是原碼、反碼、補碼、移碼

數的機器碼錶示——徹底弄清什麼是原碼、反碼、補碼、移碼

## 數的機器碼錶示 @[toc] 為了妥善的處理資料運算過程中符號位的問題,於是就產生了**把符號位和數值位一起編碼**起來**表示相應的數**的各種表示方法。例如我們熟悉的原碼、反碼、補碼、移碼等。通常將未經**編碼的數稱為真值**,編碼後的數稱為**機器數或者機器碼**。 - **真值的形式**:**正、負號加某進位制數絕對值的形式**,即**數的實際值**。如`+3`,`-5`等 - **機器數的形式**:真值按某種編碼方式進行編碼後的數值,即**真值在機器中的表示**,稱為機器數,一般可以分為 **無符號數和有符號數**兩種如:**X=01011** ,**Y=11011** ### 原碼 #### 定點整數 若定點整數的原碼形式位$x_n x_{n-1}\cdot\cdot\cdot x_1x_0$,其中$x_n$為符號位,則原碼錶示的定義為: $$ x_{[原]}=\left\{\begin{matrix} x & 0 \leqslant x<2^n \\ 2^n-x=2^n+|x| & -2^n0000 - -0 - 1000-->1111 #### 定點小數 假設定點小數的反碼形式為$x_s.x_1x_2...x_n$(**實際上小數點是不儲存的**),其中$x_s$代表符號位。則反碼的定義為: $$ x_{[反]}=\left\{\begin{matrix}x & 0 \leqslant x<1 \\ 2-2^{-n}+x=2-2^{-n}-|x| & -1 我們既可以順時針調也可以逆時針調。也就是說我們$5-2$和$5+10$的效果是一樣的。 而把這種思想引入到計算機中,不就可以把減法轉為加法了嗎? $$ 5-2=5+10(MOD 12) \\5+(-2)=5+10(MOD 12)\\ -2=10(MOD 12) $$ 在上面的式子中,在**模為12的情況下,-2的補碼就是10**。一個負 數用其補碼代替,同樣可以得到正確的運算結果。 那什麼是**模**呢? >
計算機中運算器、暫存器、計數器都有一定的位數,不可能容納無限大的任意數。當運算結果超出實際的最大表示範圍, 就會發生溢位,此時所產生的**溢位量**就是**模(module)** 可以把模定義為一個**計量器的容量**。 如一個**八位計數器**,`0000 0000~1111 1111`,它表示的範圍就是`[0,255]`。當計數器表示為`1111 1111` 時,如果**計數器再加一**,那麼此計數器就會溢位。計數器上的數值會變成**0000 0000**.而此計數器的溢位量就是**256**. 假設此計數器表示一個定點小數,它表示的範圍就是[0,2-$2^{-8}$]。當表示最大的時候,計數器值為**1.111 1111**當**計數器加一**的時候,那麼計數器就會清零,變為**0.0000000**。那麼此此定點小數的溢位量就是**2**. 從上面我們可以推匯出,一個`n+1`位定點整數$x_n x_{(n-1)} ... x_2 x_1 x_0$,它的溢位量為$2^{n+1}$,所以模為$2^{n+1}$。 任意一個定點小數$x_s.x_1x_2...x_n$,它的溢位量是2,所以模為2。 而**在計算機中**,機器能表示的**資料位數是固定**的, 其**運算都是有模運算**。若運算結果**超出**了計算機所能表示的數值範圍, 則只**保留它的小於模的低n+1位**的數值,**超過n+1 位**的高位部分就**自動捨棄**了。 下面我們來引入**補碼的定義**: #### 定點整數 定點整數的補碼形式為$x_n x_{n-1}\cdot\cdot\cdot x_1x_0$,其中$x_n$為符號位,補碼錶示的定義為: $$ x_{[補]}=\left\{\begin{matrix}x & 0 \leqslant x<2^n \\ 2^{n+1}+x=2^{n+1}-|x| & -2^n \leqslant x\leqslant0\end{matrix}\right. $$ 在上式中,**x代表的是真值**。 例如,$x=+7$,化為二進位制表示為$x=+0111$;$x_{[補]}=0111$。 $x=-7$,化為二進位制表示為$x=-0111$;$x_{[補]}=2^4+(-0111)=10000-0111=1001$。 我們可以總結出來: - 對於正數$x=+x_{n-1}...x_1x_0$,它的補碼是它自己本身,常常在最高位前面補0,代表它是一個正數。 - $x_{[補]}=0_nx_{n-1}...x_1x_0$ - 對於0,根據補碼的定義: - $+0=+0_{n-1}...0_10_0$ - 此時正0的補碼為$+0_{[補]}=0_n0_{n-1}...0_10_0$ - $-0=-0_{n-1}...0_10_0$ - 此時負0的補碼為$-0_{[補]}=(10_n0_{n-1}...0_10_0-0_{n-1}...0_20_10_1)mod(10_n0_{n-1}...0_10_0)=0_n0_{n-1}...0_10_0$ 由此可見,**零的補碼是唯一的**,沒有+0和-0之分。 - 對於負數$x=-x_{n-1}...x_1x_0$ ​ 常常可以通過把**數值位按位取反**,然後**末位加一來計算負數的補碼**。 - 如求$-127$的補碼 - $[-0111 1111]_{[補]}=10000 0000-01111111 $ - $10000000=11111111+1$ - $[-0111 1111]_{[補]}=11111111-01111111+1$,這一步剛剛說明了上面的計算方法的原理。 $-128$的補碼的求法。 - -128=-1000 0000 - $[-10000000]_{[補]}=11111111-10000000+1=10000000$ 其實還有一個更為漸變的補碼的求法:**從右到左遇到第一個 1 的前面各位取反。**也就是從右向左,遇到1之前,還保持原樣。遇到1之後,各位取反。以-126為例:$-126=-01111110$ [-01111110]的補碼位**100000**10,10是不變的。而黑體部分則是取反的。 對於補碼的取值範圍,10000000-11111111——- $-128$~$-1$ 00000000~011111111 ——0~127 推廣到n位數,則**補碼的範圍**就是[$-2^{n-1},2^{n-1}-1$] #### 定點小數 定點小數的補碼形式為$x_s.x_1x_2...x_n$(**實際上小數點是不儲存的**),其中$x_s$代表符號位。則補碼的定義為: $$ x_{[原]}=\left\{\begin{matrix}x & 0 \leqslant x<1 \\ 2+x=2-|x| & -1\leqslant x\leqslant0\end{matrix}\right. $$ 在上式中,x代表的是真值。 例如,$x=+0.875$,化為二進位制表示為$x=+0.111$;$x_{[補]}=0.111$。 $x=-0.875$二進位制表示為$x=-0.111$;$x_{[補]}=10.000-(0.111)=1.001$。 我們可以總結出來: - 對於正數$x=+0.x_{n-1}...x_1x_0$,它的補碼是它自己本身,常常在最高位前面補0,代表它是一個正數。(注意,**前面的0.實際上是不儲存的**,也就是實際最高位是$x_{n-1}$) - $x_{[原]}=0.x_{n-1}...x_1x_0$ - 對於0,根據補碼的定義: - $+0=+0.0_{n-1}...0_10_0$ - 此時正0的補碼為$+0_{[補]}=0.0_{n-1}...0_10_0$ - $-0=-0.0_{n-1}...0_10_0$ - 此時負0的補碼為$-0_{[補]}=(10.0_{n-1}...0_10_0-0.0_{n-1}...0_10_0)mod(10.0_{n-1}...0_10_0)=0.0_{n-1}...0_10_0$ 也就是說**,0的補碼是唯一的**。 - 對於負數 按位取反,末位加一,和定點整數一樣。雖然我們看起來有一個小數點,但是實際上小數點是不儲存的。 定點小數的補碼的範圍,0000~0111--> [0,0.875] 1000~1111--> [-1,-0.125] 擴充套件到n位補碼 我們很容易求出它的範圍$[-1,1-2^{-(n-1)}] $ #### 補碼的運算 假設一個二進位制整數補碼有n+1位,$x_nx_{n-1}...x_2x_1x_0$,則補碼與真值的對應關係可以這麼表示: $X_{真值}=-2^n\times x_n+\sum_{0}^{n-1}2^ix_n$ 當該數為正整數的時候,$x_n$位變為0,$0x_{n-1}...x_2x_1x_0$, $X_{真值}=\sum_{0}^{n-1}2^ix_n$ 當該數為負整數的時候,$x_n$位變為1,$1 x_{n-1}...x_2x_1x_0$, $X_{真值}=-2^n+\sum_{0}^{n-1}2^ix_n$ - 以-127為例,-127=-01111111 - 它的補碼為10000001 - 從補碼求真值的過程:-10000000+1=-01111111-1+1=-01111111 上面我們說到過,計算機中用原碼進行加減運算是十分麻煩的,那麼我們來看一下用補碼來運算。 $126-127=0$ - 原碼 - $01111110-01111111$ - 我們需要比較數值位的絕對值大小,來決定符號位,1 - 1111111-1111110=0000001 - 10000001 假設把減法看成加法 - 01111110+(-01111111)=01111110+11111111=**1**00000001 - 1會溢位,那麼得到的結果就是00000001,按原碼錶示的化,真值為1,顯然是錯誤的。 - 補碼 - 01111110+10000001=11111111 - 轉換為真值後為-1。正確的 - 原理是這樣的 - 126-127=-1 - 126+(-127)=-1 - 126+129mod256=-1 - 這裡-127等效於129mod256 由此,我們可以看出,補碼實際上是**可以直接帶符號位運算的運算**的。 求相反數的補碼:**由[X]補求[-X]補** **帶符號位一起取反,然後末位加一** 如求我們**已知+127的補碼求-127的補碼**: - 01111111 - 10000001 ### 移碼 移碼通常用於表示浮點數的階碼。由於階碼是k位的整數,假設定點整數移碼形式為$e_ke_{k-1}...e_2e_1e_0$最高位為符號位是。移碼的傳統定義是: $[e]_移=2^k+e$ 上式中,e為真值,$2^k$為固定的偏移值常數。 **與[x]補的區別:符號位相反** | 真值 | 補碼 | 移碼 | | ---- | ------ | ------- | | -8 | 1000 | 0000 | | -7 | 1001 | 0001 | | -6 | 1002 | 0002 | | …… | ...... | ...... | | 0 | 0000 | 1000 | | +1 | 0001 | 1001 | | …… | ...... | ....... | | +7 | 0111 | 1111 | #### 移碼的表示 移碼錶示的機器數為數的真值在數軸上向右平移了 固定的偏移值。 如八位移碼: ![image-20200319141504001](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2doL2MxYXRhL2ltZ2JlZDIwMjAvaW1nL2ltYWdlLTIwMjAwMzE5MTQxNTA0MDAxLnBuZw?x-oss-process=image/format,png) #### 移碼的特點 - 在移碼中,最高位為0表示負數,最高位為1表示正數,這與**原 碼、補碼、反碼的符號位取值正好相反**。 - 移碼為全0時所對應的真值最小,為全1時所對應的真值最大! 因此,**移碼的大小直觀地反映了真值的大小,這將有助於兩個 浮點數進行階碼大小比較**。 - 真值0在移碼中的表示形式是**唯一**的,即:**[+0]移= [0]移= 100…00** - 移碼**把真值對映到一個正數域**,所以可將移碼視為無符號數, 直接按無符號數規則比較大小。 - 同一數值的移碼和補碼除最高位相反外,其他各位相同。 ### 原碼、反碼、補碼、移碼 取值範圍的一個比較 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4uanNkZWxpdnIubmV0L2doL2MxYXRhL2ltZ2JlZDIwMjAvaW1nLyVFNyVBNyVCQiVFNyVBMCU4MSVFNSU4RiU4RCVFNyVBMCU4MSVFNSU4RSU5RiVFNyVBMCU4MSVFOCVBMSVBNSVFNyVBMCU4MS5qcGc?x-oss-process=image/form