電腦中數值和字元串怎麼用二進位表示?

来源:https://www.cnblogs.com/xiaoniuhululu/archive/2023/01/18/17059919.html
-Advertisement-
Play Games

作者:小牛呼嚕嚕 | https://xiaoniuhululu.com 電腦內功、JAVA底層、面試、職業成長相關資料等更多精彩文章在公眾號「小牛呼嚕嚕」 大家好,我是呼嚕嚕。我們都知道現代電腦採用 0 和 1 組成的二進位,來表示所有的信息。那大家是不是有時候會有這些疑問:為什麼電腦採用了 ...


作者:小牛呼嚕嚕 | https://xiaoniuhululu.com
電腦內功、JAVA底層、面試、職業成長相關資料等更多精彩文章在公眾號「小牛呼嚕嚕

大家好,我是呼嚕嚕。我們都知道現代電腦採用 0 和 1 組成的二進位,來表示所有的信息。那大家是不是有時候會有這些疑問:為什麼電腦採用了二進位?二進位是如何表示電腦的相關信息的?比如數字、字元串、聲音、圖片、視頻等等

進位

進位計演算法是一種常見的計算方式,常見的有十進位,二進位,十六進位

  1. 十進位

十進位,都是以0-9這九個數字組成,不能以0開頭, 逢十進一。
十進位是我們從小就潛移默化般學習的,我們大多數人擁有的手指或腳趾的數目就是10,天生讓我們適合十進位為基礎的數字系統

  1. 二進位

二進位,數字中只有 0 和 1,逢二進一

  1. 八進位

八進位,數字0-7,逢八進一

  1. 十六進位

十六進位,數字有 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F組成,逢十六進一。
其表示形式比較特殊,因為10~15不能用數字來展示,所以強制規定:10 用 A 表示、11 用 B 表示、12 用 C 表示、13 用 D 表示、14 用 E 表示、15用F表示

進位間的轉換

  1. R進位 → 十進位按權展開

  1. 十進位 → R進位整數小數分開處理
  • 整數部分的轉換方法是:“除基取餘,上右下左”。即用要轉換的十進位整數去除以基數R,將得到的餘數作為結果數據中各位的數字,直到餘數為0為止。上面的餘數(先得到的餘數) 作為右邊低位上的數位,下麵的餘數作為左邊高位上的數位。

  • 小數部分的轉換方法是:“乘基取整,上左下右”。即用要轉換的十進位小數去乘以基數R,將得到的乘積的整數部分作為結果數據中各位的數字,小數部分繼續與基數R相乘。以此類推,直到某步乘積的小數部分為0或已得到希望的位數為止。最後,將上面的整數部分作為左邊高位上的數位,下麵的整數部分作為右邊低位上的數位。

我們需要註意的是:在轉換過程中,可能乘積的小數部分總得不到0,即轉換得到希望的位數後還有餘數,這種情況下得到的是近似值

  1. 二進位轉八進位、十六進位

由於把二進位的三位看成一個整體就是八進位的數,二進位的四位也就是十六進位的數。通過這個規律,我們很容易地就能實現二進位與八進位、十六進位的相互轉換。
整數部分低向高每3或4位數用一個等值八/十六進位數替換,不足時高位補0小數部分高向低每3或4位數用一個等值八或十六進位數替換,不足時低位補0

  1. 八進位、十六進位 轉二進位

每一位數改成等值的3或4位二進位數,整數部分高位0省略;小數部分低位0省略

電腦為什麼使用二進位?

我們從小更熟悉十進位的運算,0、1、2、3、4、5、6、7、8、9十個數字,逢十進一。但是電腦中使用二進位,只有0和1兩個數字,逢二進一。

採用二進位的原因:

  1. 二進位在自然界中最容易被表現出來。自然界中二值系統非常多,電壓的高低、水位的高低、門的開關、電流的有無等等都可以組成二值系統。
  2. 電腦使用二進位和現代電腦系統的硬體實現有關。製造二個穩定態的物理器件容易,使得組成電腦系統的邏輯電路通常只有兩個狀態,即開關的接通與斷開。由於每位數據只有斷開與接通兩種狀態,因此二進位的數據表達具有抗干擾能力強、可靠性高的優點
  3. 二進位非常適合邏輯運算,可方便地用邏輯電路實現 算術運算

