計算機用戶的壹個普遍誤解是計算機數值計算的絕對準確性。也就是說,如果乘以:
3 × (1/3)
您預期的準確結果是1。另壹方面,妳發現計算機並沒有給出這個結果,而只是壹個近似值,類似於0.4000000000005
這似乎是系統的“bug”,但更令人驚訝的是,沒錯,計算機就是這樣工作的(計算機代數系統除外)。本文將詳細解釋這個問題。
位、字節、半字節和無符號整數
幾乎所有的計算機用戶都知道“位”的概念。在計算機中,通過切換設置表達式值0或1。如果妳有兩個比特可供選擇,妳可以很容易地得到四種不同的狀態:
00 01 10 11
如果妳有三位,妳可以用八種狀態來表示它們:
000 001 010 011 100 101 110 111
每增加壹點,就會得到兩倍的狀態。
很多計算機都是用八位來表示信息,有的則多了八位,比如16位,32位或者64位。通常以八位為壹組作為基本單位,並使用另壹個詞“字節”。計算機的處理器壹次處理壹位或八位的倍數的信息。存儲器使用壹個或多個八位字節來存儲數據。
其實有些情況下用四位處理問題更方便。這種四位數據通常被稱為“四位字節”。但實際上,“bit”比“nybble”更常用。
壹個nybble可以編碼16種不同的情況,比如數字0到15。壹般來說,可以用任何序列排列來表示不同的16狀態,但在實際應用中通常是這樣的:
0000 = decimal 0 1000 = decimal 8 0001 = decimal 1 1 = decimal 9 001010 = decimal 2 1011 = decimal 11 = decimal 1100 = decimal 4 654383 0110 =十進制6 1110 =十進制14 0111 =十進制7 1166。
這種表示法很自然,因為它符合我們熟悉的十進制數表示法。例如,給定壹個十進制數:
7531
我們自然把它理解為:
7 × 1000 + 5 × 100 + 3 × 10 + 1 × 1
或者,用10的冪來表示:
7 × 10 3 + 5 × 10 2 + 3 × 10 1 + 1 × 10 0
註意,任何數字(除了0)的0次冪都是1。
數據中的每壹個數字都代表壹個從0到9的值,所以我們有10個不同的數字,這也是我們稱之為“十進制”的原因。每個數字都可以用10的冪來確定。聽起來很復雜,但其實並不是這樣。這正是妳在看壹個數的用法時想當然的,妳甚至都沒有仔細想過。
類似地,使用二進制編碼,如上所述,13的值編碼如下:
1101
每個位置有兩個數可供選擇,所以我們稱之為“二進制”。因此,它們的位置確定如下:
1101 =
1 × 2 3 + 1 × 2 2 + 0 × 2 1 + 1 × 2 0 =
1×8+1×4+0×2+1 = 13(十進制)
註意這裏用的是2的冪:1,2,4,8。癡迷於計算機的人通常能記住2的2次方從2到16,不是因為記憶力,而是因為他們大量使用這些數字:
2 0 = 1 2 8 = 256 2 1 = 2 2 9 = 512 2 2 = 4 2 10 = 1 024 2 3 = 8 2 11 = 2 048 2 4 = 16 2 654 38+02 = 4 096 2 5 = 32 2 13 = 8 192 2 6 = 64 2 14 = 16 384 2 7 = 128 2 15 = 32 768 2 16 = 65 536
註意,根據公制單位,值2 10 = 1 024通常被稱為“千”或縮寫為“k”,因此2的許多高次冪通常可以縮寫為:
2 11 = 2k = 2 048 2 12 = 4k = 4 096 2 13 = 8k = 8 192 2 14 = 16K = 16 384 2 15 = 32k = 32 768 2 16 = 64k
同樣,值2 ^ 20 = 1 ^ 024 x 1 ^ 024 = 1 ^ 048 ^ 576通常縮寫為“m”:
2 21 = 2米2 22 = 4米
而2 ^ 30壹度被稱為“吉”或“克”。我們將在下面大量使用這些修飾語。
有壹個很微妙的話題。如果我們使用16位,我們可以得到65 536個不同的值,但這些值的範圍是從0到65 535。人們通常從1開始計數,但計算機從0開始計數。因為這對他們來說更容易。這個小問題有時候會把電腦搞糊塗。
現在,我們有了計算位數的方法:
妳只能在妳所有位的範圍內進行算術運算。換句話說,如果妳使用的是16位,就不能對65 535或更大的數據進行操作,否則會得到“數據溢出”錯誤。這個術語表示您所做的是“有限精度”的操作。
小數不能用這種編碼來表示。只能使用非十進制的“整數”。
負數不能用這種編碼來表示。所有數字都是“無符號數字”
盡管有這個限制,“無符號整數”對於計算機中1的簡單計數仍然非常有用。計算機很容易處理它們。通常計算機使用16位或32位無符號整數,通常稱為“整數”或“長整數”。整數允許對0到65,535的數據進行運算,而長整數允許對0到4,294,967,295的數字進行運算。
八進制和十六進制數字
現在我們來討論壹些無關的話題:二進制數的表示。計算機通常使用二進制來表示數據,但在實踐中,如果像這樣使用二進制:
1001 0011 0101 0001
那會是壹件痛苦的事情,容易出錯。通常計算機使用基於二進制的表達式:八進制,或者更常見的十六進制。
這個聽起來很狡猾,其實很簡單。如果不是這個,我們也不會這樣用。在通常的十進制系統中,我們有10個數字(0到9)按以下方式排列:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...
在八進制中,我們只有八個數字(0到7)按以下方式排列:
0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20 21 22 23 24 25 26 ...
也就是說,八進制“10”相當於十進制“8”,八進制“20”相當於十進制“16”,以此類推。
在十六進制中,我們只有十六個數字(0到9,然後從A到F)按以下方式排列:
a b c d e f 10 10 12 13 14 15 16...
換句話說,十六進制的“10”相當於十進制的“16”,十六進制的“20”相當於十進制的“32”。
這些數值方法都是表位制,只是用了8和16,而不是像decimal那樣用10。例如:
八進制756
= 7 × 8 2 + 5 × 8 1 + 6 × 8 0
= 7 × 64 + 5 × 8 + 6 × 1
= 448+40+6 =十進制494
十六進制3b2
= 3 × 16 2 + 11 × 16 1 + 2 × 16 0
= 3 × 256 + 11 × 16 + 2 × 1
= 768+176+2 =十進制946
好吧,如果妳沒有得到它,不要擔心。我們要解釋的是,對於八進制來說,它們只是與3位二進制有完美的對應關系:
000 =八進制0
001 =八進制1
010 =八進制2
011 =八進制3
100 =八進制4
101 =八進制5
110 =八進制6
111 =八進制7
類似地,壹個十六進制數只對應壹個4位二進制數。
0000 =十六進制0 1000 =十六進制8 0001 =十六進制1 =十六進制9 0010 =十六進制2 1010 = 1011 =十六進制b 0100 =十六進制4 165438十六進制6 1110 =十六進制e 0111 =十六進制7 111 =十六進制f。
所以把很長的二進制數轉換成八進制數是很簡單的,比如把二進制數10011010101轉換成八進制數:
1 001 001 1 01 001二進制= 111八進制。
轉換成十六進制更容易:
1001 001 01 001 0001 = 9351十六進制。
但是轉換成十進制(37 713)就比較麻煩了。八進制和十六進制使得轉換二進制機器級別的數字變得容易。
有符號整數和補碼
在定義了無符號二進制數之後,我們將開始定義負數,或者說“有符號整數”。最簡單的方法是保留壹位來表示數值的符號。這個“符號位”可以位於值的最左邊,當然也可以位於值的最右邊。如果符號位為0,則表示值為正,如果符號位為1,則表示值為負。
做到這壹點是可能的。雖然從人類的角度來看,這是最明顯的解決方案,但它可能會給計算機帶來壹些困難。例如,這種編碼使正負零都成為可能。人們可能對此感到不可思議,但它適用於計算機。
對於計算機來說,更自然的表達方式是將給定位數的二進制數按其值域分成兩半,其中前半部分用來表示負數。例如,在壹個4位數中,您可以得到:
0000 =十進制0 0001 =十進制1 0010 =十進制2 001100 =十進制3 0101 = 011 =十進制7 1000 =十進制-8 65438+38+0101 =十進制-3 110 =十進制-2 1111 =十進制-65438+。
現在我們有了“有符號整數”數字系統,由於壹些不重要的原因,它使用了已知的“補碼”編碼方法。對於16位有符號數字編碼,我們可以得到-32 768到32 767範圍內的有符號數字。對於32位有符號編碼系統,我們可以對從-2 147 483 648到2 147 482 647的數字進行編碼。
與只改變符號位來表示負數的編碼方法相比,“補碼”編碼方法是不同的。例如,對於-5,只有符號位被編碼,它應該是:
1101
但對於“補碼”編碼方法,它是:
1011
這是符號編碼的-3。我們將在後面解釋為什麽計算機使用補碼編碼。
所以,現在我們可以用二進制的方式來表示兩個不同的值,正的和負的。記住,解釋二進制數只有兩種方法。如果內存中有這樣的二進制值:
1101
-這只能解釋為十進制的“13”或“-3”。
不動點
這種格式通常用於商業計算(例如,在電子表格或COBOL中);因為在這裏,舍棄小數點來記錄金錢是不可接受的。所以,了解二進制如何存儲小數是非常有用的。
首先,我們必須決定用多少位存儲小數部分,用多少位存儲整數部分。假設我們用32位來表示這個格式,那麽我們用16位來表示整數部分,用16位來表示小數部分。
小數部分怎麽用?這遵循了整數的表達方式:如果8位後面跟著4位,就是2位和1位,那麽當然後面是半位,1/4位和1/8位等等。
例如:
整數小數位0.5 = 1/2 = 0000000000000.100000000000005438+0.25 = 1 1/4 = 00000000000001.0100000000000 7
有壹點比較棘手的是,如果要表示1/5(十進制0.2),無法得到壹個準確的數值表達式。最好的辦法只能是:
13107/65536 = 0000000000000.00111101111110065438.
13108/65536 = 0000000000000.00111101111101065438 = 0.
然而,不,妳不能只是做,因為妳有更多的數字來表達。問題是有些小數不能用二進制精確表示。除非妳用特殊的方法。這種特殊的方法是用兩個數來表示小數:壹個是分子,壹個是分母。然後妳可以用在學校學的加減乘除來得到它們。但是這些方法不能表示更高階的數(比如平方根),或者這兩個分母的最小公倍數很大的情況下很難使用。這就是用定點小數表示小數的好處。
浮點小數
當我們使用有符號和無符號的數值表達式時。如果我們遇到壹個大範圍的數字,連32位都不足以表達,或者我們也許能夠表達,但為此我們不得不放棄小數位,那麽我們可以選擇的獲得更大範圍的數值表達式的方法就是使用“浮點小數”格式,而不是“固定小數”格式。
在十進制系統中,我們熟悉下列表達式:
1.1030402 × 10 5 = 1.1030402 × 100000 = 110304.02
或者縮寫為:
1.1030402E5
這意味著“1.103402乘以壹個1後跟五個零的數”。我們可以得到壹個確定的數值(1.1030402)稱為尾數,乘以10的某個冪級數(E5,代表10 5或100 000),即冪級數。如果我們使用負冪級數,就意味著乘以正級數的倒數。例如:
2.3434 e-6 = 2.3434×10-6 = 2.3434×0.000001 = 0.0000023434
使用這個定義的好處是我們可以得到更大範圍的值,雖然尾數部分的精度會受到影響。類似的原理也適用於計算機使用的二進制系統。已經設計了許多方法,但是通常使用由美國電氣和電子工程師協會定義的壹種方法。它將64位浮點格式定義為:
11二進制表示索引,使用“超級1023”的格式。這種格式允許將指數表示為0到2047的無符號數,但在得到真值之前,必須將其從1023中減去;
52位尾數,用無符號數字表示;
壹個符號位;
讓我們用壹個例子來理解計算機如何使用8位內存來存儲這些數字:
0號:Sx10x9X7XXX5X4號1號:x3x 2 x 1x0m 51 m50 m 49m 48 2號:M47m46m45m44m43m41m40號:M39m38m38。35 m34 m33 m32第四名:m31 m30 m29 m28 m27 m26 m25 m24第五名:m23m 22m 21m 20m 19m 17m 16第六名:M15m6556。13m 12m 1m 10m 9 M8第7名:m7 m6 m5 m4 m3 m2 m1 m0。
其中“s”代表符號位,“x”代表指數位(秩碼?),“m”是尾數的意思。壹旦這些數字被讀取,它將被轉換成:
& ltsymbol >×(1+& lt;分數尾數>;)×2 & lt;訂單代碼>- 1023
此方法提供的數字範圍是:
最大值和最小值
正數是1.7991346231e+308484+0246534
負數-4.94065645412465 e-324-1.791331e+308。
這個方法還定義了壹些不是數字的值,比如“NaNs”(“不是數字”)。這通常用於返回指示數字溢出的信息。平時大家都不碰,我們就不進壹步討論了。壹些程序使用32位浮點小數。最常見的是使用32位尾數、1位符號位、8位代碼和“超127”格式。它提供7個十進制數字。
0號:S x7 X6 X5 x4 x3 x2 x 1號。1:x0m 22m 21m 20m 19m 17m 16二號:M15m65438+。M11 m10 m9 m8第三名:m7 m6 m5 m4 m3 m2 m1 m0。
用於數字轉換:
& ltsymbol >×(1+& lt;分數尾數>;)×2 & lt;訂單代碼>- 127
其範圍是:
最大值和最小值
正數3.402823E+38 2.802597E-45
負數-2.802597e-45-3.423e+38
壹般我們可以稱32位二進制浮點數為“單精度”,64位二進制浮點數為“雙精度”。如果我們用real,通常表示壹個雙精度浮點數;使用float時,我們通常指的是單精度浮點數。
但是記住,比特就是比特,它們在計算機中的存儲是連續的。當計算機內存中有壹個64位數據時,它可能是壹個雙精度浮點數,兩個單精度浮點數,或者四個有符號或無符號整數,或者其他8位數據,這取決於計算機如何讀取它們。如果計算機將四個無符號整數讀取為雙精度浮點小數,可以得到壹個雙精度浮點小數,但這個數據可能是垃圾數據。
所以,雖然我們現在解決了正數和負數的存儲方式,但是對於浮點數來說還是有壹些像整數壹樣的缺陷,比如:
和整數壹樣,浮點數也有範圍。雖然我們得到的範圍比整數大得多,但還是有限的。如果您嘗試將兩個非常大的數字相乘,您可能會得到“數據溢出”錯誤。而如果用壹個小數字除以壹個大數字,可能會使索引的值出錯,造成“數據下溢”的錯誤。最大值通常被稱為“機器無窮大”,因為這是計算機可以處理的最大數。
另壹個問題是準確性。雖然妳有15位數來表示壹個非常大的數,但是在對它進行四則運算時,他們可能會丟棄壹些數而不給妳任何提示。這意味著,如果妳把壹個很小的數加到壹個很大的數上,計算機會丟棄它,因為這個數太小,不能用15或16的精度顯示。如果妳在計算時得到壹個非常奇怪的數字,妳可能需要檢查妳的數據範圍是否合適。
這意味著如果妳做浮點運算,較小的數字很可能被丟棄。雖然這壹點在平時並不明顯,但是如果妳在做要求很高的數學分析,這些錯誤可能會累積,以至於最後的結果非常不準確。
這個誤差對於從事數學研究的人來說是非常重要的。他們必須非常了解誤差,並研究壹些減少誤差的方法,他們應該能夠估計誤差的大小。
對了,“精度”的問題和“範圍”的問題是不壹樣的。前者指尾數的表達範圍,後者指指數的表達範圍。
另壹個不太明顯的錯誤是浮點數的二進制和十進制並不完全相等。如果妳運算的數是2的冪級數的倒數,比如0.75,那麽在二進制中可以準確地標記為0.11,因為它正好是1/2+1/4的值。然而不幸的是,妳可能通常得不到這樣壹個合適的數字,也就是說,計算機會扔掉壹些數字,比如,要表示0.1,妳只能用無窮無盡的二進制十進制0.00011001...
如果妳不明白這部分,不用擔心。這裏的重點是,計算機不是萬能的,它只是壹臺機器,它必須符合壹定的規則,並受到限制。雖然很多人對計算機有著孩童般的信任,但是在計算機的好的解決方案下,還是有壹些不可避免的不準確。
編程語言中的數字
對於低級語言的程序員來說,他們要操心有符號和無符號,定點和浮點運算。他們必須使用非常不同的代碼來實現操作。
但是對於高級語言的程序員來說,比如LISP和Python提供了壹些“有理數”“復數”等抽象數據類型。並且他們可以斷言他們的系統可以使用數學運算來做正確的運算。由於運算符的重載,數學運算可以應用於任何數——無論是有符號數、無符號數、有理數、定點小數、浮點小數還是復數。
文本編碼:ASCII和字符串
既然我們有了不同的存儲數據的方法,那麽文本呢?我們如何存儲姓名、地址或給朋友的信?
當然,如果妳記得壹個比特就是壹個比特,我們沒有理由不能用壹個比特來表達字母“a”或“?”得到壹個“z”什麽的因為很多計算機壹次處理壹個字節,所以用單字節數據表示單個字母比較方便。我們可以用這個:
0100 0110(十六進制46)
為了表示字母“f ”,計算機使用這種“字符編碼”將所需的文本傳送給顯示程序。
以下是存儲西文字母的標準二進制碼,俗稱“美國信息交換碼標準碼”(縮寫為“ASCII”)。以下代碼為ASCII碼,其中“D”代表十進制碼,“H”代表十六進制碼,“O”代表八進制碼:
ASCII碼表_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
ch CTL d h o ch d h o ch d h o ch d h o h o ch d h o _ _ _ _ _ _ _ _ _ _ h o
nul ^@ 0 0 sp 32 20 40 @ 64 40 100 ' 96 60 140 soh ^a 1 1!33 21 41 a 65 41 1 a 97 61 1 stx ^b 2 2 2 2 " 34 22 42 b 66 42 102 b 98 62 142 ETX ^c 3 3 # 35 23 43 c 6338 26 46 f 70 46 106 f 102 66 146貝爾^g 7 7 7 ` 39 27 47g 71 47 107g 103 67 147
英國^h 8 8 10(40 28 50h 72 48 10h 104 68 150 ht ^i 9 9 11)41 29 51 I 73 49 11 I 65438 42 2a 52j 74 4a 112j 106 6a 152 vt ^k 11 b 13 _ 43 2b 53k 75 4b 1107 6b 153 ff ^l 12 c 46 2e 56n 78 4e 116n 10 6e 156 si ^o 15 f 17/47 2f 57 o 79 4f 117o 11116f 15438
dle ^p 16 10 20 0 48 30 60 p 80 50 120 p 12 70 160 DC 1 ^q 17 11 21 1 1 49 31 66638 54 124t 11674 164納克^u 21525 553 35 65 u 85 55 125 u 1175 165 syn ^v 22 16 26 6 654 36
能^x 24 18 30 8 56 38 70 x 88 58 130 x 120 78 170 em ^y 25 19 31 9 57 39 71y 89 59 131y 12159 3b 73[91 5b 133 { 123 7b 173 fs ^\ 28 1c 34 & lt;60 3c 74 \ 92 5c 134 124 7c 174 GS ^]29 1d 35 = 61 3d 75]93 5d 135 } 125 7d 175 RS ^^ 30 136 & gt;62 3e 76 ^ 94 5e 136 ~ 126 7e 176美國^_ 31 1f 37?63 3f 77 _ 95 5f 137 DEL 127 7f 177 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
上面的列表最左邊有壹個奇怪的字符,比如“FF”和“BS”,不是文本字符。取而代之的是控制字符,也就是說當這些字符被發送到特定的設備時,會產生壹些動作。比如“FF”就是換頁或者彈出的意思;“BS”的意思是退格,而“BEL”的意思是壹個音。在文本編輯器中,它們會顯示為白色或黑色的方塊,或者笑臉、音符或其他奇怪的符號。要鍵入這些字符,您可以使用CTRL鍵和合適的代碼。例如,同時按住“CTRL”和“G”,或者縮寫為“CTRL-G”或“G”可以鍵入壹個BEL字符。
上面的ASCII碼意味著定義了128個字符,也就是說ASCII碼只需要7位數字。然而,許多計算機以字節存儲信息。這個額外的位可以定義128的第二個詞集,壹個“擴展的”詞集。
在實踐中,有許多不同的“擴展”單詞集,它們提供了許多符號,如數學符號或非英語字符。這個擴展的詞集是不規範的,經常引起混亂。
這張表強調了本文的主題:比特就是比特。在這種情況下,您可以使用位來表示字符。妳可以把特殊代碼描述為特殊的十進制、八進制和十六進制,但它們仍然是相同的代碼。這些值的表達式,無論是十進制、八進制還是十六進制,都只是同壹位的表達式。
當然,妳可以在壹個段落中表達許多字符,例如:
虎虎燒亮!
這只是由ASCII碼代替,表示為:
54 69 67 65 72 2c 20 74 69 67 65 72 20 62 75...
計算機將這個ASCII“字符串”存儲為壹個連續空間的“數組”。有些應用程序可以包含壹個二進制值來表示字符串的長度,但更常見的是使用字符null(ASCII表中的0字符)來表示字符串的結尾。
請參考: