コンピュータの数値表現
コンピュータの数値表現（コンピュータのすうちひょうげん）では、コンピュータシステムにおける数の表現方法について解説する。数学では「数」の概念は複素数などに大きく広がっているが、この記事では冒頭部を除くと、もっぱら固定長の整数の、しかもコンピュータの内部の話に偏っていて、数学的な議論は多くない。実数の近似表現などについては浮動小数点数の記事や任意精度演算の記事を、代数的数のコンピュータでの扱いなどといった話題については、適切な参考文献を参照のこと。
コンピュータでの数値計算には誤差がないという誤解がある。例えば、formula_1 を計算すると正確に 1 が得られると期待するかもしれない。しかし実際には、コンピュータや電卓では 0.9999999999999999 のような結果となったり、場合によっては 0.99999999923475 のような値になることもある。後者の値 (0.99999999923475) はバグではなく、二進法の浮動小数点数による近似の結果生じる。ある種の任意精度演算系や、数式処理システムでそういった演算に対応している場合は、1 や 0.9999999999999999... という結果が得られるものもある。また、電卓などでは、内部計算において9がいくつか連続する場合は繰り上がりをするといった処理により、表示画面においては誤差がないようにみえることもある。なお、十進法の浮動小数点数では誤差が発生しないという誤解もあるが、十進法の浮動小数点数でも本来なら 0.9999999999999999 のような数になる。実数型という用語は、実数を正確に表せる型という誤解を生む可能性がある。
コンピュータにおける二進法による非負整数値の表現を説明する。
ビット（bit）の概念は、1 または 0、on または off、yes または no などの二値として解釈でき、何らかのトグルスイッチを符号化したものといえる。単一のビットは下表の2種類の状態のどちらかを必ず表す。コンピュータの論理回路である電子回路では、電位の高い (High) 低い (Low) を使うことも多いから、HとLという表現が使われることも多い。
1ビットの値は2種類ある。
単一のビットでは2種類の値しか表せないが、2ビットの組合せでより多くの値が表せる。
2ビットの値は4種類ある。
3ビットでは、さらに2倍の種類の値を表せる。
3ビットの値は8種類ある。
ビット数が増えると、0 と 1 の組合せで表される数の種類も指数関数的に増える。上の例では、1ビットでは2種類の値しか表せないが、2ビットでは4種類の値、3ビットでは8種類の値が表せる。ビット数 "b" によって表せる値の個数 "N" は、下表のとおりになる。
バイト（byte）は8ビットで、256種類の値を表せる。バイトを単位とするコンピュータの多くは、ワードとして8に2の冪をかけたビット数（例えば、16ビット、32ビット、64ビット）の単位をよく使うことが多い。
なお、バイトは必ずしも8ビットではなく、5ビット〜9ビット程度をバイトとするコンピュータもあったことなどから、「オクテット」という語もある。オクテットは必ず8ビットであり、任意のコンピュータを接続する可能性を考慮しているコンピュータネットワークの分野では「オクテット」を好んで使うことも多いが、近年はバイトの語も、ほぼ8ビットを指すようになっている。
オクテットの半分、すなわち4ビットのビット列をニブル（nibble）と呼ぶ。16種類の値を符号化でき、非負整数に当てはめれば 0 から 15 になる。目的によっては、以上のような方法とは異なる表現法が便利なこともある。例えば、グレイコードなどがある。
八進法と十六進法は二進法の3桁と4桁に直接対応するので、コンピュータ関連の数を表現するためなどによく使われている。たとえば、整数値を二進法で 1001001101010001 などと表示しても、ぱっとその値を把握することはできない。なので、八進法や十六進法がよく使われる。
十進の体系では、10種類の数字（0 から 9）を組み合わせて数値を以下のように表す。
八進の場合、8種類の数字を使う（0 から 7）。
すなわち、八進の "10" は十進の "8" に等しく、八進の "20" は十進の "16" に等しい。
十六進では、16種類の数字を使う（0 から 9 の後に一般に A から F までが続く）。
すなわち、十六進の "10" は十進の "16" に等しく、十六進の "20" は十進の "32" に等しい。
これらはいずれも位取り記数法だが、十進法の桁の重み付けが10のべき乗であるのに対して、八進法では8のべき乗、十六進法では16のべき乗になっている。十六進や八進による表現から整数値を得るには、十進や二進などの他の記法の場合と全く同様に、各桁の数字にその桁位置の重み付け値をかけ、それらの総和を求めればよい。例えば、
また、八進法の1桁は二進法の3桁にそのまま対応する。
同様に、十六進法の1桁は二進法の4桁にそのまま対応する。
そのため、二進による表現は、1001001101010001 のように長くても、単に数桁毎に区切って、そのまま書き換えるだけで、八進や十六進による表現に、書き換えできる。
しかし、十進には、これらのような単純な書き換えで変換することはできず、整数値を経由して普通に変換することになる。
符号付きの数値の表現法はいくつか存在する。必ずしも「符号ビット」があるとは限らない。例えば、ゲタ履き表現では、ゲタの値によっては「符号ビット」としては扱えない。符号ビットがある場合は、最上位ビットが符号ビットのことが多い。なお、符号ビットについて「1なら負の数を表し、0なら正の数を表す」といったように考えるのが自然なのは「符号と絶対値」表現の場合だけであり、たとえば「2の補数」表現であれば、最上位ビットは −(2) の重みを持つ桁である、と解するのが自然である。
「符号と絶対値」表現は、1ビットの符号ビットと、絶対値を表す残りのビット群からなる。例えば、4ビットの場合で、MSBの、0が正、1が負を示すものとすると、
となる。浮動小数点数はたいていがこのような形をしている。ただし、絶対値の部分が指数部と仮数部に分かれている。
1の補数とは、数のビット毎の反転が符号を反転させるとする表現である。これはビット単位のNOT演算に他ならない。例えば、
1の補数でも符号-仮数部表現でも、ゼロの表現が二種類存在するという問題がある。このため、どちらも最近のコンピュータでは滅多に使われない。1の補数では、
符号-仮数では、
最近のコンピュータでは2の補数表現が広く使われている。2の補数は、ビット毎のNOT演算を施した後に1を加算することで得られる。例えば、
したがって、
この方式では整数であれば、16ビットで −32,768 から 32,767 の範囲、32ビットでは −2,147,483,648 から 2,147,483,647 の範囲を表現する。
2の補数の利点は、加減算かつオーバーフローを考えない範囲に結果が収まるような限りであれば、符号を気にせず符号無しの整数と同様に扱える点である。
例えば、5 + (-5) は以下のようになる。
ここで、数値は4ビットで表しているので、演算結果も4ビットでなければならず、したがって先頭の1は捨てられ、結果として期待した通りの0が得られる。
演算において符号を気にする必要がないのは、2 を法とした合同式になっているためである。例えば、15 ≡ -1 (mod 16) である。コンピュータは一般に固定のビット数で数値を表すので、法が一定であり理想的である。2の補数表現と符号なし表現の違いは、大小比較方法と表示方法である。
2の補数表現の唯一の奇妙な点として、表現可能な最も小さい数（16ビットなら -32768）の2の補数をとると自分自身になる点が上げられる。しかし、それが問題となることは滅多に無い。むしろ、符号無しの場合にオーバーフローになる演算が、2の補数表現ではごくあたりまえの、0をまたいだ計算（正確には、負の整数と非負整数にまたがった計算）であることのほうが、慣れるまでは非直感的かもしれない。
固定小数点数形式は、金銭勘定など浮動小数方式と相性が悪い場合、すなわちビジネスにおける計算（表計算ソフトやCOBOLなど）でよく使われる。「浮動小数点数の精度では十分ではない場合に採用される」というのは誤解である。単精度の場合に足りないようであれば、それは同じ長さの固定小数方式であっても足りてない。
整数部と小数部のビット数は、必要とされる精度や範囲に十分なように選ばれる。例えば、32ビット形式では、整数部に16ビット、小数部に16ビットといったように設定される。
桁位置の重み付けは、整数部と小数部で連続的となる。例えば整数部が、8の位、4の位、2の位、1の位となっている場合、小数部は0.5の位、0.25の位、0.125の位と続く。
例:
ただし、この形式では二進では表せない数が出てくる。例えば、1/5（十進では 0.2）は正確に表すことはできず、最も近い値は以下のようになる。
これは、桁を増やしても正確に表すことはできない。1/3 という数値を考えてみよう。これを十進の小数で表すと 0.333333... となって永遠に続く。これを適当な桁で止めると、その数値表現は 1/3 を正確に表すことはできていない。
つまり、十進で有限小数で表せる数が二進で有限小数になるとは限らない。これを回避する方法として、小数ではなく分子と分母を別々に格納した一種の分数として内部で保持する方式がある。しかし、平方根を求めるなどといった演算はできない。また、分数同士の加減算では通分によって分母が表現できないほど大きな値になる可能性があるため、「有理数型」というデータ型として、分母分子ともに多倍長整数で表すことが普通である。
絶対値が非常に大きな数や非常に小さな数を扱うには、浮動小数点方式を使う。
まず、コンピュータの内部表現ではなく、日常的な十進での表記で説明する。浮動小数点方式では、数を次のように表記する。
あるいは、より簡潔に以下のように表記する。
これは、「1.103402 と1の後に5個ゼロが続く値をかける」ことを意味する。1.1030402 を「仮数」、それにかける10のべき乗（E5、すなわち 10）を「指数」と呼ぶ。指数が負の場合、1の前に小数点とゼロがある値をかけることを意味する。例えば、
この方式の利点は、仮数の桁数だけでは表せない範囲の値を指数をかけることで表せる点にある。この方式の二進版がコンピュータ向けに定義されている。近年において最も一般的なものとして IEEE 754 があり、一例として以下のような「倍精度」の二進浮動小数点数形式（binary64）を定義している。この形式がどう見えるかを示すため、以下のようにメモリ上のバイト毎の内容を示す。
ここで "S" は符号ビット、"x" は指数ビット、"m" は仮数ビットである。これを計算に使用する場合、以下のように解釈する。
この方式で、十進にして約15桁の有効数字の以下の範囲の値を表現できる。
これ以外に特別な値として NaN（Not A Number）があるが、ここでは解説しない。32ビットの浮動小数点数形式もあり、符号ビット、23ビットの仮数部、8ビットの指数部（エクセス127）から成る。
数値としての解釈は以下の通りである。
表現できる値は以下の範囲である。
浮動小数点数も整数と同様、表せる値の範囲がある。また、精度も制限されている。64ビットの浮動小数点数は十進の15桁程度の精度しかない。演算結果の桁数がそれより多い場合、誤差が生じる。例えば、非常に大きな数に非常に小さな数（ゼロに近い数）を加算すると、有効数字の桁の範囲が違いすぎるため元の大きな数が得られる場合がある。なお、拡大解釈して「浮動小数点数を使った演算では常に誤差が生じる」などというのは間違いである。たとえば、1.0 + 1.0 は厳密に 2.0 になる。
また、二進の浮動小数点表現の問題として、人類が多用する十進の小数表現でいわゆる「きりが良い」数との相性が悪い場合の存在がある。すなわち、0.75 のような十進でも二進でも有限小数で表せる数なら何の問題もない。しかし、例えば 0.1 という十進の小数は、二進では 0.000110011... というように無限小数になる。
低水準言語では、符号の有無や固定小数点か浮動小数点かを気にする必要がある。例えば浮動小数点の加算なのか整数の加算なのかによって、使用する命令が全く違ったものとなる。
LISPやPythonといった高水準言語では数値のデータ型はより抽象的であり、"rational"、"bignum"、"complex" などがある。それらの言語では、低水準言語と比べ、より抽象化され扱いやすいものとしてそれらの値が提供される。しかし「どんな数値であっても算術演算を正しく処理することが期待できる」というのは間違いで、コンピュータでは真の実数を扱うことはできないのだから、これらのデータ型でも同様に、それぞれの性質に注意して扱わねばならないことに違いは無い。演算子オーバーロードなどによって、符号無し整数、符号付整数、浮動小数点数、複素数など、複数のデータ型に対して、同じ見た目の式になるような言語もある。