機器數和真值

機器數

一個數在電腦中的二進位表示形式, 叫做這個數的機器數、機器碼。
由於我們平時不僅使用的是正數,還有大量的負數,而電腦是無法識別符號"+","-", 所以電腦規定,用二進位數的最高位0表示正數,如果是1則表示負數。機器數是帶符號的

如果十進位中的數 +3 ,電腦字長為8位,轉換成二進位的機器數就是0000 0011。如果是-3,就是 10000011

真值

帶符號位的機器數對應的真正數值是 機器數的真值,我們知道機器數的第一位是符號位, 比如1000 0011直接轉換成十進位為131,但實際上最高位1 是負號,其真正的值為 [-3]

機器數的編碼形式有哪些?

原碼

原碼就是符號位加上真值的絕對值, 即用最高位表示符號, 其餘位表示值

比如如果是8位二進位:

  • [+1] = (0000 0001)原
  • [ -1] = (1000 0001)原

我們人類根據二進位的規則,可以一眼就明白原碼代表的數字,方便了人類

面試的時候有一個經典的問題:8位二進位數原碼的取值範圍是
我們只需將除了最高位,用來表示符號,其他位都是1,即[1111 1111 , 0111 1111],換算成十進位:[-127 , 127]

那n位二進位數呢?

取值範圍:

現在看起來都是那麼美好,然而當我們將正負數相加時,遇到了問題:2個[+1]相減 ,其實就相當於[+1][-1] 相加,我們的預期是0
但電腦實際上計算時:(0000 0001)原+(1000 0001)原=(1000 0010)原 =[-2]

為瞭解決這個問題,反碼就應運而生了

反碼

反碼主要是針對負數的,正數的反碼是其本身,負數的反碼是在其原碼的基礎上, 符號位不變,其餘各個位取反

  • [+1] = (0000 0001)原 = (0000 0001)反
  • [-1] = (10000001)原 = (1111 1110)反

反碼如果是表示的一個正數,那我們還是一眼就能知道他的數值,但如果是負數的反碼時,我們就需要轉換成原碼才能看出它的真值。
如果最高位有進位出現,則要把它送回到最低位去相加(迴圈進位)的

[- 1] = (1000 0001)原 = (1111 1110)反

[+7] = (0000 0111)原 =(0000 0111)反

[-1] + [+7] = (1111 1110)反 +(0000 0111)反= (1 0000 0101)反 = (0000 0110)反 =[+ 6]

2個正數相減:[+1] - [+1] = [+1] + [-1] = (0000 0001)反 + (1111 1110)反 = (1111 1111)反 =(1000 0000)原 = [**-0**]
這樣就完美實現了“正負相加等於0",但奇怪的是 ,這個[-0]是有符號的,這就要歸因於 原碼的設計之初,存在的問題,

  • (1000 0000)原=[- 0]
  • (0000 0000)原=[+0]

對的,你沒看錯,零竟然有2個,習慣電腦的萬事萬物一一對應,嚴謹認真的工程師們表示無法接受,得想辦法去掉[-0],最後他們就發現了神奇的補碼

補碼

補碼的規則:針對負數繼續改進了思路:正數的補碼就是其本身。負數的補碼是在其原碼的基礎上, 符號位不變, 其餘各位取反, 最後一位+1。即在反碼的基礎上最後一位+1

[+1] = (0000 0001)原 = (0000 0001)反 = (0000 0001)補
[-1] = (1000 0001)原 = (1111 1110)反 = (1111 1111)補

[+1] - [+1] = [+1] + [-1] = (0000 0001)補 + (1111 1111)補 = (1 0000 0000)補 = (0000 0000)補 = [0]
如果補碼在補一位1的時候,發生最高位進位,會自動丟掉最高位。期間引用了電腦對符號位的自動處理,利用了最高位進位的自動丟棄實現了符號的自然處理。

