原碼、反碼、補碼、移碼和數值計算


歡迎訪問我的個人站點,老廖的個人博客

前言

  計算機的數值編碼和運算應該是本科一年級就會學習的基礎知識。從軟件開發這個角度來說,很多時候這些知識沒有在開發過程中得到有效的利用和實踐。
  不巧,最近在做的一個項目,常常需要從補碼的角度考慮數值表示和相關關系。因此,也就趁此機會簡單的寫一寫。

數值表示

真值,機器數

  機器數就是數值在計算機中的二進制表示,數值有正負之分,因此機器數用最高位來表示數值的符號,0 代表正數,1 代表負數。根據表示方法不同,機器數分為:原碼、反碼、補碼、移碼等。

  真值就是帶正負的真實十進制值。

原碼

  原碼很簡單,就是最高位作為符號位,其余位表示真值的絕對值。

反碼

  正數的反碼就是 其原碼本身負數的反碼則是 原碼除符號位外 ,其余位取反。

  反碼的運算方法為 循環進位,即 最高位的進位要加到最低位來。如:8位為例:11111110(-1) + 11111110(-1) = 11111100 + 1 = 11111101(-2)

  產生的原因: 計算機的所有計算本質上都是加法,然而若正負值相加時讓計算機判斷符號位來選擇運算方式會使得加法電路設計變得復雜,但是若直接讓符號位參與運算則會帶來 1 + (-1) = -2 (8位為例:00000001 + 10000001 = 10000010) 等等問題。反碼的提出就是為了解決 符號位參與運算 的加法問題。

補碼

  反碼是一個不完美的解決方案,有不近人意的問題。比如: 8位為例,00000000 和 11111111 都可以表示零,一個 +0,一個 -0。為了解決這個問題,引入補碼來表示數值。

  正數 的補碼是 其原碼本身負數 的補碼是 其反碼 + 1 。這個設計使得加法運算滿足一個等式:a(補) + b(補) = (a + b)(補) ,由此,不管符號為何,直接參與運算都能得到正確的結果。目前補碼是最佳的解決方案。 現行的編程語言,都是用補碼來表示數值和進行算數運算

  設計的原理

  設計補碼的原因是要解決表示反碼表示 0 的問題。

  計算機表示數值是有范圍的,取決於用多少 bit 來表示,當加法計算超過了 bit 的長度,就會產生溢出 ,溢出的部分消失,這和 模運算 恰好一致。比如上限是 255,則 5 mod 255 = 5,260 mod 255 = 5。 我們可以說,在8位的情況下,5 和 260 是等價的。同理,在負數的情況下 (反向理解),我們可以用負數的模除值等價負數,這樣相當於用正數 (負數的模除值為正數) 表示了負數。

  根據模運算的運算法則,(a+b) mod m = [(a mod m) + (b mod m)] mod m ,在 m 為最大值的情況下,數 mod m 的值就是 的反碼。反碼 + 1 等價於 m + 1 並不破壞等式,同時保證了符號的正確和 0 的正確表示。

移碼

  移碼則是 把補碼的符號位取反,常常用在 浮點 數值的二進制表示中。

浮點的二進制表示

  浮點數的二進制表示比較特殊,整個二進制位分為三個部分:

類型 符號位 階碼 尾數 總位數
float 1 8 23 32
double 1 11 52 64

  設階碼為 e,尾數為 m,則浮點的值為: $ m * 2^e\quad $,其中階碼是用移碼表示。

  用移碼表示的原因在於,階碼作為指數是有正負的,用移碼表示能在 不考慮符號 的情況下比較浮點數大小。如:(8位情況下) 11111111 是最小值,00000000 是 0 如果不考慮符號,則 11111111 > 00000000 顯然不方便。而用移碼則:11111111 -> 00000001,00000000 -> 10000000 大小一下就比較出來了。

  階碼確定浮點數的取值范圍,尾數確定浮點數的精度

數學運算

  上面也說到了,計算機的數值運算是 通過補碼完成的 。兩個數的補碼進行加法運算得到最終的值。若要顯示成人類可讀,則通過補碼的定義轉換成真值。

整型溢出

  數值運算時極易發生整型溢出,直接通過補碼的變化來判斷溢出是否發生是非常准確的。

  我們把最高位 (符號位) 緊挨着的那一位稱為最高有效位,那么,若符號位和最高有效位只有一個發生了進位,則出現了整型溢出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM