高等学校情報/情報の科学/負数と小数のデジタル表現

コンピュータは、引き算を計算する時、じつは下記のように足し算に置き換えている。時計のように循環する数だと、なんと足し算で引き算を代用できるのだ。
なお割り算では、引き算の仕組みを活用できる。

さて、コンピュータでは、このような循環的な仕組みを利用して、足し算と引き算を置き換えている。
ならば、引き算をしたい場合は、さきほどの節に書いたように、循環的な数における引き算を足し算に置き換えれば、論理回路(半加算器や全加算器など)で計算できる。
(※ 単元『高等学校情報 情報の科学/論理回路と半導体』でも習うように、足し算をする論理回路は、存在している。)

コンピュータのメモリ内のデータで、マイナスの数を表現する方法には、歴史上、いくつかの方式があった。
いちばん単純な方式は、符号と絶対値に分ける方式である。歴史的には、メモリの最上位のビットを符号用のビットとして、符号ビットが0なら正、符号ビットが1ならマイナスとして、残りのケタで絶対値をあらわす方式も、過去には用いられたことがあった。
ただし現代では、この符号-絶対値方式は、コンピュータの中枢レベルでは、マイナスの表現としては用いられていない。
符号-絶対値の方式だと、演算回路などを実装するのが複雑になってしまったりするという欠点があった。
代わりの方式として現代にマイナスの表現として用いられている方式は、「補数」(ほすう)表現というものである。
なお、コンピュータでは2進数を扱う。とりあえず例として、10進数の0から7までの数(3ビットぶん)の数を下記の表のように定義しよう。


次に、今後の負数の表現のための対応として、最上位にもう1ケタぶんのビットを加え、上記の正の数を下記のように表してみる。


次に、負数の表現方法の規則がまだ定義されていないので、いっそ表現方法の規則のほうをイジくり、私たち人類が演算回路を設計しやすい方式で、負数の表現方法の規則を考えてしまえばよく、そういう方式で規定された表現方法のひとつとして、2の補数(ほすう)がある。

また、この合同式を活用できるようにマイナスの数をビットで定義すると、結果的に、各ビットをビット反転したあとに、プラス1をしたものになる。
だが、ビット反転そのものに数学的に深遠(しんえん)な理屈はあまり無く、あくまでも本質は整数の合同式であり、また、本質的なアイデアは「すべてのビットが1である状態を十進数のマイナス1である」と定義したアイデアである。
左表のように、すべての数が1になる場合を「マイナス1である」1と定義するマイナスの表現方法のことを、2の補数(2のほすう、Two's complement)という.

上記の表から結果だけ抜き出すと、

というような要領で、マイナスの数が定義される。

2の補数を使うことで、減算を加算に置き換えることができる。
ためしに、5-2をしてみよう。5+(-2)を計算するわけだが、上記表により 510は01012, -210は11102 なので、
となり、ケタあふれした最上位の「1」は無視して残り4桁の「0011」が答えになるが、表と照らし合わせると、3であり、たしかに 5-2 になっている。
なぜ、このように2の補数だと減算を加算で処理できるかというと、合同式の考え方を用いているからである。
とりあえず、「2の補数」の予備知識は抜きにして、単純に2進数の合同式の理論を考えてみよう。
1111 の次を 0000 として、10000 を法とする合同式を考えてみよう(なお、これが2の補数の正体)。
そして、10000を法と定義することは、言い換えれば、それから1を引き算した1111をマイナス1であると定義することになる。
なので、すべての数が1になるビットを(十進数の)「マイナス1である」と定義することが、合同式で10000を法とする数の体系を定義したことにつながる。
そして、冒頭の時計でのたとえの節で述べたように、減算を加算で置き換えできる。
時計の場合、マイナス2時をするとき、かわりにプラス10時をしてもよいのであった。
さて、私たちは今、10000(※ 10進数では16)を法とする世界を考えているのだから、
マイナス2をするときは、かわりにプラス14をしてもいいわけだ。 そして、プラス14は2進数で1110なので(8+4+2=14)、プラス1110をするわけ。
このように、2の補数を採用することにより、整数の合同式の成果を活用できるので、回路設計も単純にできる。(証明 おわり)

例として負数のある数 -32.741 などのように、小数部をもつ数字をコンピュータで扱う場合を、これから説明する。
まず、-3.274×10 のように、指数形式に置き換える。
この例の場合の、3.274の部分を「仮数部」(mantissa マンティッサ)という。符号「-」の部分を「符号部」(sign サイン)という。
10の「」の部分を「指数部」(exponent エクスポーネント)という。
そしてコンピュータの記憶部分に、符号部と仮数部と指数部さえ記憶しておけば、あとはそこから数値を復元できる。
なお符号部については、数値が正(プラス)の値のときは符号部は0として、数値が負(マイナス)のときは符号部は1とするのが一般的である。
このように、符号部と仮数部と指数部だけをもとに数値を記録する方式を浮動小数点(英: floating point number)という。
数の記憶は、このような仕組みのため、コンピュータでは有限の桁までしか数を記憶できない。
なので、たとえば、0.44444444444...... のように、無限につづく小数は記憶できない。
コンピュータでは、整数と、有限桁でおわる小数を、記憶する事ができる。コンピュータで記憶できる、これらの、整数と、有限桁で終わる小数をまとめて、「実数」という(数学でいう実数とは、定義が違うので注意。)。
このようにコンピュータでは、有限桁までしか扱えないため、細かい計算を行うときに誤差が発生する可能性がある。 誤差を小さくしたい場合には、数値を記憶するためのビット数を増やせばよいのだが、そのぶんメモリの容量を使用するので、メモリを圧迫する事になる。
さて、コンピュータでは、数値を扱う場合、メモリの使用領域の大きさがあらかじめ数種類に決められており、たとえば、16ビットぶんの記憶領域、または32ビットぶんの記憶領域、または64ビットぶんの記憶領域、のように決められているのが一般的である。

まず、針時計を考えてみよう。1から11までの数字が円周上に配置されている、あの時計だ。
今、5時だとして、それに10時間を足してみると、15時になるが、13時になると1時に戻るので、 つまり、
となり、結果的に3時になる。もとの時刻の5時から見れば、マイナス2時間された事と同じである。(5-2=3)
つまり、このような針時計において、10時をプラスすることは、2をマイナスする事と同じである。
同様に、11時をプラスする事は、マイナス1時をする事と同じである。
同様に、9時をプラスする事は、マイナス3時をする事と同じである。
つまり、循環的な数字を扱う場合13時まで進むと1時に戻る、という循環)、足し算は引き算に置き換えることが出来る。
つまり、プラスA時をする事は、マイナス(12-A)時をする事と同じである。
また、循環的な数において、引き算を足し算に置き換えることもできる。
たとえば今5時だとしてマイナス2時をするとき、かわりにプラス10時をしてもいいわけである。
つまり、マイナスB時は、プラス (12-B) 時と同じである。
じっさい、マイナスでは
いっぽう、プラスでは
となり、たしかに「5時マイナス2時」と「5時プラス10時」はともに「3時」である。
説明の都合で13=1としたが,なにも13=1の循環にかぎらず、循環的な数なら、同様の仕組みが成り立つ。たとえば10=1という循環や、100=1という循環でも、同様の議論が成り立ち、足し算を引き算に置き換えることができる。
さて、13=1の場合にハナシにもどる。
13=1の両辺からそれぞれ1を引いて、
としよう。そのほうが数学的には扱いやすい。

つぎに、24時間の時計を考えよう。
そして、おひるの正午を時刻「0」としてお昼前の午前11時を「マイナス1」時、お昼どきの午後1時を「プラス1時」と考えよう。
すると、これは合同式の理論でいう「24を法とした」数の体系になるが、
さらに、マイナスの数が入っている。
高校数学では、このようなマイナスを含む合同式には深入りしないが(大学の数学の理論でも、あまり考えられていない)、しかし私たちはコンピュータでマイナスの数を扱いたいので、いまここで、マイナスを含む合同式に理論を発明的に考え出して構築してしまおう。