(1000 0000)補 那現在表示多少?-128

  • (1000 0000)補 =-1 * 2^7 =[-128]
  • (1011)補 = -1 * 2^3 + 02^2 + 12^1 + 1*2^0 = -5
  • (0011)補 = 0 * 2^3 + 02^2 + 12^1 + 1*2^0 =3

如果是8位二進位, 使用原碼或反碼表示的範圍為[-127, +127], 而使用補碼表示的範圍為[-128, 127],使用補碼還能夠多表示一個最低數

補碼其實脫胎於 模運算系統
比如一天中的24小時是一個模運算系統,任意時刻的鐘點數都是0到23間的一個整數,這有點類似24進位

  1. 今天的第24點,就是明天的0點;
  2. 今天的25點,就是明天的凌晨1點;
  3. 今天的-4點,就是昨天的20點,我們稱20是-4對模24的補碼,模就是容量、極值的意思

再舉個例子:鐘錶上的12個刻度也是一個模運算系統。假定時鐘現在指向10,要把指針只向6,有兩種方法

  1. 倒撥4格:10 - 4 = 6
  2. 正撥8格:10 + 8 = 18 = 6 (mod 12)

所以模12系統中 -4 = 8 (mod 12),我們稱8是-4對模12的補碼

一個模運算系統中:一個負數可以用它的正補數(負數的補碼)代替,一個負數的補碼 = 模 - 該負數的絕對值

那我們之前公式 一個負數的補碼 = 符號位不變, 其餘各位取反, 最後一位+1,是怎麼來的?

負數的原碼 取反 再加1, 這隻是方便大家記憶的手段,實際上它相當於加一個模256也就是2^8,為什麼要拆,這是由於8位機,8位2進位數,至能表示0~255個數,一共256個數,所以它是表示不了256這個值的,只能是255+1。由於電腦系統裡面不僅只有正數,還有負數呢,這個該怎麼表示?
電腦大師就想到了,可以將256個數一分為二,規定最高位為符號位,最高位1開頭的表示為負數,最高位0開頭表示正數。我們這裡需要註意一下,特殊的0,所以8位2進位數表示範圍就變成了[-128,127],這個範圍是不是很熟悉!

[-1] = (1000 0001)原 = (1111 1110)反 = (1111 1111)補,如果符號位參與計算,(1111 1111)補 的十進位 等於 255。255 + |-1|= 256,也就是模。補碼本天成,妙手偶得之

小結一下:

  1. 補碼不僅解決了[-0]的問題,更核心的是讓電腦做減法運算,變成加法運算A - B = A + B的補碼
  2. 使用補碼,將減法變成加法運算,這樣硬體上只需有加法器即可,不需要其他硬體,降低了電路的複雜度
  3. 使用補碼,不浪費編碼個數,存儲空間利用率高
  4. 補碼可以用n&0判斷負數奇偶
  5. 所以電腦底層存儲數據時使用的是二進位數字,但是電腦在存儲一個數字時並不是直接存儲該數字對應的二進位數字,而是存儲該數字對應二進位數字的補碼

定點數和浮點數

定點數

定點數的意思是:即約定機器中所有數據的小數點位置是固定不變的。通常將定點數據表示成純小數或純整數,為了將數表示成純小數,通常把小數點固定在數值部分的最高位之前;而為了將數表示成純整數,則把小數點固定在數值部分的最後面。
例如:十進位的 25.125

  • 整數部分:25使用二進位表示為:11001
  • 小數部分:0.125使用二進位表示為:.001
  • 所以合起來使用11001.001 表示十進位的25.125

本文的原碼、反碼、補碼概念都是基於定點數

浮點數

定點數表示法的缺點在於其形式過於僵硬,固定的小數點位置決定了固定位數的整數部分和小數部分,不利於同時表達特別大或特別小的數,最終,絕大多數現代的電腦系統採納了浮點數表達方式,這種表達方式利用科學計數法來表達實數,即用一個尾數(Mantissa,尾數有時也稱為有效數字,它實際上是有效數字的非正式說法),一個基數(Base),一個指數(Exponent)以及一個表示正負的符號來表達實數
例如:

  1. 352.47 = 3.5247 * 10的2次方
  2. 178.125轉化為二進位為 10110010.001,又可表示為:1.0110010001 乘以 2的111次方(111是7的二進位表示)
  3. 123.45用十進位科學計數法可以表示為1.2345x10的2次方,其中1.2345為尾數,10為基數,2為指數。浮點數利用指數達到了浮動小數點的效果,從而可以靈活地表達更大範圍的實數。

