18.3.1 整数の符号化

まずは,最も基本的な数である整数をコンピュータ内で表現する方法について説明します.

非負整数の符号化

非負整数の符号化は 2 進法をそのまま用いて行います.m ビットを用いると 0 から 2m-1 の範囲の整数が表現可能です. たとえば 4 ビットであれば,次の表のようにして 0 から 15 = 24-1 までの整数が表現できます.

数値 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
ビット列 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

計算機,特に CPU (中央演算装置) が 1 回の命令で扱う情報をワード (word) と呼びます.これは計算機固有の値で,現在では 1word が 32 ビットないし 64 ビットの計算機が中心となっています.32 ビットの長さで非負整数を扱う場合,0 から 4294967295 = 232-1 までの範囲を表現できます.

負の整数の符号化

負の整数はどのように表現すれば良いでしょうか? 日常生活では「-1」のように「-(マイナス記号)」を用います. しかし,これでは 0 と 1 以外の記号が必要になり, 2 進符号化には直接利用できません. そこで,通常は 2 の補数表現と呼ばれる方法を用いて負の整数を表します.

m ビットによる 2 の補数表現は次のように定義されます.

  • 一番上の位が 0 である数は,2 桁目から m 桁目までをそのまま 2 進数として読みます.
  • 一番上の位が 1 である数は,2 桁目から m 桁目までの数を 2m から引いた数にマイナスをつけたものを表します.

言葉で書くと少しややこしいですが,例を見れば規則が分かると思います.たとえば 4 ビットの場合,次のようにして -8 = -23 から 7 = 23-1 を表現します.

数値 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7
ビット列 1000 1001 1010 1011 1100 1101 1110 1111 0000 0001 0010 0011 0100 0101 0110 0111

負の数の下 3 桁を見ると, -8, -7, … , -1 と数が増えるにしたがって 000, 001, … , 111 と対応する数が増えていきます.後述しますが,数の増える向きと対応するビット列の下 3 桁の向きが同じになっているおかげで,コンピュータ内では負の数と正の数を統一的に扱うことができます.

このように 2 の補数表現を用いると -2m-1 から 2m-1-1 までの範囲の整数を表現できます.よく使われる 32 ビット整数では, -2147483648 = -231 から 2147483647 = 231-1 の範囲の数が表現可能です.

演算

めでたく整数を符号化できたので,今度は符号化された整数同士の演算を考えてみましょう.

非負整数同士の演算は簡単です.10 進法と 2 進法の違いは単にそれぞれの桁が 10 で繰り上がるか 2 で繰り上がるかだけの違いです.ですから 2 進法での足し算は 10 進法と全く同じように,筆算で行うことができます.たとえば 6(10)+5(10)=11(10) という計算を 2 進法で行うと 0110(2)+0011(2)=1001(2) となります.この計算が筆算でやった結果と同じになることは簡単に確認できるので,ぜひチェックしてみてください.同様に,かけ算や除算 (商と余りの計算) もできます.

実は負の整数が混ざった演算も非負整数と全く同じようにできます.たとえば上の 4 ビット符号付き整数の表を見ると -1(10)=1111(2), 1(10)=0001(2) です.この 1111 と 0001 を非負整数のときと同じように足すと 10000 となりますが,4 ビットで加算をする回路ははみ出た一番上の 1 を落とすよう設計されているので,結果は 0000(2) となります.全く同様に 2 と -2, 3 と -3 ,… を非負整数と同じ規則で足すと 0000 になり,正しい結果を得られることがわかります.このように 2 の補数表現を採用したおかげで,整数が正でも負でもコンピュータはまったく同じ仕組みで計算をすることができます.こうするとコンピュータの中で整数の計算を担う半導体の種類が減り,設計上大変有利になります.

2 の補数表現を用いるとマイナスの計算も簡単にできます.たとえば -3(10)=1101(2) は 3(10)=0011(2) の各ビットを反転させて 1100 にして,その後 0001 を足せば得られます.ですから引き算の計算も,補数を計算してから足し算をすれば実現できます.こうした点も,コンピュータを回路として設計する上では大変有利に働きます.

オーバーフロー

m ビットで表せる非負整数は 0 から 2m-1 までですが,この範囲内の数同士であっても,足し算や引き算の結果が範囲を外れてしまうことがあります.たとえば 1101(2)+0101(2)=10010(2) ですが,4 ビットの範囲でこの計算をやると頭の位の 1 が落ち,結果が 0010(2)=2(10) になってしまいます.このような現象をオーバーフロー (overflow) などと呼びます.もちろん非負整数に限らず,符号付き整数の演算でもオーバーフローは起こります.たとえば 4 ビット整数の場合,7 に 1 が加わると -8 に, 逆に -8 から 1 が引かれると 7 になってしまいます. このように正の数値は負の数値に,負の数値は正の数値に化けてしまいます.

オーバーフローはプログラムのバグの原因になります.たとえば,とあるゲームで「所持金が 0 円の状態で 1 円のアイテムを買ったら,所持金が 255 円になってしまった」という現象が起きました.この理由は 8 ビット整数で 0(2) から無理矢理 1(2) を引いた結果が11111111(2) = 255(10) だからです.オーバーフローが起きると,プログラム内の変数が予期せぬ値に書き換わった状態でプログラムが走り続けます.大きい数を扱うプログラムを書く人は十分注意してください.