まず、このような時計での時刻の並びかたは、
で、輪っかのように、深夜12のあとに、マイナス11時になるハズだ。
なので、これは、合同式の用語で言えば、24を法とした体系である。(けっして12を法とした体系ではない。24を法としていることに注意せよ。)
このような数の体系で、なんとかして減算を足し算に置き換える方法を発見してしまえば、私たちの勝利である。
では勝利をしに行こう。
まず、天下り的だが、「マイナス11時」を、いったん「13時」に置き換えてしまおう。同様に、「マイナス10時」は「14時である」
すると、数の体系は、
となる。
こう書くと、2行目の終わり「12」の次が、1行目のはじめにもどって「13」となるので、12→13と数が連続につながるので、なんか便利そうである。
さて、私たちは減算を加算に置き換えたいのだった。
まず合同式の理論により、12時間時計では、減算としてマイナスB時することは、プラス12-b 時をすることと同じなのであった。
ならば、00から23までの数字のある24時間時計でも、減算としてマイナスB時することは、プラス24-b 時をすることと同じなのであった。
ならば、負の時刻のある時計ではどうか? つまり、-11時~+11時の数字のある24時間でも、同様の論理が成り立つはずである。
たとえば、5時からマイナス2時するのを、マイナスの時刻のある24時間時計で考えた場合、
まず、負時計のマイナス2時とは、これは正時計の22時のことである。
これは、ちょうど、24-bでb=2とした場合の結果に等しい。
なので結局、0から11までの正の数bにマイナス符号を追加したものは、24-bで置き換えることができる。
「0から12まで」でなく「0から11まで」としているのは、24-12=12 で 計算後が同じ数になってしまうからである。
つまり、置き換えとして、
という置き換え結果になる。
これを、0から23まで順番に数を書いてみたものを、13以上の数をマイナスに置き換えたものに差し替えてみると、
という並びになる。
プラス12の直後は、マイナス11になるのに注意。(直後はマイナス1ではなく、マイナス11である。)
あるいは、プラス12をマイナス12で置き換え
という書き方もある(コンピュータの負数表現は、こっちの書式に近い)。
この場合も、プラス11の直後はマイナス12であることに注意。

24時間時計のプラス0時~プラス11時までの数値は、マイナス時計でも同じ数値であることの注目しよう。
さて、今度は逆にマイナス時計を基準に考え、マイナス1から順に、24時間時計のどの時刻に対応するかの表を書こう。

24時間時計のプラス0時~プラス11時まではマイナス時計でも同じ数値なので省略した。
この対応表から、つまりマイナス1を23に置き換えればイイことが分かる。
つまり合同式の用語で言うなら、一般にNを法とする体系の場合、マイナス1を正の正数 N-1 で置き換えるようにすることができる。
逆に考えると、Nを法とする世界での正の整数 N-1 は、マイナス1のことでもある。
※ 補数の定義で、マイナス1を1111のように全てのビットを1とするように定義するのも、コレ。これにより、補数によるマイナスの表現が、合同式のような循環する数になっている。


では、なぜビット反転したあとにプラス1で、補数のマイナスの計算がうまくいくのか?

2進数だと分かりづらいので、また、時計だと分かりづらいので、10進数で、1000を法とする体系を考える。
この場合、
とりあえず、1234 を反転したものは、8765 であり、
である。
つまり、10進の場合の「反転」とは(私たちの用法では)、もとの数に反転した数を足すと9999になるような数のことである。
なお 2進数の場合の反転なら、0010 の反転は 1101 で、足し算は 0010 + 1101 = 1111 で、1111になるような数だった。
このように、10進の場合の「反転」の定義が、2進の場合の「反転」の定義の自然な拡張になってるだろう事を読者に認めてもらいたい。
つまり、ある2進数をビット反転したものに、もとの数のビットを足すと、絶対に(9999ならぬ) 1111 になるわけだ。

そして、十進数「9999」とは 10000 を法とする世界では、マイナス1のことである。
同様に、二進数「1111」とは 10000 を法とする世界では、マイナス1のことである。

2000 - 0123 を計算する代わりに、引く数を反転してみて
を計算してみよう。
すると、2000 + 9876 = 11876 である。ケタあふれの冒頭「1」を無視すると、 1876 である
いっぽう、反転しない もとの引き算は
となり、反転した数の加算の結果とは、ちょうど1の差であり、引き算のほうが1大きい

これは、どんな数でも、引き算のほうが1大きくなる。
なぜならば、なにか(たとえば0123)を「反転した数」とは、つまり「9999 - もとの数 」のことで(こう定義すると、反転した数ともとの数を足して9999になる)、
さらに 9999 = 10000 - 1 なので

よって、たとえば、
となる。
右辺のマイナス1を移項して右辺に移し、

となる。
さらに、右辺の計算順序を変更し、
と1000を最後にもっていくようにも書ける。
ここで、右辺の最後の加算「10000」はケタあふれをするので、計算しなくても同じである。
なので、合同式のように循環する数では、
と書ける。
よって、引き算は、ある進数で、法となる単位で各ケタを反転した数の足し算プラス1による計算で、引き算を足し算に置き換えることができることが証明された。


日本の高校教育では紹介されないが、実は、半加算器や全加算器と同様に、「半減算器」や「全減算器」といった減算の専用の回路も学術的には知られている。
まず、X-Y の減算(半減算器)の真理値表は、次のようになる。

真理値表の出力の「B」は前のケタからのケタ借り(borrow)の有無。
出力のDは、差(difference)。
たとえば、あるケタが x=0, Y=1 なら、単純計算で X-Y = 0-1 = -1 であるが、この場合、前のケタからケタ借りされるので B=1 である。