字元串編碼

ASCII 碼

在電腦中, 不僅數值可以用二進位表示,字元串也能用二進位表示。上世紀美國制定了一套字元編碼,對英語字元與二進位位之間的關係,加上數字和一些特殊符號, 然後用 8 位的二進位,就能表示我們日常需要的所有字元了,這個就是我們常常說的ASCII 碼

ASCII 碼就好比一個字典,將二進位和字元一一對應。其中我們看幾個典型的例子:

  1. 小寫字母 a 在 ASCII 裡面,十進位97,也就是二進位的 0 110 0001,而大寫字母 A,十進位65,對應的二進位 0 100 0001
  2. 需要註意的是,裡面的數字,比如數字1,二進位對應0000 0001 在ASCII 裡面,表示的其實是字元"1",對應的二進位是0 011 0001
  3. 字元串 15 也不是用 0000 1111 這 8 位二進位來表示,而是變成兩個字元 1 和 5 連續放在一起,也就是0 011 00010 011 0101,需要用兩個 8 位二進位來表示 。所以**電腦儲存數據時,二進位序列化會比直接存儲文本能節省大量空間 **

EASCII:擴展的 ASCII

一開始美國編寫ASCII表,英語用128個符號編碼就夠了,但隨著電腦的普及,西歐國家不全是英語國家,有德語,法語等等

比如 字母上方有註音符號,它就無法用 ASCII 碼表示。於是歐洲工程師就決定,利用位元組中閑置的最高位編入新的符號。他們把 ASCII 擴充變成了 EASCII,這擴充的包括希臘字母、特殊的拉丁符號等。由於 ASCII 只占了 7 位,所以 EASCII 把第 8 位利用起來,仍然是一個位元組來表示,這時表示的字元個數是 256。

Unicode

但 EASCII 並沒有成功,西歐國家以及各個 PC 廠商各自定義出了好多不同的編碼字元集,ISO-8859將西歐國家的編碼一起包含進去。
但隨著電腦來到中國,那些歐美國家把 現有的字典都用完,而且漢字有十多萬個,所以急需新的"字典"。GB2312編碼就出來了,使用兩個位元組表示一個漢字(漢字太多),所以理論上最多可以表示 256 x 256 = 65536 個符號。後來GBK編碼 將古漢字等生僻字加進來。臺灣地區又創造了BIG5編碼,再後來GB18030對東南亞地區的文字,進行了統一。簡單瞭解下這些編碼,就不具體展開了

再後來電腦全球普及,各個國家地區的文字編碼太多太亂,Unicode編碼的出現,為了統一全世界的所有字元。Unicode 是一個很大的集合,現在的規模可以容納100多萬個符號。

由於Unicode 只是一個字元集(Charset),它只規定了符號的二進位代碼,卻沒有規定這個二進位代碼應該如何存儲,也就是字元編碼(Character Encoding),這就導致電腦無法區別 Unicode 和 ASCII ,比如三個位元組表示一個符號,而不是分別表示三個符號呢?

隨著互聯網的崛起,UTF-8 就是在互聯網上使用最廣的一種 Unicode 的實現方式。UTF-8 它是一種變長的編碼方式。它可以使用1~4個位元組表示一個符號,根據不同的符號而變化位元組長度。Unicode 字元集中的大部分漢字,如果用 UTF-8 編碼的話,是占 3 個位元組的

下麵我們看看UTF-8是如何相容Unicode的:
UTF-8編碼致力於統一世界上所有的字元集,所以它的設計上既向下相容ASCII碼的編碼方式,同時又考慮了可拓展性,規則如下:
1)對於單位元組的符號:位元組的第一位設為0,後面7位為這個符號的 Unicode 碼。與 ASCII 編碼規則相同;
2)對於n位元組的符號(n > 1):第一個位元組的前n位都設為1,第n + 1位設為0,後面位元組的前兩位一律設為10。剩下的沒有提及的二進位位,全部為這個符號的 Unicode 碼。

