計算機數值編碼

計算機數值編碼是指電子計算機內部表示數字的方式。通常數值會儲存為位元組序列，而以組成它們的位元數給以名稱，例如 32 位整數、64 位浮點數。對於計算機處理器指令集所使用的位元編碼，通常需要將其格式再轉換以供外界的使用者來判讀，例如列印和顯示。對於紙筆書寫看似相同的文字元號或數值，在不同型別的電子計算機處理器內部中，可能有不同的約定用於整數和實數。大多數計算都是使用適合處理器暫存器的數碼格式來執行，但是某些軟體系統允許使用多種儲存方式，來表示任意大數。

二進製表示法

電腦會將資料用一串二進位制的數字來表示，二進位制數字是由位元所組成，也會將位元組合成較大的單位，像是位元組。

計算機的基本儲存單位是「位元」（bit），透過開關變化設定表達值 0 或 1。位元的概念可以表示為 1 或 0、開或關、是或不是、真或假等等。

一個位元可以表示二個值，而幾個位元可以表示較大的數值。例如，三個位元就可以表示八個不同的數值，如表 1 所示。位元數越多，可以表達的數字也就越多，可以表達的數字會隨著位元數指數成長， n 位數的二進位制數字可以表達 2n 種可能的數字，如表 2 所示。

為了方便理解，會將特定個數的二進位制數字組合起來，變成較大的單位，且有其名稱。

位元組（byte）是長度可以表示字元的二進位制位陣列。在現有大部份的電腦中，位元組是由 8 個位元所組成，由於位元組的定義和表示字元所需要的位元個數有關，有些較舊的電腦可能有不同的位元組定義。在許多計算機系統結構中，位元組是最小的，也就是位址定址的最小單位。例如，即使 64 位元處理器一次可以處理 64 位元，仍然將其記憶體以 8 位元為單位來分割。這就是位元組定址的記憶體。以往有許多 CPU 是以 8 位元的整數倍來讀取資料。位元組由 8 個位元所組成的情形非常常見，但不是標準化的定義，因此有出現八位元（octet）的名稱，明確說明是由 8 個位元所組成的單位。

是由 4 個位元所組成的單位。4 個位元可以表示 16 個值，因此 nibble 也會稱為十六進位制數字。

八進位制和十六進位制數

對二進位制數字的表示方法。計算機通常使用二進位制來表達資料，但是在實際中如果使用像這樣的二進位制：

 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）按以下方式構成排列：

 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 ...

也即是說，十六進位制的「10」相當於十進位制的「16」，十六進位制的「20」相當於十進位制的「32」。

這些表示數值的方法都是表位置的系統，但是它們使用的不是像十進位制那樣的 10，而是分別使用 8 和 16。例如：

 八進位制 756

 = 7 × 82 + 5 × 81 + 6 × 80

 = 7 × 64 + 5 × 8 + 6 × 1

 = 448 + 40 + 6 = 十進位制 494

 十六進位制 3b2

 = 3 × 162 + 11 × 161 + 2 × 160

 = 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 1001 = 十六進位制 9

0010 = 十六進位制 2 1010 = 十六進位制 a

0011 = 十六進位制 3 1011 = 十六進位制 b

0100 = 十六進位制 4 1100 = 十六進位制 c

0101 = 十六進位制 5 1101 = 十六進位制 d

0110 = 十六進位制 6 1110 = 十六進位制 e

0111 = 十六進位制 7 1111 = 十六進位制 f

因此，把一個很長的二進位制數轉換成一個八進位制就變得很簡便，例如：把二進位制 1001001101010001 轉化成八進位制：

1 001 001 101 010 001 二進位制 = 111521 八進位制

轉換成十六進位制則是：

1001 0011 0101 0001 = 9351 十六進位制

因為要把它轉換成十進位制（37,713）比較複雜，八進位制和十六進位制使得轉換二進位制機器級的數字變得簡單方便。

有符號整數和補碼

在定義負數中，有兩種普遍的方法，分別是「無符號整數」及「有符號整數」。

3.1 無符號整數

這個方法保留了一個位來表示數值的符號。這個「符號位」可以位於數值的最左邊，也可以位於數值的最右邊。如果這個符號位為 0，表示數值是正的，如果這個符號位為 1，表示數值是負數。然而這樣做對於計算機來說有可能帶來一些缺點。這種編碼使得 0 可以有正負兩種，有符號整數會浪費掉一個表達數值的範圍

3.2 有符號整數

更優勝的表達方式是對給定的位數的二進位制數按其範圍分成兩半，其中前一半用來表示負數。例如，在 4 位數值中，你可以得到：

0000 = 十進位制 0

0001 = 十進位制 1

0010 = 十進位制 2

0011 = 十進位制 3

0100 = 十進位制 4

