タイトルAround the System and Development》データと文字コード(5)数値データの表現カテゴリーAround the System and Development
作成日2002/3/13 15:44:25作成者新居雅行
前回はJISコードの話をしたので、今度はShift-JISかと思うところだろうけど、ちょっと違う、少し戻って、2進数の世界に入りたい。前にも説明したが、電子回路での2通りの状態を2進数で記述することで、物理的な世界とは離れて動作設計などができるというところが、情報工学のはじまりだった。そこで、さまざまな情報を2進数で処理できるようなさまざまな工夫が行われることとなる。たとえば、言語、すなわち文字を扱うためには、文字1つ1つに番号をつけて、その番号を記録すれば、たとえば単語や文章を記録することができる。では、その番号をどう記録するかであるが、すでに答えは出ているものの、精密に考えればちょっとややこしいと思うかもしれない。たとえば、Aは41H=65という番号を割り当ててている。つまり、2進数で示すと0100 0001となる。このままをメモリ素子に記憶させればいいと言えばそれまでだけど、実際に今のコンピュータでも、Aという文字を覚えているメモリの一角は、そこでの電子回路の状態がこのような0と1の並びになっている。
このとき、2進数のいちばん低い桁は、LSB(Least Significant Bit)と呼ばれる。つまり、いちばん右側の桁がLSBだ。そして、いちばん左側の最上位の桁はMSB(Most Significant Bit)と呼ばれる。これは名前だけの問題だけども、一般には、情報はLSBから詰め込まれることが多い。たとえば、数値が0〜5と決まっていれば、LSBから3桁だけを使えばいい。単に1つの数字しか扱わなくてもいい場合には、通常はLSBから使うのである。LeastとMostが逆のような気もするが、こういうものなのだろう。

本来はここで、電子回路についての説明が必要だ。正確には「論理回路」と呼ばれている世界である。電子回路は実際の部品を組み合わせてフリップフロップなどを組み合わせるのだが、論理回路はそうした基本素子を集めてより高度な処理をするためにはどうすればいいかという世界となる。つまり、コンピュータの基本設計部分なのである。だが、これで1冊の本になるような話なので、少しすっ飛ばしてしまうことをお許し頂きたい。論理回路では、2進数で表現される情報の処理をさまざまに組み合わせる。計算機では計算が得意と思うかもしれないが、実は論理回路レベルで定義されている基本的な演算処理は、和と積の2種類と言っても良い。また、厳密には、論理和と算術和という風にあるのだが、これは可能であれば別の回に説明する。ここでは、とにかく2進数の加算の機械が電子回路で作られていると考えていただきたい。
では、単に数値は2進数として表現した形式を、メモリに保存したりディスクに書き込んだりするという場合を考えよう。もちろん、8ビットで1バイトを区切りとした場合、256通りの数値しか扱えない。話を簡単にするために、ここではまず1バイトの状態だけを考えることにする。ここでも厳密に言えば、0を0000 0000B、1を0000 0001B…というふうに割り当てると、0〜255(1111 1111B=FFH)までの整数しか扱えない。だけど、たとえば、1900という数値データを0000 0000B=00Hに割り当てれば、2002という数値データは0110 0110B=66Hとして表現できる。この場合は、1900〜2155までの整数を1バイトで表すことができる。何をややこしいことを…と思うかもしれないが、表計算ソフトの日付データを思い出してほしい。表計算ソフトでは、日付けそのものを覚えるのではなく、基準日からの日数をセルに記録し、その数値をもとに人間が日付として見える文字列を画面に表示している。Excelは1904年1月1日を0として、その日からの経過日数を記録する。たとえば、2002年3月13日なら35866という数値を記録しているのである。こうした基本的な整数の扱いでも、ちょっと独特なのがコンピュータの特徴でもあるだろう。
ところで、0〜255として1バイトをカウントした場合、たとえば、3+4 = 0000 0011B+0000 0100B = 0000 0111Bで7となり計算は可能だ。だけど、254+3 = 1111 1110B + 0000 0011B = 1 0000 0001Bとなり、1バイトを超えてしまう。いきなり2進数の加算は大変かもしれないが、ちょっとメモ帳のはしっこにでも手で書いてみて、10進数と同じように計算してもらえばいい。ここで、仮に記憶領域として8ビット分しか用意されていないのでれば、下から9桁目の1は捨てられる。これが論理回路の常であるが、場合によっては回路上で捨てたかどうかが分かる仕組みを用意している場合もある。また、広くシステムという世界で言えば、ソフトウエア的に捨てたかどうかを判別するようにすることも多い。だが、一般には桁は捨てられるのである。