Unicode UTF-8 byte 數
0000~007F 0XXX XXXX 1
0080~07FF 110X XXXX 10XX XXXX 2
0800~FFFF 1110 XXXX 10XX XXXX 10XX XXXX 3
1 0000~1F FFFF 1111 0XXX 10XX XXXX 10XX XXXX 10XX XXXX 4

我們可以發現,UTF-8 編碼的第一位如果是 0,則只有一個位元組,跟 ASCII 編碼完全一樣,所以相容了。如果是 110 開頭,則是兩個位元組,以此類推如上表所示。所以開頭幾位的值,是編碼本身,同時又是判斷後續還有幾個位元組數的推碼(通過推碼才能判斷這個位元組之後還有幾個位元組共同參與一個字元的表示)

亂碼的來源

編碼是把數據從一種形式轉換為另外一種形式的過程,而解碼則是編碼的逆向過程。編碼是一種格式,解碼是另一種格式,當然會出問題。下麵我們舉個例子,來看看這個問題:

  1. 創建hello.txt文件,用Notepad++打開編輯,以 UTF-8 格式寫入你好
  2. 然後我們改變Notepad++formaat格式,改為GB2312,然後你好就變成了浣犲ソ

在UTF-8 字典中,你好兩個字的16進位編碼分別是E4BDA0E5A5BD
在GB2312字典中,浣犲ソ三個字的16進位編碼分別是 E4BDA0E5A5BD

由於在UTF-8 編碼漢字是 3 個位元組,在GB2312編碼漢字卻是2個位元組,電腦用GB2312去解析UTF-8,硬生生的把3個位元組以每2個位元組為一組去解碼,所以才會有出現這種亂碼。當我們知道亂碼出現的原因,如何解決就變的非常簡單了


參考資料:
《編碼:隱匿在電腦軟硬體背後的語言》
《深入理解電腦系統 第三版》
《電腦組成原理》
《深入電腦組成原理》
https://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
https://www.zhihu.com/question/20159860
https://blog.csdn.net/f919976711/article/details/116714860