0101 = 十進位制 5

0110 = 十進位制 6

0111 = 十進位制 7

1000 = 十進位制 - 8

1001 = 十進位制 - 7

1010 = 十進位制 - 6

1011 = 十進位制 - 5

1100 = 十進位制 - 4

1101 = 十進位制 - 3

1110 = 十進位制 - 2

1111 = 十進位制 - 1

這個有符號整數的系統使用所知道的「補碼」編碼方式。

對 16 位元有符號數字編碼來說，可以為從 - 32,768 到 32,767 的數編碼。

對一個 32 位元的有符號編碼系統來說，可以為從 - 2,147,483,648 到 2,147,483,647 的數編碼。

與只改變符號位來表示負數的編碼方式相比，「補碼」編碼方式與之有所不同。例如對於 - 5 來說，只對符號位編碼，應該是：

1101

但是對於「補碼」編碼方式來說，則是：

1011

這對於符號編碼來說是 - 3。關於為什麼計算機要使用補碼這種編碼方式會在後面解釋。

所以，可以以二進位制方式來表示正負兩種不同的數值。請記住對於一個二進位制數來說，只有兩種解釋方式。如果在記憶體中有一個這樣的二進位制數值：

1101

-- 這隻能解釋為十進位制的「13」或「-5」。

定點小數

這種格式通常被用於商業計算（例如在電子表格或 COBOL 中）；因為在這裡，丟棄小數位來記錄金錢是不能接受的。因此瞭解二進位制如何存貯小數是十分有用的。

首先必須決定要用多少位元來存貯小數部分和多少位元來儲存整數部分。假設使用 32 位元來表示這種格式，那麼用 16 位元表示整數部分，16 位元來表示小數部分。

小數部分可以沿用表示整數的方式：如果 8 位元接下來是 4 位元，是 2 位元，1 位元，接下來就是半位元，1/4 位元和 1/8 位元等等。

例如：

整數位 小數位

0.5 = 1/2 = 00000000 00000000.10000000 00000000

1.25 = 1 1/4 = 00000000 00000001.01000000 00000000

7.375 = 7 3/8 = 00000000 00000111.01100000 00000000

如果要表達 1/5（十進位制的 0.2），那麼不能得到精確的數值表達方式。最好的方法只能是：

 13107/65536 = 00000000 00000000.00110011 00110011 = 0.1999969... 十進位制

 13108/65536 = 00000000 00000000.00110011 00110100 = 0.2000122... 十進位制

一些小數使用二進位制的方式不能精確地表達出來。除非使用一個特殊的辦法。這個特殊的辦法是分別使用兩個數字來表達小數：一個是分子，一個是分母。然後可以使用學校學習的加、減、乘、除來得到它們。然而，這些方法不能表達更高階的數字（例如平方根），或者如果這兩個分母的最小公倍數很大的話，那就難以使用。這就是使用定點小數表達小數的缺點。

浮點小數

當使用了有符號和無符號的數值表達方式時。如果遇到連 32 位元也不足以表達的大範圍的數，或也許可以表達，但必須為此放棄小數位時，可以選擇的以獲得更大範圍的數值的表達方式的方法是使用「浮點小數」格式而拋棄「定點小數」格式。

在十進位制中，對以下的表達方式很熟悉：

 1.1030402 × 105 = 1.1030402 × 100000 = 110304.02

或簡寫成：

1.1030402E5