ここで、数値の扱いをちょっと拡張して、負の数を扱いたいとする。負の数は、正の数にマイナス記号をつけて記述できるのはよく御存じの通りだろう。つまり、符号をなんとかすれば負の数を扱えるかもしれないわけだ。そこで、8ビットのうち、MSBを符号のビットとして、0なら正の数、1なら負の数であるとまず取り決めてしまう。ならば、3は0001 0011B=03Hとなるが、-3はそれじゃあ1000 0011Bとしようかというのがまずは思い付く発想だろう。もちろん、16進数にしたら83Hと本来の-3は想像しづらいものになるが仕方ない。いずれにしても、こうしたやり方は間違いではない。ただ、もっと効果的な方法が考案された。それが補数という扱いだ。こうした負の数の扱いをした場合、加算の処理がややこしくなる。そこを改良したというわけである。
ここで、ある数と、その数の負の数を加えると0になるというのは算数の常識である。つまり3+(-3)=0であり、一般的に数xとその負の-xがある場合、加算したx+(-x)=0となる。ここで、ならば「加えて0になる」ということを実現するためには、1バイトは8桁しかないことを利用する。そして、負の数は正の数のビットを全部反転(0と1を置き換える)して1を加えるという表現を取る。これを2の補数と呼んでいる。(単にビットを反転させただけのものは1の補数と呼ばれる。)
つまり、-1は、0000 0001Bの0と1を反転した1111 1110Bに1を加えて、1111 1111Bとする。-5だと、1111 1011Bとなる。では、10+(-5)を考えて見よう。2進数で書けば、0000 1010B+1111 1011Bとなり、2進数で計算すると1 0000 0101Bとなる。あふれたいちばん上位の1を捨てれば0000 0101B=05H=5となり、ちゃんと計算が合っている。つまり、2の補数は、加算で引き算が可能とも言えるし、負の数にも対応した加算が可能というようになる。
数学的には、引き算a-bについては、a-b = a-b+P-P = a+(P-b-P)となる。ここで、aとbが1バイトの数値だとすると、0〜255が扱えるがPを256としてみる。256 = 1 0000 0000Hである。したがって、0〜255の数値から256を足しても引いても同じものである。つまり、「-P」はあってもなくてもいいわけで、a-b=a+(P-b)となる。すなわち、「bを引く」ということは「P-b」を加えるのと同じになる。ここで、Pは256であるから、たとえばbが5ならP-b=251となって1111 1011B = FBHとなる。
ここで、0〜255を扱えると言ったが、-5は251と同じである。こうした対応を-1から順番にチェックしたいが全部一覧表にしたいくらいだが、今回は結果だけを述べると、…-127 = 1000 0001B = 81H、-128 = 1000 0000B = 80Hとなる。つまり、0〜255とは言うものの、負の数を扱えるようにするには、結果的に128〜255の範囲は負の数に割り当てることになるので、-128〜0〜127の範囲の整数を扱えるということになる。さらにここでは「MSBは符号ビット」と解釈しても問題なくなっていることに気づいていただけただろうか。また、どうして正の数と負の数の範囲が負の数に片寄っているのかということも気づかれるだろう。つまりマイナスは128までなのに、プラスは127と対称でない。これは、理由は簡単で、ゼロというのはMSBが0であり事実上正の数として扱われているからだ。数学的にはゼロは正でも負でもないということになっているのだが、違うと言えば違うところだ。ただ、これは大きな問題ではない。

1バイトを生のまま2進数にすれば、0〜255なのであるが、-128〜127に割り当てることで、この場合は限られた範囲ではあるが、負の数も扱うことができる。だが、どちらもメモリ上の物理的な電気の状態で言えば、同じなのであるが、要は解釈の仕方で違う。もちろん、常に人間が解釈するとは限らず、ソフトウエアはこうした解釈を機能として組み込まれているのが一般的だ。同じ1バイトを読み取るだけでも、0〜255と仮定して処理をするのか、-128〜127として処理をするのかで意味が違ってくる。つまり、この解釈を取り違えたらデータ処理は失敗するのが大きなポイントだと言えるだろう。

∽∽∽∽∽∽∽この項、以上∽∽∽∽∽∽∽[新居雅行]∽∽∽∽∽∽∽
関連リンク