本篇文章到這裡就結束啦,很感謝靚仔你能看到最後,如果覺得文章對你有幫助,別忘記關註我!
電腦內功、JAVA源碼、職業成長、項目實戰、面試相關資料等更多精彩文章在公眾號「小牛呼嚕嚕


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 簡介 限流顧名思義是對流量大小進行限制,防止請求數量超過系統的負載能力,導致系統崩潰,起到保護作用。 現實生活中限流也隨處可見,節假日出門旅行的人數會劇增,對於旅游景點來說往往會不堪重負,如果不進行人數控制,對整個景點的壓力會非常大,游客的體驗也會非常差,還容易出現安全事故等危險。 同樣的在一線城市 ...
  • 轉載:https://blog.csdn.net/tslx1020/article/details/128250777 1、spawn - 冷啟動 frida-trace -U -f com.apple.ExampleCode -m “+[NSURL URLWithString:]" 2、attac ...
  • 伺服器信息 在阿裡雲買了個搶占式的伺服器,地區為華南廣州,系統為Ubuntu 20.04,8核16GB。 安裝Docker 命令如下: $ apt-get update -y $ apt-get upgrade -y $ apt-get install -y docker.io 安裝成功後,檢查一下 ...
  • 2023-01-14 一、Spring底層IOC實現 1、IOC:將對象的控制器反轉給Spring 2、BeanFactory與ApplicationContext (1)BeanFactory:IOC容器的基本實現,是Spring內部的使用介面,是面向Spring本身的,不是提供給開發人員使用的。 ...
  • 牛牛剛剛出生,嗷嗷待哺,一開始他只能學說簡單的數字,你跟他說一個整數,他立刻就能學會。輸入一個整數,輸出這個整數。 ...
  • 本篇文章,我們就一起聊一聊如何來更好的使用緩存,探尋下如何降低緩存交互過程的性能損耗、如何壓縮緩存的存儲空間占用、如何保證多個操作命令原子性等問題的解決策略,讓緩存在項目中可以發揮出更佳的效果。 ...
  • C++11 智能指針 shared_ptr Written on 2023-01-16 個人學習智能指針記錄合集: C++11 智能指針 C++11 智能指針 shared_ptr C++11 智能指針 unique_ptr C++11 智能指針 weak_ptr std::shared_ptr 共 ...
  • 前言 用.net6開發一個Winform程式,處理Excel文件,並把結果導出Excel文件。 要用到兩個演算法,一是turf.js庫的booleanPointInPolygon方法,判斷經緯度坐標是否在區域內;二是經緯度糾偏演算法,因為對方給的區域坐標集合有偏移,需要糾偏。 這兩個演算法,網上找C#的實 ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 1. 講故事 年前遇到了好幾例托管堆被損壞的案例,有些運氣好一些,從被破壞的托管堆記憶體現場能觀測出大概是什麼問題,但更多的情況下是無法做出準確判斷的,原因就在於生成的dump是第二現場,借用之前文章的一張圖,大家可以理解一下。 為了幫助更多受此問題困擾的朋友,這篇來整理一下如何 快狠準 的 ...
  • 前言 .NET6 開始,.NET Croe API 項目取消了 Startup.cs 文件,在 Program.cs 文件的 Main 函數中完成服務的註冊和中間件管道的管理。但當我們項目引入更多包的時候,Program.cs 文件也會看起來很臃腫。 而且,我們不只會有一個後端項目,為了方便快速創建 ...
  • 目錄 背景 get 與 post 的區別 所有介面都用 post 請求? 背景 最近在逛知乎的時候發現一個有趣的問題:公司規定所有介面都用 post 請求,這是為什麼? 看到這個問題的時候其實我也挺有感觸的,因為我也曾經這樣問過我自己。在上上一家公司的時候接到一個項目是從零開始搭建一個微服務,當時就 ...
  • *以下內容為本人的學習筆記,如需要轉載,請聲明原文鏈接 微信公眾號「englyf」https://mp.weixin.qq.com/s/2GFLTstDC7w6u3fTJxflNA 本文大概 1685 個字,閱讀需花 6 分鐘內容不多, 但也花了一些精力如要交流, 歡迎關註我然後評論區留言 謝謝你的 ...
  • 在新版本的pandas中,上述代碼會引起警告,建議改成SQLAlchemy connectable(engine/connection),後續代碼將引入這種升級的連接方式。 ...
  • 幾乎所有的高級編程語言都有自己的垃圾回收機制,開發者不需要關註記憶體的申請與釋放,Python 也不例外。Python 官方團隊的文章 https://devguide.python.org/internals/garbage-collector 詳細介紹了 Python 中的垃圾回收演算法,本文是這篇 ...
  • 如果您想查找高於或低於平均值的數字,可以不必計算該平均值,就能查看更高或更低的值。通過Java應用程式,可以自動突出顯示這些數字。除了快速突出顯示高於或低於平均值的值外,您還可以查看高於或低於的值的個數。現在讓我們看看如何在 Java應用程式中實現此操作。 引入jar包 導入方法1: 手動引入。將  ...
  • 第一種方式:使用{} firstDict = {"name": "wang yuan wai ", "age" : 25} 說明:{}為創建一個空的字典對象 第二種方式:使用fromkeys()方法 second_dict = dict.fromkeys(("name", "age")) #valu ...
  • 在golang中可以使用a := b這種方式將b賦值給a,只有當b能進行深拷貝時a與b才不會互相影響,否則就需要進行更為複雜的深拷貝。 下麵就是Go賦值操作的一個說明: Go語言中所有賦值操作都是值傳遞,如果結構中不含指針,則直接賦值就是深度拷貝;如果結構中含有指針(包括自定義指針,以及切片,map ...
  • 本文結合京東監控埋點場景,對解決樣板代碼的技術選型方案進行分析,給出最終解決方案後,結合理論和實踐進一步展開。通過關註文中的技術分析過程和技術場景,讀者可收穫一種樣板代碼思想過程和解決思路,並對Java編譯器底層有初步瞭解。 ...