シリーズ「コンピュータの計算のしくみ」 第8回
回路が「小数を扱う」とは?
小数点は、どこへ行ったのか
前回で、四則演算がそろった。 加算器と配線とわずかなゲートだけで、足し算・引き算・掛け算・割り算ができてしまった。
けれど、ここまで出てきた数はすべて整数である。
0011 は 3 であって、3.5 でも 0.75 でもなかった。
4 桁のスイッチを、もう一度思い出してほしい。 左から 8 の位、4 の位、2 の位、1 の位。一番右が 1 の位だ。
その右には、何も無い。
小数点を打つ場所が、回路には用意されていないのである。 0 と 1 を入れるスイッチは 4 つあるが、「点」を入れるスイッチはどこにも付いていない。
では、コンピュータはどうやって 3.5 を扱っているのだろう。
小数の桁も、重みを持っている
先に、小数点の右側が何を意味していたかを確かめておく。
十進法で 3.14 と書いたとき、3 は 1 の位、1 は 0.1 の位、4 は 0.01 の位だった。 小数点の右へ 1 つ進むごとに、桁の重みは 10 分の 1 になる。
2 進法なら、右へ 1 つ進むごとに 2 分の 1 になるだけである。
| 小数点からの位置 | 重み |
|---|---|
| 4 つ左 | 8 |
| 3 つ左 | 4 |
| 2 つ左 | 2 |
| 1 つ左 | 1 |
| 1 つ右 | 0.5 |
| 2 つ右 | 0.25 |
| 3 つ右 | 0.125 |
| 4 つ右 | 0.0625 |
2 進法の小数は、半分、その半分、さらにその半分の足し算である。
0.1は 0.50.11は 0.5 + 0.25 = 0.750.101は 0.5 + 0.125 = 0.625110.1は 4 + 2 + 0.5 = 6.5
規則は、整数のときと 1 つも変わっていない。 1 が立っている桁の重みを、全部足す。 それだけである。
名札を、右へずらす
ここで第 4 回を思い出してほしい。
あのとき私たちは、回路を 1 本も変えずにマイナスの数を手に入れた。 やったことは、一番左の桁の名札を「8 の位」から「-8 の位」に書き替えただけだった。
小数も、同じ手でいける。
8 桁の模様を考える。名札はふつう、左から 128、64、32、16、8、4、2、1 だ。 この 8 枚の名札を、全部 16 で割ってみる。
8、4、2、1、0.5、0.25、0.125、0.0625。
模様は 1 マスも動いていない。スイッチも配線も、さっきと同じものである。 書き替えたのは名札だけだ。
それだけで、右の 4 桁が小数になった。 小数点は、名札の並びのあいだに現れる。
「小数点を右から 4 桁目の手前に置く」と言うのは、 「8 枚の名札を全部 16 で割る」と言うのとまったく同じことなのである。
そして名札は、回路の中には無い。
押して、確かめる
この回だけ、桁を 8 桁に増やした。 第 6 回で 4 桁 × 4 桁の答えが 8 桁になった、あの幅である。
8 桁の固定小数点 シミュレータ
下のボタンで小数点を動かしてみてください。青いマスの模様は変わりません
小数点を、右から何桁目に置くか
0 桁は、これまでどおりの整数の読み方
01101000=104
64 + 32 + 8 = 104
はじめから 01101000 を入れてある。小数点は右端、つまり全部が整数の読み方だ。104 である。
小数点の位置を 0 → 1 → 2 → 3 → 4 と動かしてみてほしい。
| 小数点の位置 | 読み方 | 10 進法 |
|---|---|---|
| 0 桁 | 01101000 | 104 |
| 1 桁 | 0110100.0 | 52 |
| 2 桁 | 011010.00 | 26 |
| 3 桁 | 01101.000 | 13 |
| 4 桁 | 0110.1000 | 6.5 |
値が毎回ちょうど半分になっている。当然で、名札を全部 2 で割ったのだから。
第 7 回の最後に、1 桁右にずらすのは半分になることを見た。 あれと同じことが起きている。ただし今回ずれているのは、模様ではなく小数点のほうだ。 模様を 1 桁右へずらすことと、小数点を 1 桁左へずらすことは、同じ意味を持つ。
そして——青いマスの模様は、一度も変わっていない。
回路が持っているのは、この 8 個の 0 と 1 だけである。 それが 104 なのか 6.5 なのかは、回路の外側で決まっている。
小数点を 4 桁目に置いたまま、いくつか作ってみてほしい。
00001000→0000.1000。0.5 だ。0.5 の位に 1 が 1 つ立っている。00001100→0000.1100。0.5 + 0.25 で 0.75。00000001→0000.0001。0.0625。この読み方で表せる、一番小さい数である。11111111→1111.1111。15.9375。一番大きい数だ。
席は、256 個のまま
第 4 回で、4 桁の模様は 16 個しかなくて、輪になっていると書いた。 8 桁なら 256 個である。
小数点を動かしても、席の数は増えない。256 個のままだ。 変わるのは、隣の席との間隔である。
| 小数点の位置 | 一番重い名札 | 表せる範囲 | 隣との間隔 |
|---|---|---|---|
| 0 桁(整数) | 128 | 0 〜 255 | 1 |
| 1 桁 | 64 | 0 〜 127.5 | 0.5 |
| 2 桁 | 32 | 0 〜 63.75 | 0.25 |
| 3 桁 | 16 | 0 〜 31.875 | 0.125 |
| 4 桁 | 8 | 0 〜 15.9375 | 0.0625 |
右へ行くほど、細かい数が書けるようになる。代わりに、大きい数が書けなくなる。
第 6 回で、部品の数と時間を交換する話をした。 今度は範囲と細かさを交換している。 8 桁という予算は変わらないのだから、どちらかを増やせば、どちらかが減る。
どこに点を置くかは、作る人が決める。そして決めたら、動かさない。 だからこの表し方を、固定小数点という。
マイナスの小数は、どうなるか
名札の貼り替えは、これで 2 種類になった。
- 第 4 回 … 一番左の名札に、マイナスを付けた(8 の位 → -8 の位)
- 今回 … 全部の名札を、16 で割った
この 2 つは、重ねてかけてもいい。
1111.1000 の一番左を -8 の位と読めば、-8 + 4 + 2 + 1 + 0.5 = -0.5 である。
つまり、小数を扱うのに符号の話は要らない。 足し算と引き算なら、2 の補数はマイナスの小数でもそのまま通る。
ただし掛け算と割り算になると、そうはいかない。 それは次回にまとめるので、シミュレータでは 0 から上の読み方だけを用意した。 この記事の最後で、もう一度ここへ戻ってくる。
足し算は、何も変えなくていい
では、この読み方のまま計算させてみよう。2.5 + 1.25 である。
小数点を 4 桁目に置いた 8 桁で書くと、2.5 は 0010.1000、1.25 は 0001.0100 だ。
答えは 0011.1100。2 + 1 + 0.5 + 0.25 = 3.75。合っている。
ここで大事なのは、回路の側で何も変えていないことである。 第 2 回で作った加算器を 8 桁ぶん並べて、2 つの模様を入れただけだ。
繰り上がりも、そのまま隣へ上がっていく。 0.5 の位で繰り上がった 1 は、1 の位へ上がる。 桁の重みが全部 16 分の 1 になっていても、隣どうしの比が 2 倍であることは変わらないからだ。 繰り上がりが見ているのは、その比だけである。
引き算も同じで、第 3 回の加減算器にスイッチを足す必要はない。
小数点の位置がそろっていれば、足し算と引き算は整数とまったく同じ回路で済む。 これが固定小数点の、一番のうまみだ。
掛け算では、小数点がずれる
掛け算は、そう素直にいかない。
0.5 × 0.5 をやってみよう。答えは 0.25 のはずである。
小数点を 4 桁目に置いた読み方では、0.5 は 0000.1000 だ。
回路に渡すとき、小数点は消える。
回路が受け取るのは 00001000、ただの 8 という整数である。
上の 4 桁はどちらも 0 なので、第 6 回で作った 4 桁 × 4 桁の乗算器がそのまま使える。
1000 × 1000 を計算させると、こうなる。
8 × 8 で、64 である。
この 64 は、間違いなのだろうか。
小数の掛け算の筆算を、思い出す
小学校で 1.5 × 2.5 をどう計算したか、思い出してほしい。 まず、小数点を忘れて 15 × 25 を計算した。
375 である。それから、掛けた 2 つの小数部の桁数を数えた。 1.5 が 1 桁、2.5 が 1 桁。合わせて 2 桁。 そして答えの右から 2 桁目の手前に点を打つ。3.75。
小数の掛け算だけは、なぜか「小数点をそろえる」と言われなかったはずだ。 足し算では小数点をそろえて書かせたのに、掛け算では右そろえのまま、 最後に位を数えて点を打つ。あの手順である。
やっていたことを並べると、こうなる。
- 小数点を忘れて、ただの整数として掛ける
- 2 つの小数部の桁数を足す
- その桁数ぶん、答えの点を左へ動かす 回路にやらせていることも、これと同じである。 桁が 10 倍ずつではなく 2 倍ずつになるだけで、手順は 1 つも変わらない。
違うのは、3 の後始末だけだ。 紙の上では、点を好きな場所に打てる。だから答えは 3.75 のまま持ち歩ける。 けれど回路には、点を打つ手が無い。点の位置は「小数部 4 桁」と先に決めてしまっている。
点が動かせないなら、数のほうを動かす。 それが、このあとの「ずらす」である。
2 進法でも、数え方は同じ
入れた 2 つは、どちらも小数部が 4 桁だった。 掛け算の筆算では、部分積をずらして足すだけである。だから 小数部の桁数は、足し算になる。 答えの小数部は 4 + 4 = 8 桁だ。
つまり 01000000 は、右から 8 桁目の手前に点を打って読むのが正しい。
.01000000 である。0.25 の位に 1 が立っている。0.25 だ。合っている。
回路は正しい答えを出していた。ずれていたのは、点のほうである。
困るのは、私たちが欲しかったのが「小数部 4 桁の 8 桁の模様」だという点だ。
紙なら、右から8桁目に点を売って、それで終わりでよかった。 けれど回路の点は4桁目に固定されている。小数部 8 桁のままでは、次の計算に渡せない。
ずれは8-4で4桁ぶん。そして戻し方は、拍子抜けするほど簡単である。4 桁ぶん、右にずらす。
01000000 を 4 桁右へずらすと 00000100。
小数部 4 桁で読めば 0000.0100、やはり 0.25 だ。こうすれば「小数部 4 桁の 8 桁の模様」を得られる。
そして第 6 回で見たとおり、ずらすのはタダだった。 配線を 4 つ隣につなぐだけで、ゲートは 1 つも増えない。
- 掛ける前 … 小数部 4 桁 と 小数部 4 桁
- 掛けたあと … 小数部 8 桁
- 4 桁ずらす … 小数部 4 桁に戻る
掛け算に必要なのは、第 6 回の乗算器と、答えを 4 桁ずらす配線だけである。 新しい部品は、今回も出てこない。
ずらすときに押し出される下の 4 桁は、どこへ行くのか。 その話は、このシリーズの最終回でする。
割り算では、逆に足りなくなる
割り算は、掛け算の裏返しである。
1 ÷ 2 を、小数部 4 桁の読み方でやってみる。答えは 0.5 になってほしい。
1 は 0001.0000、2 は 0010.0000。回路が受け取るのは 16 と 32 だ。
第 7 回の除算器に渡すと、16 ÷ 32 は商 0、余り 16 である。
商が 0 になってしまった。
理由は、桁数の数え方を裏返せば分かる。 掛け算では小数部の桁数が足された。割り算では引かれる。
4 − 4 = 0。商の小数部は 0 桁、つまり整数しか出てこない読み方になっている。 整数の世界で 1 ÷ 2 は 0 なのだから、回路はここでも何も間違えていない。
欲しいのは小数部 4 桁の商だ。 ならば、割られる数の小数部を、先に 8 桁にしておけばいい。 8 − 4 = 4 で、ちょうど合う。
小数部を 4 桁増やすとは、4 桁左へずらすことである。
16 を 4 桁左へずらすと 256。そして 256 ÷ 32 = 8。
小数部 4 桁で読めば 0000.1000、0.5 だ。
ここでも、増えた部品は配線だけである。
そしてこれは、小さくない話だ。
第 7 回まで、割り算の答えは「商と余り」だった。 13 ÷ 3 は 4 あまり 1 で、そこで終わっていた。 けれど割られる数を先に左へずらしておけば、余りの先を数えられる。 割り算が、小数を返せるようになったのである。
紙の筆算で、割り切れないときに 0 を書き足して先を計算したことを思い出してほしい。 左へずらすのは、あの「0 を書き足す」にあたる。 何桁ずらすかが、小数第何位まで求めるかを決めている。
それで、小数点はどこにあるのか
改めて問う。この回路のどこに、小数点はあるのだろう。
- スイッチには無い。0 か 1 しか入らない
- 配線にも無い。信号が通るだけだ
- 加算器にも無い。1 桁ずつ足しているだけである
小数点は、回路のどこにも無い。
あるのは、それを作った人の頭の中と、プログラムの中の約束だけだ。 「この 8 桁は小数部 4 桁として読む」と決めた人がいて、その決めごとを覚えているのは 回路ではなく、コードのほうである。
実際の開発では、この約束を Q 表記という書き方で残す。 整数部 4 桁・小数部 4 桁なら Q4.4、あるいは単に Q4 と書く。 コメントに書いたその一行が、小数点の在り処だ。
身近な例もある。 お金を扱うプログラムで、金額を「円」ではなく「銭」や「厘」を単位とした 整数で持つことがある。アメリカなら「円」は「ドル」、「銭」や「厘」は「セント」と読み替えられる。小数点を 2 桁ぶん右に置いた固定小数点と、やっていることは同じである。
音声や画像を扱う小さなコンピュータでも、固定小数点は今でもよく使われている。 足し算と引き算が、整数の回路そのままで済むからだ。
書けない数がある
最後に、1 つだけ気になることを見ておこう。
小数部を 4 桁にした読み方で、0.1 を作ってみてほしい。
作れない。
0.5、0.25、0.125、0.0625 をどう組み合わせても、0.1 にはならない。 0.0625 の席と 0.125 の席のあいだに、席が無いのである。
似たことは、十進法でも起きていた。 1 ÷ 3 を小数で書こうとすると 0.3333… と終わらない。10 を 3 で割り切れないからだ。
2 進法の小数の重みは、2 で割った数ばかりである。
だから 2 で割り切れるものしか、ぴったりは書けない。
0.1 は 10 分の 1 であって、2 のべき乗では割り切れない。
2 進法で書くと 0.0001100110011001100… と、無限に続く。
桁を増やせば近づく。けれど、届かない。 そして届かないとき、コンピュータはいちばん近い席に置く。
このことが、あとでとても大きな話になる。最終回で、それを見る。
点を、動かしたい
固定小数点は、点を固定した。決めたら動かさない。 だから加算器をそのまま使えたし、引き算もタダで付いてきた。
けれど固定したことの代償が、さっきの表に出ていた。
小数部を 4 桁にすると、書ける数は 0 〜 15.9375 である。 0.0625 刻みで細かく書ける代わりに、16 には届かない。 逆に整数で読めば 255 まで届くが、0.5 は書けない。
目盛りを細かくすれば短い物差しになり、長い物差しにすれば目盛りが粗くなる。 点が動かないかぎり、この取引から逃げられない。
現実には、大きい数と小さい数を同じ物差しで扱いたいことがある。 そういうとき、私たちはどうすればいいのか。
ならば、点そのものを動かせばいいのではないか。 「点が右から何桁目にあるか」を、01の世界の中に一緒に書き込んでしまえばいい。
それが、このあとの話である。
その前に、決めなければいけないことがある
自分のことを計算機を作る設計者だと考えてほしい。 8 桁のうち何桁を点の位置に使い、何桁を数そのものに使うか。自分で決めることになる。
そして、符号をどこに置くかも決めなければならない。
ここで手が止まる。 第 4 回で手に入れた 2 の補数は、あまりにうまくできていた。 名札を 1 枚書き替えるだけで、マイナスの数が現れた。 足し算と引き算は、プラスもマイナスも同じ 1 つの回路で済んでしまった。
それなら、点を動かす表し方でも 2 の補数を使えばいいはずである。
ところが、いま世界中のコンピュータで実際に使われている形は、そうなっていない。 符号のためだけに、1 桁を別枠で取っている。 2 の補数ではないのだ。
なぜ、あんなに便利だった 2 の補数を使わなかったのだろう。
心当たりは、もうある。
- 第 6 回。符号付きの掛け算で、上の 4 桁が狂った。 下 4 桁だけは合っていた
- 第 7 回。符号付きの割り算の余りをどう扱うか、触れずに済ませた 2 の補数は、足し算と引き算にはこの上なく都合がよかった。 けれど掛け算と割り算では、どうも素直に通っていない。
次回は、マイナスの数と掛け算・割り算の関係を見る。 符号付きの掛け算で上の桁が狂う理由と、その直し方。 そして割り算の余りに符号を付けるとき、どこで意見が分かれるのか。
それを見てから小数点を動かせば、「なぜ符号だけ別枠なのか」は、 教わる話ではなく、自分で気づく話になる。
参考文献
- 松下俊介 著. 基礎からわかる論理回路. 第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