深入理解計算機中的原碼、補碼、反碼

平兄聊Java發表於2021-05-10

基本資料型別



計算機底層儲存資料時使用的是二進位制數字,但是計算機在儲存一個數字時並不是直接儲存該數字對應的二進位制數字,而是儲存該數字對應二進位制數字的補碼。所以接下來我們需要來了解一下原碼反碼補碼

  • 機器數:一個數在計算機的儲存形式是二進位制數,我們稱這些二進位制數為機器數,機器數是有符號,在計算機中用機器數的最高位存放符號位,0表示正數,1表示負數。
  • 機器數的真值:因為帶有符號位,所以機器數的形式值不等於其真值,以機器數1000 0111為例,其真正表示的值為-7,而形式值為135。將帶符號的機器數的真正表示的值稱為機器數的真值。

原碼

原碼的表示與機器數真值表示的一樣,即用第一位表示符號,其餘位表示數值,例如的十進位制的的正負1,用8位二進位制的原碼錶示如下:

【+1】= 原:[ 0000 0001 ]

【-1】= 原:[ 1000 0001 ]


反碼

反碼的表示方法為:

  • 正數的反碼是其原碼本身。
  • 負數的反碼是在其原碼的基礎上,符號位不變,其餘各位取反。

【+1】= 原: [ 0000 0001 ] = 反:[ 0000 0001 ]

【-1】 = 原:[ 1000 0001 ] = 反:[ 1111 1110 ]


補碼

補碼的表示方法為:

  • 正數的補碼是其原碼本身。
  • 負數的補碼是在其原碼的基礎上,符號位不變,其餘各位取反後加1(即在反碼的基礎上加1)。

【+1】= 原: [ 0000 0001 ] = 反:[ 0000 0001 ] = 補:[ 0000 0001 ]

【-1】 = 原:[ 1000 0001 ] = 反:[ 1111 1110 ] = 補:[ 1111 1111 ]


資料在計算機中的儲存形式

計算機實際只儲存補碼, 所以原碼轉換為補碼的過程,也可以理解為資料儲存到計算機記憶體中的過程:

在原、反、補碼中,正數的表示是一模一樣的,而負數的表示是不相同的,所以對於負數的補碼來說,我們是不能直接用進位制轉換將其轉換為十進位制數值的,因為這樣是得不到計算機真正儲存的十進位制數的,所以應該將其轉換為原碼後,再將轉換得到的原碼進行進位制轉換為十進位制數。(機器數包含符號位)


問題:為何使用原碼、反碼、補碼

我們上面說過,原碼、反碼、補碼的表示對於正數來說都是一樣的,而對於負數來說,三種碼的表示確是完全不同的,那大家是否會有個疑問:如果原碼才是我們人類可以識別並用於直接計算的表示方式,那為什麼還會有反碼和補碼?計算機直接儲存原碼不就完事了?

在解決這些問題前,我們先來了解計算機的底層概念,我們人腦可以很輕鬆的知道機器數的第一位是符號位,但對於計算機基礎電路設計來說判別第一位是符號位是非常難和複雜的事情,為了讓計算機底層設計更加簡單,人們開始探索將符號位參與運算,並且採用只保留加法的方法,我們知道減去一個數,等於加上這個數的負數,即:1-1 = 1 + (-1) = 0,這樣讓計算機運算就更加簡單了,並且也讓符號位參與到運算中去。

1.使用原碼運算

計算十進位制表示式:1-1 = 0;

1 - 1 = 1 + (-1)

= 原:[ 0000 0001 ] + 原:[ 1000 0001 ]

= 原:[ 1000 0010 ] = -2

如果用原碼錶示,讓符號位也參與計算,對於減法來說,結果是不正確的。這也是計算機內部在儲存資料時不使用原碼的原因,為了解決這一問題,出現了反碼。


2.使用反碼運算

計算十進位制表示式:1-1 = 0

1 - 1 = 1 + (-1)

= 原:[ 0000 0001 ] + 原:[ 1000 0001 ]

= 反:[ 0000 0001 ] + 反:[ 1111 1110 ]

= 反:[ 1111 1111 ] = 原: [ 1000 0000 ] = -0

通過計算我們發現用反碼計算減法,結果的真值部分是正確的。而唯一的問題出現在"0"這個特殊的數值上,雖然人們理解上+0和-0是一樣的,但是0帶符號是沒有任何意義的,而且會有[0000 0000]原和[1000 0000]原兩個編碼表示0。為了解決這一問題,出現了補碼。


3.使用補碼運算

1 - 1 = 1 + (-1)

= 原:[ 0000 0001 ] + 原:[ 1000 0001 ]

= 補:[ 0000 0001 ] + 補:[ 1111 1111 ]

= 補: [ 0000 0000 ] = 原: [ 0000 0000 ] = 0

這樣0用[0000 0000]表示,而以前出現問題的-0則不存在了,而且人們還發現可以用[1000 0000]表示-128,-128的推算過程如下

(-1) + (-127) = -128

= 原:[1000 0001] + 原:[ 1111 1111 ]

= 補:[ 1111 1111 ] + 補:[ 1000 0001 ]

= 補:[ 1000 0000 ]

注意:因為實際上是使用以前的-0的補碼來表示-128,所以-128並沒有原碼和反碼錶示,只要補碼是[1000 0000],其十進位制數值就為-128。


因為補碼能多儲存一個-128,而且在計算機底層中儲存的是補碼,所以在計算機中一個8位的二進位制數的儲存範圍是用補碼錶示的[-128,127],而不是用原碼或反碼錶示的[-127,127]。這也可以解釋為什麼計算機中一個位元組的取值範圍是[-128,127]。

這樣也能夠回答我們開始提出的問題了,原碼、反碼、補碼的使用,是人們為了讓符號位能參與運算並讓計算機底層運算更加簡單而設計出來的資料儲存表示方式。


更多精彩內容敬請關注微信公眾號:【平兄聊Java】

相關文章