這表示「1.103402 乘以一個 1 後面跟著 5 個零的數」。可以得到一個確定的稱謂「尾數」的數值（1.1030402），乘以一個 10 的某個冪級數 (E5，表示 105 或 100,000），也就是「冪級數」。如果使用一個負的冪級數，那就意味著乘以該正級數的倒數。例如：

 2.3434E-6 = 2.3434 × 10-6 = 2.3434 × 0.000001 = 0.0000023434

使用這種定義的好處是可以得到更大範圍的數值，雖然尾數部分的精確度受到影響。相似的原理可以應用於二進位制中為計算機使用。人們設計了很多方式，但是作常用的是由美國電器和電子工程師協會定義的一種方法。它對 64 位浮點格式的定義是：

11 位元二進位制表示指數，使用「超 1023」格式。這種格式使得指數可以表示為 0 到 2047 的無符號數，但是在得到真實值前必須先減去 1023；
52 位元尾數，使用無符號數字表示；
一個符號位；

下面使用一個例子來了解計算機如何使用記憶體的 8 位元組儲存這些數字：

第 0 位元組： S x10 x9 x8 x7 x6 x5 x4

第 1 位元組： x3 x2 x1 x0 m51 m50 m49 m48

第 2 位元組： m47 m46 m45 m44 m43 m42 m41 m40

第 3 位元組： m39 m38 m37 m36 m35 m34 m33 m32

第 4 位元組： m31 m30 m29 m28 m27 m26 m25 m24

第 5 位元組： m23 m22 m21 m20 m19 m18 m17 m16

第 6 位元組： m15 m14 m13 m12 m11 m10 m9 m8

第 7 位元組： m7 m6 m5 m4 m3 m2 m1 m0

其中的「S」表示符號位，「X」表示指數位（階碼？），「m」表示尾數位。一旦這些數字被讀取，它將轉換成：

 × (1 +) × 2 - 1023

這個方式提供的數字範圍為：

這種方式也定義了一些不是數字的值，例如「NaNs」（「不是一個數字」）。這通常用來返回表示數字溢位的資訊。一些程式使用 32 位元浮點小數。最普遍的是使用 32 位元尾數，1 位元符號位，8 位元階碼和「超 127」格式。它提供 7 位十進位制數字。

第 0 位元組： S x7 x6 x5 x4 x3 x2 x1

第 1 位元組： x0 m22 m21 m20 m19 m18 m17 m16

第 2 位元組： m15 m14 m13 m12 m11 m10 m9 m8

第 3 位元組： m7 m6 m5 m4 m3 m2 m1 m0

數字轉換時使用：

 × (1 +) × 2 - 127

它的範圍為：

通常可以把 32 位元的二進位制浮點數稱為「單精度」浮點數，而把 64 位元的二進位制浮點數稱為「雙精度」浮點數。如果使用 real 的時候，通常表示雙精度的浮點數；而使用 float 的時候，通常指單精度的浮點數。

但是要記住的是，浮點數在計算機的儲存是連續的：當計算機記憶體中有一個 64 位元資料時，它可能是一個雙精度的浮點數，也可能是兩個單精度的浮點數，或 4 個有符號或無符號的整數，或其它 8 位的資料，這取決於計算機如何讀取它們。如果計算機把 4 個無符號整數以雙精度浮點小數方式讀出的話，它可以得到一個雙精度浮點小數，但這個資料可能是一個垃圾資料。

所以，現在雖然已經解決了正、負數的存貯方式，但是對於浮點數來說，仍然存在一些與整數一樣的缺陷，例如：

像整數一樣，浮點數也有範圍。雖然得到了比整數要大得多的範圍，但是仍然是有限的，如果試圖把兩個很大的數乘起來，可能會得到「資料上溢」的錯誤。而如果把一個小的數字除以一個函大的數字，那就可能使得指數的數值出錯，出現「資料下溢」的錯誤。通常把最大值稱為「機器無窮」，因為它是計算機所能處理的最大的數字。
另一個問題是精度。雖然有 15 位元的數字來表示很大的數，但是當對它進行四則運算的時候，它們可能不給任何提示就把一些數字丟棄。這意味著，如果把一個很小的數加到一個很大的數值上去的時候，由於這個數字太小，以至於 15 位元或 16 位元的精度都不能顯示它，計算機就會把這個數字丟棄。如果在做計算的時候得到一個十分奇怪的數字，可能需要檢查資料範圍是否合適。

這意味著，如果做浮點計算，較小的數字很可能被丟棄了。雖然這在平常來說並不明顯，但是如果在做要求很高的數學分析工作，這些錯誤可能會累積起來，以至於最後得到的結果十分不準確。

這個錯誤對進行數學研究的人來說十分重要，他們必須對誤差十分了解，以及研究一些辦法來減少誤差，並且應該可以估計到誤差的大小。

順便說一句，「精度」問題與「範圍」問題不同，前者指的是有關尾數的表示範圍，後者指的是指數的表達範圍。

另外一個不太明顯的誤差是由於浮點數的二進位制和十進位制並不完全相等。如果操作的數好是 2 的冪級數的倒數，例如 0.75，那是用二進位制可以準確的標示為 0.11，因為它剛好是 1/2+1/4 的值。可是不幸的是，可能通常不會得到如此恰到好處的數字，這就是說，計算機會把一些數字丟掉，例如要表示 0.1，只能使用無窮迴圈的二進位制小數 0.000110011…… 表示。

計算機不是萬能的，它只是一部機器，並要符合一定的規則和受到一定的限制。在計算機雖好的解決方法下也有一些不可避免的不精確。

程式語言中的數

對於低階語言的程式設計者來說，他們要擔心有符號和無符號、定點和浮點數的運算。他們必須使用十分不同的程式碼來實現操作。

但是，對高階語言的程式設計者來說，諸如 LISP 和 Python 提供了一些列諸如「有理數」、「複數」之類的抽象資料型別。而他們可以斷言他們的系統可以使用數學操作做正確的運算。由於運算子過載，數學運算可以應用於任何數字 —— 無論是有符號的、無符號的、有理數、定點小數、浮點小數或複數。

字元編碼：ASCII 和字串

對於字元，例如姓名、地址或寫給朋友的信，如果還記得幾位是幾位的話，當然可以使用數字來表達字元「A」或「?」或「Z」。計算機同理。計算機每次處理一個位元組，所以使用單位元組的資料來表達單個字元會很方便。可以使用這個：

0100 0110 (hex 46)

來表示字元「F」。計算機使用這樣的「字元編碼」來向顯示程式傳送要求的文字。

下面是一個用來儲存西方字母的標準二進位制編碼，稱為「美國資訊交換標準碼」（英文簡稱「ASCII」），下面的編碼為 ASCII 編碼，使用「d」表示十進位制 (decimal) 編碼，「h」表示十六進位制 (hexadecimal) 程式碼，「o」表示八進位制 (octal) 程式碼：

ASCII 碼錶

ch ctl d h o ch d h o ch d h o ch d h o

NUL ^@ 0 0 0 sp 32 20 40 @ 64 40 100 ' 96 60 140

SOH ^A 1 1 1

33 21 41 A 65 41 101 a 97 61 141

STX ^B 2 2 2 " 34 22 42 B 66 42 102 b 98 62 142

ETX ^C 3 3 3 # 35 23 43 C 67 43 103 c 99 63 143

EOT ^D 4 4 4 $ 36 24 44 D 68 44 104 d 100 64 144

ENQ ^E 5 5 5 % 37 25 45 E 69 45 105 e 101 65 145

ACK ^F 6 6 6 & 38 26 46 F 70 46 106 f 102 66 146

BEL ^G 7 7 7 ` 39 27 47 G 71 47 107 g 103 67 147

BS ^H 8 8 10 ( 40 28 50 H 72 48 110 h 104 68 150

HT ^I 9 9 11 ) 41 29 51 I 73 49 111 i 105 69 151

LF ^J 10 a 12 * 42 2a 52 J 74 4a 112 j 106 6a 152

VT ^K 11 b 13 _ 43 2b 53 K 75 4b 113 k 107 6b 153

FF ^L 12 c 14 , 44 2c 54 L 76 4c 114 l 108 6c 154

CR ^M 13 d 15 _ 45 2d 55 M 77 4d 115 m 109 6d 155

SO ^N 14 e 16 . 46 2e 56 N 78 4e 116 n 110 6e 156

SI ^O 15 f 17 / 47 2f 57 O 79 4f 117 o 111 6f 157

DLE ^P 16 10 20 0 48 30 60 P 80 50 120 p 112 70 160

DC1 ^Q 17 11 21 1 49 31 61 Q 81 51 121 q 113 71 161

DC2 ^R 18 12 22 2 50 32 62 R 82 52 122 r 114 72 162

DC3 ^S 19 13 23 3 51 33 63 S 83 53 123 s 115 73 163

DC4 ^T 20 14 24 4 52 34 64 T 84 54 124 t 116 74 164

NAK ^U 21 15 25 5 53 35 65 U 85 55 125 u 117 75 165

SYN ^V 22 16 26 6 54 36 66 V 86 56 126 v 118 76 166

ETB ^W 23 17 27 7 55 37 67 W 87 57 127 w 119 77 167

CAN ^X 24 18 30 8 56 38 70 X 88 58 130 x 120 78 170

EM ^Y 25 19 31 9 57 39 71 Y 89 59 131 y 121 79 171

SUB ^Z 26 1a 32 : 58 3a 72 Z 90 5a 132 z 122 7a 172

ESC ^ 27 1b 33 ; 59 3b 73 91 5b 133 { 123 7b 173

FS ^\ 28 1c 34 62 3e 76 ^ 94 5e 136 ~ 126 7e 176

US ^ 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 個字集，一個「擴充套件」字集，例如 EASCII

除了 EASCII，還有很多不同的「擴充套件」字集，提供很多例如數學符號等的符號或非英語字元。這個擴充套件字集並沒有進行標準化，並經常會引起混淆，同樣的字串在不同的裝置上顯示不同的內容

這個表格強調了位元就是位元。這樣的話，可以使用位來表示字元。可以把特殊的程式碼描述成特殊的十進位制、八進位制和十六進位制，但是它們仍然是相同的程式碼。這些數值的表達，無論是十進位制、八進位制或十六進位制，都只是相同的位的表達。

在一段話中表達很多的字元，例如：

 Tiger, tiger burning bright!

以 ASCII 碼，會表示成：

 54 69 67 65 72 2c 20 74 69 67 65 72 20 62 75 ...

計算機把這種 ASCII「字串」以連續空間的「陣列」來儲存。一些應用程式可以包括一個二進位制數值表示字串的長度，但是更通常的做法是使用一個表示結尾的字元 NULL（ASCII 表中的 0 字元〕表示字串的結束。