計算機二進制中的原碼,反碼,補碼


公號:碼農充電站pro
主頁:https://codeshellme.github.io

計算機最基本的工作是處理數據,而數據的最底層表現形式是二進制,並非是我們人類熟悉的十進制。可以這么認為,計算機其實是很“笨的”,它只理解二進制數據。

今天,主要介紹計算機是怎樣做加減運算的。你可能會想,加減運算?這么簡單的事情,還用介紹?也許還真不是你想的那樣。

計算機的運算是由CPU 完成的,而CPU 只會做加法運算,不會做減法運算,那計算機怎樣完成減法工作呢?

1,二進制數

我們先來看看二進制數。

二進制數是由0,1 組成的,比如:

  • 十進制的5,用二進制表示是 101。
  • 十進制的7,用二進制表示是 111。

數字由正數和負數組成。為了表示正負數,計算機中就有了有符號數無符號數之分:

  • 無符號數:英文為 unsigned,只能表示正數。
  • 有符號數:英文為signed,即能表示正數,又能表示負數。

C/C++ 語言中的數字有有符號數無符號數之分。
Java 語言所有的數字都是有符號數

假如,我們用 4 位二進制,來表示無符號數,也就是只表示正數,能表示的范圍是 0 到 15,轉換關系如下表:

十進制數 二進制數 十進制數 二進制數
0 0000 8 1000
1 0001 9 1001
2 0010 10 1010
3 0011 11 1011
4 0100 12 1100
5 0101 13 1101
6 0110 14 1110
7 0111 15 1111

有符號數,即要表示正數,也要表示負數。

要用二進制表示有符號數,需要用二進制的最高位來表示符號,0 表示1 表示。所謂的最高位,也就是最左邊那一位。

用 4 位二進制,來表示有符號數,能表示的范圍是 -8 到 7,轉換關系如下表:

十進制數 二進制數 十進制數 二進制數
0 0000 -8 1000
1 0001 -1 1001
2 0010 -2 1010
3 0011 -3 1011
4 0100 -4 1100
5 0101 -5 1101
6 0110 -6 1110
7 0111 -7 1111

上表中的最高位的符號位,已標紅。

要注意,對於有符號的4 位二進制 ----1000 不是 -0,而是 -8

可以總結出,對於N 位的二進制數:

  • 無論是表示有符號數還是無符號數,都能表示2^N 個數字。
  • 若用於表示無符號數,則能表示的范圍是 [0, 2^N - 1]
  • 若用於表示有符號數,則能表示的范圍是 [-2^(N-1), 2^(N-1) - 1]
    • 需要注意,在有符號數中,對於符號位是 1,后面 N-1 位全是 0,這種情況表示的是 -2^(N-1)(也就是所能表示的最小值),而不是 -0
    • 實際上是將-0 這種情況解釋成了最小值,否則就會出現 +0-0 兩個0

2,二進制原碼

上面介紹到的二進制就是原碼形式。

原碼就是除符號位外的其他位,保存該二進制數的絕對值。

用原碼進行加法計算

計算機中的數字運算都會先轉成二進制數再進行計算。

我們用原碼來計算加法,用4 位二進制數來計算 3 + 2,過程如下:

在這里插入圖片描述

可以看到,用原碼計算加法是沒有問題的。

用原碼進行減法計算

我們再用原碼來計算減法,因為CPU 只會計算加法,所以計算減法時,會將減法轉換成加法。

比如,用4 位二進制數來計算計算 3 - 2,會將其轉換成 3 + (-2), 過程如下:

在這里插入圖片描述

可以看到 3-2 計算出來的結果是 -5,顯然是錯誤的。

所以,用二進制原碼來計算減法是行不通的。實際上,計算機計算減法用的是補碼

在介紹補碼之前,我們先來看下什么是溢出

3,數字溢出

計算機中數字的表示是需要內存空間的,不同類型的數字所能占用的空間是不一樣的。

比如,在Java 語言中short 類型占用 2 個字節,int 類型占用 4 個字節。

一個字節等於 8 位。

