シリーズ「コンピュータの計算のしくみ」 第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.5
  • 0.11 は 0.5 + 0.25 = 0.75
  • 0.101 は 0.5 + 0.125 = 0.625
  • 110.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 桁の固定小数点 シミュレータ

下のボタンで小数点を動かしてみてください。青いマスの模様は変わりません

重み1286432168421
模様.

小数点を、右から何桁目に置くか

0 桁は、これまでどおりの整数の読み方

01101000=104

64 + 32 + 8 = 104

表せる範囲0 〜 255
隣の席との間隔1

はじめから 01101000 を入れてある。小数点は右端、つまり全部が整数の読み方だ。104 である。

小数点の位置を 0 → 1 → 2 → 3 → 4 と動かしてみてほしい。

小数点の位置読み方10 進法
0 桁01101000104
1 桁0110100.052
2 桁011010.0026
3 桁01101.00013
4 桁0110.10006.5

値が毎回ちょうど半分になっている。当然で、名札を全部 2 で割ったのだから。

第 7 回の最後に、1 桁右にずらすのは半分になることを見た。 あれと同じことが起きている。ただし今回ずれているのは、模様ではなく小数点のほうだ。 模様を 1 桁右へずらすことと、小数点を 1 桁左へずらすことは、同じ意味を持つ。

そして——青いマスの模様は、一度も変わっていない。

回路が持っているのは、この 8 個の 0 と 1 だけである。 それが 104 なのか 6.5 なのかは、回路の外側で決まっている。

小数点を 4 桁目に置いたまま、いくつか作ってみてほしい。

  • 000010000000.1000。0.5 だ。0.5 の位に 1 が 1 つ立っている。
  • 000011000000.1100。0.5 + 0.25 で 0.75。
  • 000000010000.0001。0.0625。この読み方で表せる、一番小さい数である。
  • 111111111111.1111。15.9375。一番大きい数だ。

席は、256 個のまま

第 4 回で、4 桁の模様は 16 個しかなくて、輪になっていると書いた。 8 桁なら 256 個である。

小数点を動かしても、席の数は増えない。256 個のままだ。 変わるのは、隣の席との間隔である。

小数点の位置一番重い名札表せる範囲隣との間隔
0 桁(整数)1280 〜 2551
1 桁640 〜 127.50.5
2 桁320 〜 63.750.25
3 桁160 〜 31.8750.125
4 桁80 〜 15.93750.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 だ。

0010.1000
+0001.0100
0011.1100

答えは 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 を計算させると、こうなる。

1000
×1000
01000000

8 × 8 で、64 である。

この 64 は、間違いなのだろうか。

小数の掛け算の筆算を、思い出す

小学校で 1.5 × 2.5 をどう計算したか、思い出してほしい。 まず、小数点を忘れて 15 × 25 を計算した。

15
×25
75
30
375

375 である。それから、掛けた 2 つの小数部の桁数を数えた。 1.5 が 1 桁、2.5 が 1 桁。合わせて 2 桁。 そして答えの右から 2 桁目の手前に点を打つ。3.75。

小数の掛け算だけは、なぜか「小数点をそろえる」と言われなかったはずだ。 足し算では小数点をそろえて書かせたのに、掛け算では右そろえのまま、 最後に位を数えて点を打つ。あの手順である。

やっていたことを並べると、こうなる。

  1. 小数点を忘れて、ただの整数として掛ける
  2. 2 つの小数部の桁数を足す
  3. その桁数ぶん、答えの点を左へ動かす 回路にやらせていることも、これと同じである。 桁が 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.10000.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 の補数は、足し算と引き算にはこの上なく都合がよかった。 けれど掛け算と割り算では、どうも素直に通っていない。

次回は、マイナスの数と掛け算・割り算の関係を見る。 符号付きの掛け算で上の桁が狂う理由と、その直し方。 そして割り算の余りに符号を付けるとき、どこで意見が分かれるのか。

それを見てから小数点を動かせば、「なぜ符号だけ別枠なのか」は、 教わる話ではなく、自分で気づく話になる。

参考文献