===》點我返回目錄《===
我們生活中一直使用十進制系統,而計算機裏面一直用二進制系統。這個系統由科學巨人Leibniz萊布尼茨重新發明並提倡。他還寫過一篇文章,介紹二進制以及用它來解釋古代中國伏羲圖,以至於後來很多人認爲他從《周易》中得到啓發發明二進制。這種論調在民族自卑情節下流傳廣泛,這是往自己臉上貼金。
(Gottfried Leibniz,1646.7.1-1716.11.14)
我們來看看進制之間怎麼轉換。二進制到十進制是很簡單的,一個二進制數根據公式其數值等於k*2n+K*2n-1+...+k*20,其中k只有0,1兩個值,如11001,就等於1*24+1*23+0*22+0*21+1*20=25。
二進制轉換成十進制的程序我就不演示了。
十進制轉換到二進制就有一點麻煩了,用的一種除2餘數法,我們操作一遍:
對十進制的25這個數,按照如下步驟操作:
先除2,得到商數12,餘數爲1;
然後用12再除2,得到商數6,餘數爲0;
然後用6再除2,得到商數3,餘數爲0;
然後用3再除2,得到商數1,餘數爲1;
然後用3再除2,得到商數0,餘數爲1;
到此計算完畢,把餘數反過來寫,即11001就是最後的結果。
我們用程序來表達上述過程:
number=int(input("Type number: "))
listt=""
while number >= 1:
r1 = number%2 #餘數
number = number//2 #整除
listt = str(r1) + listt #倒過來拼串
print(listt)
不用說你就明白了,這個程序處理不了實數,因爲實數有兩部分,這個程序處理不了小數部分。小數部分不是通過除2取餘,而是通過乘2取整得到的。
對十進制的0.125這個數,按照如下步驟操作變成二進制:
先把0.125乘2,得到0.25,取整數部分,是0;
再把0.25乘2,得到0.5,取整數部分,是0;
再把0.5乘2,得到1,取整數部分,就是1;
計算完畢,最後的結果就是0.001。
用程序實現一下:
number=float(input("Type number: "))
listt="0."
while int(number) != number:
number = number*2
r1 = int(number)
listt = listt + str(r1)
number = number - r1
print(listt)
測試0.125,得到結果0.001
測試0.124,得到結果0.00011111101111100111011011001000101101000011100101011。
初次接觸,你可能會很驚奇,二進制表示0.124這麼麻煩吶!可想而知有些小數會用到幾百幾千位才能夠表示出來。許多人就會疑惑,那計算機裏面究竟是怎麼弄的呢?
我們知道計算機存儲空間有限,不能無止盡地存這些數,所以對小數會按照固定的精度存儲。由此我們可以看到對小數的表示,在計算機裏面是不精確的,只是一個符合某種精度的大約值。
我們研究一下計算機內部數據的存儲。我們知道計算機的最小數據單元是bit(比特),存一位,這個是不可再分割的。計算機裏面所有數據和信息都是靠很多很多bit表示和存儲的。或許你對bit沒有什麼概念,它能存多少信息呢?或者反過來說,一個數字一個字符一首音樂需要多少bit表示和存儲呢?
我們簡單計算一下。
對於整數,從0到無窮,一個bit只能表示兩個數字,0/1。如果有四個數字要表示,我們需要兩個bit,即00,01,10,11,以此類推,8個bit我們可以表示256個數字,16個bit可以表示65536個數字,32個bit大約可以表示40億個數字。
存儲都是需要靠物理設備元器件的,落實到物理上,存儲設備在不同的地方不同的時代都不相同,只要能穩定地保持兩種狀態的器件都可以當成存儲設備,最形象的想象就是開關。一個開關就是一個bit,存儲65536個數字,我們需要65536*16個開關,這是一個比較大的數,如果存儲40億個數字,我們需要32*40億個開關。
你肯定很吃驚,這麼多開關合在在一起,得佔多大的地方啊?你想象得沒有錯,早期的計算機確實佔地很大,有一座房子那麼大,程序員是鑽進計算機裏面編程序的。
早期的計算機還是電老虎,性能不高(相對於現在而言),經常壞(每15分鐘燒掉一隻真空電子管),IBM的老大Watson說這個世界上最多只需要5臺計算機。
後來技術越來越進步,開關越做越小,也越來越快。
1947年,美國Bell Labs貝爾實驗室的Bardeen巴丁、Brattain布拉頓、Shockley肖克萊三人發明了晶體管。這真的是一個好東西,尺寸小,開關速度快,發熱量小,天生就是用來造計算機的。
(晶體管照片) (N型晶體管電路符號)
晶體管有三極,e,c,b。高低電平變化,c和e就導通和截斷。通過這個表示0和1。
可以表示數(二進制),也可以表示運算,邏輯運算(因爲0,1可以認爲是兩種狀態是否),這樣實現了邏輯學的基礎:布爾代數(布爾代數進行集合運算可以獲取到不同集合之間的交集、並集或補集,進行邏輯運算可以對不同集合進行與、或、非。由英國數學家George Boole開創)。
比如1-0,0-1就是邏輯非運算 NOT
比如下表是邏輯與運算 AND
0 1
0 0 0
1 0 1
比如下表是邏輯或運算 OR
0 1
0 0 1
1 1 1
比如下表是邏輯異或運算 XOR
0 1
0 0 1
1 1 0
有了二極管和晶體管,通過電路比較容易實現上面的邏輯,我們叫門電路(因爲通過開關表示,門可以開可以關,所以起了這個名字)。
與門:
分析:當輸入1爲高電平,輸入2也爲高電平時,Q11導通,Q9導通,則輸出點也爲高電平,即爲1。
或門:
分析:
當輸入1或輸入2爲高電平時,Q6導通,輸出點高電平,即數字量1。
非門:
分析:
當輸入爲高電平時,Q1導通,輸出點電壓爲Q1的c,e之間的壓降,即0.3V,即輸出爲數字0;當輸入爲低電平時,Q1的c,e之間未導通,輸出電壓爲上拉的電壓,+5V,即數字量1。
現在我們用三極管做出邏輯運算來了,可以看出數據和運算在二進制中是一回事,所以最後在底層統一到門電路了。
門電路的符號:
我們先看怎麼組合一下這些基本門電路讓它存儲狀態,也就是邏輯上存一位bit。作爲存儲器,基本的要求是保持狀態穩定,遠古的時候用小石子和繩結,它們不會輕易變化,這個固有的物理特性就讓它們適合存儲,而手指則不合適,人老得去幹別的事情,手指頭的狀態一下子就變化了。
我們用的如下雙穩態電路當存儲器:
上面我們組合兩個或非門,一個的輸出是另一個的輸入,這樣構成雙穩態結構。當S端設爲1的時候Q總是1,當R端設爲1的時候Q總是0。
當電路上一秒還在“S=0,R=1”狀態時(此時Q=0,非Q=1),突然變成了“S=0,R=0”,此時我們會驚奇的發現,由於Q=0,S=0,非Q仍然是1,非Q=1,R=0,Q仍然是0,雙穩態電路這就做到了保持Q的狀態。電路有了記憶。所以可以存儲一位bit。
這個電路叫基本RS觸發器。
把上面的RS觸發器組合在一起,就可以存儲多位了。如圖:
現在我們終於有了能存儲數字的器件了。從遠古人類用手指用小石子用繩結作爲存儲器,經過了數千年上萬年,我們發明了速度快得多體積小得多的存儲器了。
真不容易啊。
要注意我們現在講的是計算機內部的寄存器,我們平時還會接觸硬盤一類的存儲設備,它是怎麼存的呢?它們的原理不同,是用的磁介質。不做進一步解釋了。
手上有了這些器件,知道他們能從物理上表示和存儲bit,後面我們不需要再管實際的物理部件了,只要從邏輯上去理解如何進行數據表示和存儲。
先看正整數。計算機內部一般用定點法存儲正整數,所謂定點,就是不管這個數時3還是30000,都用同樣多的bit表示,如用16位bit表示整數。對3,表示爲0000000000000011(前面補充14個0)。這樣的好處時整數在計算機內部都是對齊的。定點法表示整數會有一個最大值,如16位最大能表示的數字爲65535。
正整數是無符號的,那麼怎麼表示有符號的數呢?表示負數就必須有辦法把這個符號表示出來。前面我們也提到過,在計算機裏面一般用補碼錶示。規則簡單地說就是取反加1。
這個規則另一種表述是:從最右邊的位開始,逐個複製,遇到第一個1,複製這個1之後的操作變成逐個取反。我更加喜歡第二種表述,因爲這是一種機械操作式的表述,讓人好像眼前看到了計算機內部是如何變化的。
用補碼錶示數,最左邊高位的符號,如果位1,表示爲負數,0則爲正數。
如:對-28,先看28的二進制00011100,求補碼11100100,就是這麼表示的。
如:對28,還是表示爲00011100。
因爲最高位是符號,所以補碼錶示的數字範圍是無符號表示的一半,比如對8位表示,無符號表示範圍從0到255,而用補碼錶示則爲-128到127。
我們再看看怎麼存儲實數。
前面介紹過,實數在二進制表示種有精度問題,IEEE規定了幾種格式,用得比較多的有兩種:單精度和雙精度。
這種結構是一種科學計數法,用S符號、E指數和M尾數來表示,底數定爲2——即把一個浮點數表示爲尾數乘以2的指數次方再添上符號。
S符號位 E指數 M尾數
單精度(32位) 1 8 23
雙精度(64位) 1 11 52
一個實數,按照如下辦法存儲:
- 先確定符號位,0爲正數,1爲負數
- 數字轉成二進制
- 規範化科學計數
- 計算E和M值
- 組合SEM
舉例,5.75,我們這麼辦:
這是正數,所以S符號位爲0。轉成二進制,爲101.11。把101.11規範化成1.0111*22。這樣識別出指數爲2,按照IEEE規定加127偏移量,得出129,這就是E,二進制表示爲10000001。
尾數爲0111(由於規範化之後小數點前永遠爲1,所以就不存儲了),後面加19個0,補齊23位M爲01110000000000000000000。拼在一起的到最後的存儲格式:01000000101110000000000000000000。
除了數,我們還會接觸到很多別的類型,如字符,音頻,圖像,那這些是怎麼存儲的呢?其實一切都要數字化。
對於字符,我們要規定字符的編碼,比如對於a,我們規定編碼爲61。標準化組織制定了一些標準,讓全世界的人統一編碼,用於共享和交換。比如常用的ASCII。
(ASCII碼錶)
音頻是模擬信號,要全部記錄下來需要無窮的空間存儲,所以通過採樣進行數字化,比如在一秒鐘的音頻種,我們採集100個點,記錄下點上的值(16位記錄一個點上的值),爲了耳朵聽不出來失真,要求採樣足夠密,按照MP3的標準,每秒要採樣44100次。
我們可以計算一下,按照MP3規範,一秒的音頻的位率是44100*16=640KB/s。
這下子我們能理解了爲什麼音頻文件會比文本文件大很多。
圖像也是類似的道理,我們可以逐點記錄下圖像的值。JPEG還有GIF都是這方面的標準。同樣我們可以想象到,存儲圖像也需要很大的空間。
討論到這裏,我們可以看出來了,計算機內部把一切數字化,存儲上就是存的數字。而運算也簡化成了基本運算,加法,移位,邏輯運算。
這是一種還原論,這種哲學認爲複雜的事物可以將其化解爲各部分之組合來加以理解和描述。還原論歷史悠久,第一個哲學家泰勒斯就說過名言“世界是由水做成的。”在科技史上,還原論影響巨大,但是它也不是萬能的,比如解釋生命和意識,還原論還不能讓人信服。
(Θαλῆς ὁ Μιλήσιος 泰勒斯,第一個哲學家,公元前624-公元前546)