読み込み中...コンピュータの数値表現とは、コンピュータなどのハードウェアやソフトウェアで数を表現する実装方法を指す。
コンピュータに詳しくない人は、コンピュータでの数値計算が無謬(誤りがない)であると誤解していることがある。例えば、 を計算すると正確に 1 が得られると期待するかもしれない。しかし、実際にはコンピュータや電卓では 0.9999999999999999 のような結果となり、場合によっては 0.99999999923475 のような値になることもある。
後者の値はバグの存在を示しているわけではなく、二進法の浮動小数点数による近似の結果生じるのである。十進法の浮動小数点数、数式処理システム、ある種の多倍長整数系では答えが 1 や 0.9999999999999999... になる。
ビット(bit)の概念は、1 または 0、on または off、yes または no という二値として理解でき、何らかのトグルスイッチを符号化したものと言える。単一のビットは以下の2種類の状態のどちらかを必ず表す。
| 2進値 | 10進値 |
|---|---|
| 0 | 0 |
| 1 | 1 |
単一のビットはそれ単体では2種類の値しか表せないが、2ビット列にするとより多くの値を表せる。
| 2進値 | 10進値 |
|---|---|
| 00 | 0 |
| 01 | 1 |
| 10 | 2 |
| 11 | 3 |
同様に3ビットでは、2ビットのときの2倍の種類の値を表せる。
| 2進値 | 10進値 |
|---|---|
| 000 | 0 |
| 001 | 1 |
| 010 | 2 |
| 011 | 3 |
| 100 | 4 |
| 101 | 5 |
| 110 | 6 |
| 111 | 7 |
ビット数が増えると、0 と 1 の組み合わせで表される数の種類も指数関数的に増える。上の例では、1ビットでは2種類の値しか表せないが、2ビットでは4種類の値、3ビットでは8種類の値が表せる。ビット数によって表せる値の数は以下のようになる。
| ビット数(b) | 表せる値の数(N) |
|---|---|
| 1 | 2 |
| 2 | 4 |
| 3 | 8 |
| 4 | 16 |
| 5 | 32 |
| 6 | 64 |
| 7 | 128 |
| 8 | 256 |
バイト(byte)は8ビットのビット列を指し、256種類の値を表せる。現代のコンピュータは情報を8ビット単位で処理するか、さらにそれに2の冪をかけたビット列単位(例えば、16ビット、32ビット、64ビット)で処理することが多い。8ビットは基本単位として広く採用されており、「オクテット(octet)」とも呼ばれる。コンピュータ上でアドレス指定可能な最小単位(バイト)も一般にオクテットであり、そのためにオクテットとバイトは同義語として使われることが多くなっている。
オクテットの半分、すなわち4ビットのビット列をニブル(nibble)と呼ぶ。16種類の値を符号化でき、数値に当てはめれば 0 から 15 になる。ニブルの値と数としての解釈はどういうマッピングであってもいいが、一般に以下のように解釈される。
| 2進値 | 10進値 | 2進値 | 10進値 | |
|---|---|---|---|---|
| 0000 | 0 | 1000 | 8 | |
| 0001 | 1 | 1001 | 9 | |
| 0010 | 2 | 1010 | 10 | |
| 0011 | 3 | 1011 | 11 | |
| 0100 | 4 | 1100 | 12 | |
| 0101 | 5 | 1101 | 13 | |
| 0110 | 6 | 1110 | 14 | |
| 0111 | 7 | 1111 | 15 |
他にグレイコードのような順序付けもあるが、上記の順序は一般に使われている十進法のような位取り記数法となっている。例えば以下のような十進数値があるとする。
これは一般に次のように解釈される。
あるいは10のべき乗を使うと次のようになる(ゼロでない数のゼロ乗は1)。
数値の各桁は 0 から 9 の10種類の値をとるので、これを十進法または「底が10」であるという。また各桁はその位置によって10のべき乗で重み付けされている。
同様に二進法でも同じことが言える。十進の13という値は二進では以下のように表される。
各桁は 1 から 0 という2種類の値しかとらないので、これを二進法または「底が2」であるという。また、桁位置ごとの重み付けは次のようになる。
ここで使われている2のべき乗は 1, 2, 4, 8 である。プログラマは 216 までの2のべき乗をよく使うので、暗記していることが多い。
20 = 1 28 = 256 21 = 2 29 = 512 22 = 4 210 = 1,024 23 = 8 211 = 2,048 24 = 16 212 = 4,096 25 = 32 213 = 8,192 26 = 64 214 = 16,384 27 = 128 215 = 32,768 216 = 65,536210 = 1,024 という値を(SI接頭辞とは異なるものの)「キロ」あるいは K と表すことがある(2進接頭辞では「キビ」または Ki)。その場合、それより大きな2のべき乗は次のようにも表される。
211 = 2 K = 2,048 214 = 16 K = 16,384 212 = 4 K = 4,096 215 = 32 K = 32,768 213 = 8 K = 8,192 216 = 64 K = 65,536同様に 220 = 1,024 × 1,024 = 1,048,576 は「メガ」または M と表すことがある(2進接頭辞では「メビ」または Mi)。
221 = 2 M 222 = 4 Mさらに 230 は「ギガ」または G と表される(2進接頭辞では「ギビ」または Gi)。
SI接頭辞と同じ呼び方をすると混乱が生じることがあるため、1998年12月の国際電気標準会議が2のべき乗値を表す新たな単位を策定した。これを2進接頭辞と呼ぶ。
もうひとつ注意しなければならない点として、16ビットで表せる値は65,536種類あるが、これは 0 から 65,535 までの値である。人間は物を数えるとき 1 から数え始めるが、コンピュータでは 0 を最初とすることが多い。その方がプログラムが容易になるからである。
以上がコンピュータでの二進法による数値表現の基本だが、以下のような制限がある。このような制限はあるが、このような符号なし整数は何かを数えるのには非常に便利である。これは非常に単純でコンピュータにも扱いやすい。
八進法と十六進法は二進数値を表現するのに便利で、コンピュータでよく使われている。コンピュータでは二進数値を表示することがよくあるが、単に 1001001101010001 などと表示すると間違いやすい。そこで二進数値を8を底として表したり(base-8、八進法)、16を底として表したり(base-16、十六進法)することが多い。
十進の体系では、10種類の数字(0 から 9)を組み合わせて数値を以下のように表す。
八進の場合、8種類の数字を使う(0 から 7)。
すなわち、八進の "10" は十進の "8" に等しく、八進の "20" は十進の "16" に等しい。
十六進では、16種類の数字を使う(0 から 9 の後に一般に A から F までが続く)。
すなわち、十六進の "10" は十進の "16" に等しく、十六進の "20" は十進の "32" に等しい。
そこで、1001001101010001 のような長い二進数値も容易に八進数に変換できる。
001 001 001 101 010 001 二進 = 1 1 1 5 2 1 111521 八進また、十六進数への変換も容易である。
1001 0011 0101 0001 二進 = 9 3 5 1 9351 十六進しかし、十進数(37713)への変換はこれらよりも面倒である。
八進数値や十六進数値を十進に変換する場合、以下のようなパターンを踏襲する。
(d1 * base + d2) * base + dn........すなわち、最初の桁の数字を底とかけ、それに次の桁の数字を足し、これを再帰的に繰り返せばよい。以下に例を示す。
同様に八進や二進の数値も変換できる。
コンピュータで二進数の負の数を表す方法は本質的なものではない。そのため、符号付整数の表現はいくつか存在する。いずれの場合も符号ビットがあり、多くの場合最左端のビットが符号ビットとされる。すなわち、符号ビットが1なら負の数を表し、0なら正の数を表す。
この方式では、16ビットの二進数は −32,768 から 32,767 に対応し、32ビットでは −2,147,483,648 から 2,147,483,647 に対応することになる。
2の補数の利点は、ほとんどの演算で符号を気にする必要がなく、符号無しの整数と全く同じに扱える点である。
例えば、5 + (-5) は以下のようになる。 0101 +1011 10000ここで、数値は4ビットで表しているので、演算結果も4ビットでなければならず、したがって先頭の1は捨てられ、結果として期待した通りの0が得られる。
演算において符号を気にする必要がないのは、2n を法とした合同式になっているためである。例えば、15 ≡ -1 (mod 16) である。コンピュータは一般に固定のビット数で数値を表すので、法が一定であり理想的である。2の補数表現と符号なし数値の違いは、大小比較方法と表示方法である。
2の補数表現の唯一の奇妙な点として、表現可能な最も小さい数(16ビットなら -32768)の2の補数をとると自分自身になる点が上げられる。しかし、それが問題となることは滅多に無い。
固定小数点数形式は、金銭勘定など浮動小数点数の精度では十分ではない場合、すなわちビジネスにおける計算(表計算ソフトやCOBOLなど)でよく使われる。
整数部と小数部のビット数は、必要とされる精度や範囲に十分なように選ばれる。例えば、32ビット形式では、整数部に16ビット、小数部に16ビットといったように設定される。
桁位置の重み付けは、整数部と小数部で連続的となる。例えば整数部が、8の位、4の位、2の位、1の位となっている場合、小数部は0.5の位、0.25の位、0.125の位と続く。
例: 整数ビット群 小数ビット群 0.5 = ? = 00000000 00000000.10000000 00000000 1.25 = 1? = 00000000 00000001.01000000 00000000 7.375 = 7? = 00000000 00000111.01100000 00000000ただし、この形式では二進では表せない数が出てくる。例えば、1/5(十進では 0.2)は正確に表すことはできず、最も近い値は以下のようになる。
13107 / 65536 = 00000000 00000000.00110011 00110011 = 0.1999969... 十進の場合 13108 / 65536 = 00000000 00000000.00110011 00110100 = 0.2000122... 十進の場合これは、桁を増やしても正確に表すことはできない。1/3 という数値を考えてみよう。これを十進の小数で表すと 0.333333... となって永遠に続く。これを適当な桁で止めると、その数値表現は 1/3 を正確に表すことはできていない。
つまり、十進で有限小数で表せる数が二進数で有限小数になるとは限らない。これを回避するトリックとして、小数ではなく分子と分母を別々に格納した一種の分数として内部で保持する方式がある(四則演算などを分数として行う)。しかし、平方根を求めるなどといった演算はできないし、分数同士の加減算では通分によって分母が表現できないほど大きな値になる危険性もある。
非常に大きな数や非常に小さな数を表すには、浮動小数点数形式を使う。
十進数では、以下のような浮動小数点数形式がよく使われる。
あるいは、より簡潔に以下のように表記する。
この方式の利点は、仮数の桁数だけでは表せない範囲の数値を指数をかけることで表せる点にある。この方式の二進数版がコンピュータ向けに定義されている。最も一般的なものとして IEEE 754 があり、以下のような64ビットの浮動小数点数形式を定義している。
この形式がどう見えるかを示すため、以下のようにメモリ上のバイト毎の内容を示す。
byte 0: S x10 x9 x8 x7 x6 x5 x4 byte 1: x3 x2 x1 x0 m51 m50 m49 m48 byte 2: m47 m46 m45 m44 m43 m42 m41 m40 byte 3: m39 m38 m37 m36 m35 m34 m33 m32 byte 4: m31 m30 m29 m28 m27 m26 m25 m24 byte 5: m23 m22 m21 m20 m19 m18 m17 m16 byte 6: m15 m14 m13 m12 m11 m10 m9 m8 byte 7: m7 m6 m5 m4 m3 m2 m1 m0ここで "S" は符号ビット、"x" は指数ビット、"m" は仮数ビットである。これを計算に使用する場合、以下のように解釈する。
この方式で、十進にして約15桁の有効数字の以下の範囲の数値を表現できる。
| 最大 | 最小 | |
|---|---|---|
| 正の数 | 1.797693134862231E+308 | 4.940656458412465E-324 |
| 負の数 | -4.940656458412465E-324 | -1.797693134862231E+308 |
これ以外に特別な値として NaN(Not A Number)があるが、ここでは解説しない。32ビットの浮動小数点数形式もあり、符号ビット、23ビットの仮数部、8ビットの指数部(エクセス127)から成る。
byte 0: S x7 x6 x5 x4 x3 x2 x1 byte 1: x0 m22 m21 m20 m19 m18 m17 m16 byte 2: m15 m14 m13 m12 m11 m10 m9 m8 byte 3: m7 m6 m5 m4 m3 m2 m1 m0数値としての解釈は以下の通りである。
表現できる数値は以下の範囲である。
| 最大 | 最小 | |
|---|---|---|
| 正の数 | 3.402823E+38 | 2.802597E-45 |
| 負の数 | -2.802597E-45 | -3.402823E+38 |
浮動小数点数も整数と同様、表せる値の範囲がある。また、精度も制限されている。64ビットの浮動小数点数は十進の15桁程度の精度しかない。演算結果の桁数がそれより多い場合、誤差が生じる。例えば、非常に大きな数に非常に小さな数(ゼロに近い数)を加算すると、有効数字の桁の範囲が違いすぎるため元の大きな数が得られる場合がある。浮動小数点数を使った演算では常に誤差が生じる。これを放置して計算を続けていくと、誤差が蓄積して誤った結果が得られる場合がある。
もうひとつの問題は、浮動小数点数の仮数が二進数で表されているため、十進の仮数とは完全に一致しないことがある点である。すなわち、0.75 のような十進でも二進でも有限小数で表せる数なら何の問題もない。しかし、例えば 0.1 という十進の小数は、二進数では 0.000110011... というように無限小数になる。
低級プログラミング言語では、符号の有無や固定小数点か浮動小数点かを気にする必要がある。例えば浮動小数点の加算なのか整数の加算なのかによって、使用する命令が全く違ったものとなる。
しかし、LISPやPythonといった高級プログラミング言語では数値のデータ型はより抽象的であり、rational、bignum、complex などがある。それらの言語では、どんな数値であっても算術演算を正しく処理することが期待できる。演算子オーバーロードによって、符号無し整数、符号付整数、浮動小数点数、複素数などどんな数値であっても全く同じ記述方法で演算が可能となっている。REXXやJavaなどの言語では、十進法の浮動小数点数をサポートしていて、予期しない結果を防ぐことができる。なお、Javaは符号無し整数をネイティブではサポートしていないという欠点がある。
読み込み中...