既然空間大小是有限制的,所以計算機中的數字也是有范圍的,即上限下限,如果數字超出限制,就會產生溢出。超出上限叫上溢出,超出下限叫下溢出而溢出的部分會直接被舍去

就像我們在上文中介紹的,對於N 位二進制有符號整數,所能表示的范圍是 [-2^(N-1), 2^(N-1) - 1]

由於溢出的部分會被舍去,那么最大值加1,將發生上溢出,變為最小值;最小值減1,將發生下溢出,變為最大值。

在這里插入圖片描述

我們用Java 中的int 類型來驗證,Javaint 類型的最大、最小值分別是:

  • 最大值:Integer.MAX_VALUE,是 2147483647
  • 最小值:Integer.MIN_VALUE,是 -2147483648

用下面代碼驗證:

System.out.println(Integer.MAX_VALUE + 1 == Integer.MIN_VALUE); // true
System.out.println(Integer.MIN_VALUE - 1 == Integer.MAX_VALUE); // true

這兩行代碼的輸出均為true,說明最大值加1 變為最小值,最小值減1 變為最大值。

所以,在計算機中,只要一個整數的類型確定了,那么它所能占用的內存空間大小也就確定了,從而它所能表示的數字范圍也就確定了。那么不管給這個整數加多大的數字,或者減多大的數字,最終的結果都只能在這個范圍內旋轉。

就像表盤一樣,當表針走過最大值的時候,就變成了最小值。

在這里插入圖片描述

同樣,這也等同於數學中的取余運算。只要分母確定了,不管分子是多大,或者多小的數字,最終的結果也都是在一個確定的范圍之內。

比如我們對十進制5 進行取余計算,那么最終的結果都是在[0, 4] 范圍之內,如下:

  • 0 % 5 = 0
  • 2 % 5 = 2
  • 397 % 5 = 2
  • 99999 % 5 = 4

可以總結出,對數字N 進行取余,N >= 2 且為整數,那么結果都在 [0, N-1] 范圍之內。

4,二進制反碼與補碼

知道了溢出,就可以介紹CPU 如何計算減法了。CPU 的減法運算使用了二進制補碼,補碼實際上就是采用了溢出的原理。

我們直接給出反碼與補碼的定義:

  • 反碼定義:正數的反碼等於其原碼,負數的反碼是其原碼除符號位外,按位取反。
  • 補碼定義:正數的補碼等於其原碼,負數的補碼是其反碼加1。

比如下面的幾個數字:

十進制數 2 3 -2 -5
二進制原碼 0010 0011 1010 1101
二進制反碼 0010 0011 1101 1010
二進制補碼 0010 0011 1110 1011

我們用4 位二進制補碼來計算 3+(-2),如下:

在這里插入圖片描述

最高位的1 發生上溢出,直接被舍去,所以結果是正確的。

所以,要記住,真實的計算機中的二進制是用補碼表示的,而不是原碼

5,總結

本篇文章主要介紹了:

  • CPU 只能做加法,不能做減法,減法要轉成加法做計算。
  • 二進制數字有三種表示方式:
    • 原碼:除符號位外的其他位,保存該二進制數的絕對值。
    • 反碼:正數的反碼等於其原碼,負數的反碼是其原碼除符號位外,按位取反。
    • 補碼:正數的補碼等於其原碼,負數的補碼是其反碼加1。
  • 計算機中的數字采用二進制補碼表示,而不是原碼表示。
    • 補碼采用了溢出的原理。
  • 計算機中的數字是有范圍限制的,超出限制會發生溢出。
    • 超出上限叫做上溢出。最大值加1會發生上溢出,變為最小值。
    • 超出下限叫做下溢出。最小值減1會發生下溢出,變為最大值。

(本節完。)


推薦閱讀:

決策樹算法-理論篇

決策樹算法-實戰篇

朴素貝葉斯分類-理論篇

設計模式之高質量代碼

如何高效使用VIM


歡迎關注作者公眾號,獲取更多技術干貨。

碼農充電站pro


免責聲明!

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



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