シリーズ「コンピュータの計算のしくみ」 第7回
回路が「割る」とは?
最後に残った、ひとつ
第 2 回で、残るのは引き算・掛け算・割り算だと書いた。
引き算は、加算器にスイッチを 1 つ足すだけで足し算に化けた。 掛け算は、AND と配線と加算器に分解された。
残っているのは、割り算である。
ここまでの 3 つには、共通点があった。 どれも入力を入れれば、信号が通り抜けて答えが出る。回路は何も迷わない。
割り算だけが違う。 筆算で商を立てるとき、私たちは一度「引けるだろうか」と試している。 引けなければ、書いた商を消してやり直す。
回路に、試させることはできるのだろうか。
引けるだけ、引いてみる
割り算は引き算の繰り返しだ、とも言える。 13 ÷ 3 なら、13 から 3 を引けるだけ引けばいい。
13 → 10 → 7 → 4 → 1。4 回引けて、1 が残った。 つまり商は 4、余りは 1 である。たしかに割り算になっている。
そして私たちはもう、引き算ができる回路を持っている。新しい部品は 1 つも要らない。
ただし、回数を考えてほしい。 引く回数は、商そのものと同じだけ必要になる。
4 桁どうしなら、最大でも 15 回。まあ、待てなくもない。 けれど 8 桁なら最大 255 回。32 桁なら——最大で約 43 億回。
第 6 回で、掛け算を「足し算の繰り返し」でやろうとして同じ数を見た。 素朴なやり方は、桁が増えると現実的でなくなる。
そのときの答えは、筆算だった。今回も、同じところに戻ってみよう。
筆算は、なぜ少ない手数で済むのか
十進法の割り算の筆算は、こんな形だった。138 ÷ 6 を書いてみる。
答えは 23 である。 さっきのやり方なら、6 を 23 回引かなければならなかった。 けれどこの紙の上で、引き算は2 回しか書かれていない。
やっていたのは、次のことである。
- 上の桁から見て、6 は 13 に 2 回入る。2 を立てて、12 を引く。残り 1。
- 次の桁 8 をおろして 18。6 は 18 に 3 回入る。3 を立てて、18 を引く。残り 0。
引き算の回数が、商の大きさではなく割られる数の桁数で決まっている。 これが筆算の速さの正体だ。
けれど、ここで私たちは少しずるいことをしている。 「6 は 13 に何回入るか」を、どこかで知っているのだ。
これは九九の逆引きである。 割る数が 2 桁になると、この逆引きはうまくいかなくなる。 「23 は 178 に 8 回くらいかな」と見当をつけて、引いてみて、大きすぎたら消して 7 に書き直す。
小学校で消しゴムを使ったのは、この瞬間だったはずだ。 これが「試す」である。
2 進法では、試すことが 2 択になる
さて、十進法では、商の 1 桁は 0 から 9 までの 10 通りあった。 だから「何回入るか」を探さなければならなかった。
2 進法では、商の 1 桁は 0 か 1 しかない。
つまり、探すことがない。問いはこう変わる。
「何回入るか」ではなく、「入るか、入らないか」
入るなら商のその桁は 1、入らないなら 0。それだけである。
第 6 回で、2 進法の九九は 4 通りしかなかった。 割り算でも同じことが起きている。割り算の九九も、覚えるべき表も要らない。
必要な判断は、たった 1 つに減った。
引けるか?
「引けるか」は、どうすれば判るか
引けるかどうかを知るには、2 つの数の大小を比べればよい。 けれど、比べるための新しい回路を作る必要はない。
引いてみればいい。
第 3 回で、引き算は加算器のスイッチ 1 つになった。 第 4 回で、答えの一番左の桁を見るだけで、プラスかマイナスかが一目で分かることを見た。 一番左の桁を -8 の位と読む、あの話である。 この記事では、その一番左の桁のことを符号の桁と呼ぶことにする。
この 2 つを重ねると、こうなる。
- とりあえず引いてみる(加算器にやらせる)
- 答えの一番左の桁を見る
0なら引けた。1ならマイナス、つまり引けなかった
「試す」の正体は、これだった。 引いてみて、符号の桁を 1 マスだけ見る。
ここでも、新しい部品は出てこない。
ただし、引けなかったときには後始末がいる。 引いてしまった結果はまちがいなので、元に戻さなければならない。 戻すのは簡単で、引いた数をもう一度足せばいい。
引き戻し法
手順としてまとめる。4 桁 ÷ 4 桁なら、4 ステップで終わる。
- 余りを 2 倍して、割られる数の次の桁を 1 つおろす
- 割る数 B を引く
- 一番左が
0(引けた)なら、商のその桁は 1。余りはその結果 - 一番左が
1(引けなかった)なら、商のその桁は 0。B を足して元に戻す - 4 桁ぶん、1 から繰り返す
最後に残った値が、余りである。 この方式を引き戻し法(復元法)という。
なお、1 の「2 倍しておろす」は、筆算で桁を 1 つ右へ進むことにあたる。 第 6 回で 1 桁左にずらすのが 2 倍だった、あれと同じで、2 倍するのは配線をずらすだけだ。 ここもタダである。
押して、確かめる
4 桁 ÷ 4 桁の割り算を、1 周ずつ進められるようにした。 上の 2 段の青いマスが、割られる数と割る数である。
4 桁の除算器 シミュレータ
青いマスを押して 2 つの数を作り、「1 周すすめる」で筆算を 1 行ずつ進めてください
1101 ÷ 0011 = ···· あまり ····
0 / 4 周
「1 周すすめる」を押すと、ここに筆算が 1 行ずつ現れます
はじめから 1101 ÷ 0011(13 ÷ 3)を入れてある。
「引けなければ戻す(引き戻し法)」のまま、いくつか試してほしい。
1101 ÷ 0011(13 ÷ 3)。1 周目は引けない。2 周目だけ引けて、あとは引けない。 商0100、余り0001。10 進法で 4 あまり 1 だ。1111 ÷ 0100(15 ÷ 4)。前半 2 周は引けず、後半 2 周は引ける。商0011、余り0011。0111 ÷ 1001(7 ÷ 9)。4 ステップとも引けない。商0000で、余りは0111。 割る数のほうが大きいとき、割られる数がそっくり余りになる。1100 ÷ 0100(12 ÷ 4)。余りが0000になる。割り切れる、とはこのことである。
どの周でも、判断に使っているのは結果の一番左のマス 1 つだけである。 赤くなっていれば引けなかった、白ければ引けた。それ以外は何も見ていない。
先ほどの 1101 ÷ 0011 を、紙の筆算の形で書くとこうなる。
実際に引き算が起きたのは、4周のうち1周だけだった。 残りの 3 周は「引いてみて、戻した」だけである。
「戻す」のが、もったいない
その戻す作業が、少し気になる。
引けなかった周では、引いて、また足して、元の値に帰ってきている。 行って帰ってきただけだ。4 桁なら、最悪 4 回むだになる。
省けないだろうか。
引く前の値を X 、割る数をBとしよう。引いたら X − B になる。この結果がマイナスだったとする。 この状態でこの次の周にどのような計算をするか考える。
- 戻す場合:値をX-Bから X に戻す。次の周では 2 倍して次の桁をおろし、B を引く。 結果は 2X + (次の桁) − B
- 戻さない場合:X − B のまま次の周へ行き、2 倍して次の桁をおろし、今度は B を足す。 結果は 2(X − B) + (次の桁) + B = 2X + (次の桁) − B
同じ値にたどり着いた。
理由は式を見なくても分かる。 戻すというのは B を足すことだ。その B は次の歩で 2 倍されて、2B に育つ。 そこから B を引くのだから、差し引き +B が残る。
ならば最初から戻さずに、2 倍したあとで B を 1 回足せばいい。 「戻してから引く」と「戻さずに足す」は、同じことなのだ。
引き放し法
手順はこうなる。
- 余りを 2 倍して、次の桁を 1 つおろす
- 前の余りが 0 以上なら B を引く。マイナスなら B を足す
- 結果の一番左が
0なら商のその桁は 1、1なら 0 - 4 桁ぶん繰り返す
- 最後に余りがマイナスだったら、そのときだけ B を 1 回足して直す
最後の5ステップ目が少しわかりづらいかもしれないが、これは「戻す」のと同じ計算である。 先ほどの計算式の例では引く前の値をX,割る値をBとしていた。引くとX-Bとなる。これがマイナスになったのだから、X-BにBを足すことでXに「戻した」のである。
シミュレータで「戻さない(引き放し法)」を選び、もう一度 1101 ÷ 0011 を進めてみてほしい。
商も余りも、さっきとまったく同じ 0100 と 0001 になる。
違うのは、途中の余りがマイナスの値を通っていくことだけだ。
この方式を引き放し法(非復元法)という。
1 周でやる計算は、引くか足すかのどちらか 1 回になった。 引き戻し法では、引けなかった周で引く・戻すの 2 回が必要だった。
最後の「マイナスなら 1 回足す」は、4 ステップのあいだに 1 回あるかないかである。
1101 ÷ 0011 では後始末が出るが、1111 ÷ 0100 では出ない。
シミュレータで両方を試すと、出たり出なかったりするのが見える。
比べてみる
2 つの方式を並べる。4 桁 ÷ 4 桁の場合だ。
| 引き戻し法 | 引き放し法 | |
|---|---|---|
| 何周かかるか | 4 周 | 4 周 |
| 1 周でする計算 | 引く(+戻す) | 引くか足すか、1 回 |
| 1 周あたり最大 | 2 回 | 1 回 |
| 途中の余り | いつも 0 以上 | マイナスにもなる |
| 後始末 | 要らない | 最後に 1 回あることがある |
何周かかるかは変わらない。違うのは、1 周の中身である。
もっとも、引き戻し法でも「引く前の値を取っておいて、ダメだったらそちらを選び直す」 という作り方はできる。その場合は足し直さずに済むが、代わりに どちらを選ぶかを決める部品が要る。 どこかで手間を減らせば、どこかに部品が増える。第 6 回で見た取引と同じだ。
なぜ、割り算だけ遅いのか
第 6 回で、掛け算には速い作り方があった。 4 本の部分積を加算器で一気に足してしまう、アレイ乗算器である。
あれができたのは、部分積が最初から全部わかっていたからだ。 A と B を入れた瞬間に、4 本とも決まる。だから並べて同時に足せた。 部品を増やせば、時間が買えた。
割り算は、そうはいかない。
2 周目に引くのか足すのかは、1 周目の符号の桁を見るまで決まらない。 3 周目は 2 周目を待ち、4 周目は 3 周目を待つ。
次の一周が、前の一周の答えを待っている。
だから部品をいくら増やしても、素直には速くならない。 これが、割り算だけが遅い理由である。
いまのコンピュータでも、割り算は足し算より何倍も時間がかかる。 プログラムを速くしたいときに、割り算を避ける工夫が語られるのはこのためだ。
ただし、2 で割るときだけは別である。 第 6 回で 1 桁左にずらすのが 2 倍だったことを学んだ。逆に考えると1 桁右にずらすのは0.5倍(半分)になる。 4 で割る、8 で割るのも同じだ。 2 のべき乗で割るときは、配線をずらすだけで済む。 1周もせずに計算できる。
0 で割ると、どうなるか
シミュレータで、割る数 B を 0000 にしてみてほしい。
ボタンが押せなくなっているはずだ。止めてある。
なぜ止めたのか。
0 を引いても、値は 1 も減らない。 何歩進んでも余りは減らないまま、商の桁はすべて「引けた」になる。 桁を増やしても終わらないし、増やす意味もない。答えがそもそも存在しないからだ。
困るのは、回路はそのことに気づけないという点である。 回路は引いて、符号の桁を見て、商を立てる。それしかしない。 0 で割られていることも、答えが無いことも、回路にとってはどうでもいい話だ。
だから、割る前に B が 0 かどうかを別に調べておく。 0 なら計算を始めさせず、外へ「これは無理だ」と知らせる。 CPU はこれを例外と呼んで報告する。 プログラムで 0 除算のエラーが出て止まるのは、この仕組みが働いているからだ。
第 5 回のあふれと、少し似ていて、少し違う。 あふれは、答えは出たうえで読み方が壊れる話だった。だから XOR 1 つで見つけられた。 0 除算は、答えがどこにも無い。回路の側で見つけられることではないので、 外から止めるしかないのだ。
四則演算が、そろった
第 1 回から数えて、増えた部品を並べてみる。
- 足す … 加算器(第 1・2 回)
- 引く … 加算器 + 反転のスイッチ(第 3 回)
- 掛ける … AND + 配線 + 加算器(第 6 回)
- 割る … 加算器 + 配線 + 符号の桁を見る目(今回)
四則演算がすべて、加算器と配線とわずかなゲートに還元された。
回路は、数がいくつなのかを知らない。大小を比べることもできない。 できるのは、1 桁ずつ足すことだけだ。
それでも、桁の順番と、次に何をするかの決め方をこちらが与えてやれば、 掛けることも、割ることもできてしまう。 第 1 回で、電卓の中に答えの表が入っているわけではない、と書いたのは、このことだった。
ここまでは、すべて整数の話である。
0011 は 3 であって、3.5 でも 0.75 でもなかった。
小数点は、どこへ行ったのだろう。
参考文献
- 松下俊介 著. 基礎からわかる論理回路. 第2版, 森北出版, 2021.7. 978-4-627-82842-1. https://ndlsearch.ndl.go.jp/books/R100000002-I031573740
- 馬場敬信 著. 算数で読み解くコンピュータのしくみ, 技術評論社, 2022.8. 978-4-297-12960-6. https://ndlsearch.ndl.go.jp/books/R100000002-I032268791