矢沢 久雄
今回は,2進数で負の値を表す「補数表現」と,同じ値のまま2進数のビット数を増やす「符合拡張」を説明します。紙の上ならマイナス記号を使って「-1011」のような負の2進数を表すことができますが,コンピュータにはできません。デジタルICの構造上,0と1の数字を表せても,マイナス記号を表す手段がないからです。0と1だけで,どうやって負の値を表すかが前半のテーマです。後半では,8ビットから16ビット,16ビットから32ビット,というように,2進数のビット数を増やす方法がテーマです。単に上位けたを0で満たせばよいと思われるかもしれませんが,それだけではダメなのです。
足し算で引き算ができる
2進数の0と1だけを使って負の値を表すにはどうしたらよいか? いきなりですが,答えを言ってしまいましょう。負の値を正の値で表すのです。「そんなバカげたことができるのか?」と思われるかもしれませんが,できるのです。2進数で説明する前に,皆さんが使い慣れた10進数で具体例を示しましょう。
![]() |
図1●補数を使えば負の値を正の値で表せる |
狐につままれたような話かもしれません。けた上がりを捨てるというルールが,実用的でないと思われるかもしれません。しかし,コンピュータにとって,補数は,実に都合のよいものなのです。
![]() |
図2●けた上がりを捨てるのは,コンピュータにとって好都合 |
図2で,00000101+11111110を計算する方法は,分かりますね。10進数の足し算の場合と同様に,下位けたから足していって,その結果を下に書けばよいのです。1と1を足す場合には,次のけたにけた上がりすることに注意してください。
「負の値を正の値で表すことで,マイナス符号は不要となったが,プラス符号はどうするのだろう?」と思われるかもしれません。プラス符号は使わないのです。10進数でも,+123と123は同じ意味でしょう。
補数の求め方
10進数の補数の求め方を説明しましょう。10進数の-2を補数で表すと8になったことから予測できるとおり,足してけた上がりする値が補数です。2+8=10になりますね。10-2=8だから,-2の補数は8だと考えてもOKです。
![]() |
図3●補数は,もとの数をもらって桁上がりする数 |
2進数でも考え方は,同じです。2進数の補数表現のことを「2の補数」と呼びます。簡単な例をあげてみましょう。8ビットで-1を表してみます。-1の補数は,1をもらって9ビット目にけた上がりして(9ビット目にけた上がりすれば,自動的に消えてなくなります)100000000になる8ビットの正の値でから,11111111となります。
それでは,-100を補数で表すとどうなるでしょう。10進数の100は,2進数で01100100です(ここでは8ビットで考えているので,7ビットで1100100と表せる数であっても上位けたに0を入れます)。01100100をもらって9ビット目にけた上がりして100000000になる8ビットの正の値は,いくつでしょう? ちょっと頭が混乱しちゃいますね。
![]() |
図4●「反転して1」で補数を求められる |
補数のことを考えると,1つの値が正の値を表しているのか,それもと補数で負の値を表しているのかが,区別できなくなっちゃいますね。例えば,11111111が正の値だとすれば255です。補数だとすれば-1です。そこで,約束があります。2進数の最上位けたが0なら正の値を表し,1なら負の値を表しているとみなすのです(この約束に従うかどうかは,プログラムを作るときに,変数のデータ型で指定できます)。最上位けたが正負の符号を表していることになるので,最上位けたのことを「符号ビット」と呼びます。この約束に従えば,11111111は,-1を表していることになります。
それでは,問題です。補数で表された2進数の10000001を10進数で表すと,いくつでしょうか? 符号ビットが1なので,負の値であることはわかりますね。それでは,その絶対値は...答えは簡単です。負の値の負の値は正の値になりますね。マイナスをマイナスすれば,符号が反転してプラスに戻ります。例えば,-2の負の値は2です。10000001の絶対値を知りたかったら,10000001の補数を求めて負の値を正の値にしてみればよいのです。やり方は,やはり「反転して1」です。
10000001を「反転して1」すると,01111111になります。01111111は,10進数で127です。したがって,10000001の符号はマイナスで,絶対値は127であることがわかります。10000001は,10進数で-127です。「補数の補数を求めれば,絶対値が分かる」と覚えてください。
シフト演算
「演算」とは,データに何らかの操作を行うことです。コンピュータでは,算数の四則演算だけでなく,論理演算やシフト演算なども使われます。ここでは,シフト演算をマスターしておきましょう。論理演算に関しては,この講座の第4回で説明します。
シフト(shift)とは,「ずらす」という意味です。シフト演算では,データを2進数で考えて,けたを左右にずらします。上位けたにずらすことを「左シフト」と呼び,下位けたにずらすことを「右シフト」と呼びます。何けたすなわち何ビットずらすかを「シフト数」と呼びます。例えば,00001100というデータを3ビット左シフトすれば01100000となり,2ビット右シフトすれば00000011となります。簡単ですね!
2進数の左シフトは,1ビットシフトするごとに,もとの値が2倍になります。右シフトは,1ビットシフトするごとに,もとの値が2分の1になります。これは,10進数を1けた左シフトすれば10倍になり,右シフトすれば10分の1になるのと同じ理屈です。このことから,シフト演算を掛け算や割り算の代わりに使うこともあります。
左シフトする場合は下位けたに空きビットが生じ,右シフトする場合には上位けたに空きビットが生じます。ここまでの説明では,空いたビットに何気なく0を入れてきましたが,それでは問題が生じる場合があります。最上位ビットが1となった負の値の右シフトの場合です。例えば,11000000という負の値を1ビット右シフトして,上位けたに0を入れてみましょう。01100000となり,符号ビットが0なので正の値となりますね。1ビット右シフトすることは,もとの値を2分の1にすることです。11000000(10進数で-64)を2分の1にした結果が01100000(10進数で96)というのは,なんともおかしな話です。
![]() |
図5●右シフトの場合は,上位ビットに符号ビットを入れる |
シフト演算には,2種類の形式があるのです。左シフトでも右シフト空いたけたには0を入れる「論理シフト」と,左シフトで空いた下位けたには0を入れ,右シフトでは空いた上位けたに符号ビットの値(0または1)を入れる「算術シフト」です。左シフトでは,論理シフトと算術シフトの結果に違いはありませんが,言葉として使い分けられます。
![]() |
図6●論理シフトでは,データを単なるビットの羅列と考える |
符号拡張
なぜ,算術右シフトの場合は,上位けたに符号ビットを入れることで,正しい結果が得られるのでしょう。正の値の場合なら符号ビットは0なので,上位けたに0を入れていくことで正しい結果が得られることは,納得できますね。ところが,負の値の場合に,上位けたに1を入れていくことは,何とも不思議です。符号ビットを1に保つことは納得できても,複数ビット算術右シフトする場合に,どんどん1を入れて行って問題ないのでしょうか?
冷静に考えてみれば,不思議でも何でもないのです。補数で表した負の値の絶対値を求めるためには,負の値の負の値,すなわち補数の補数を求めました。補数を求める方法は,「反転して1」です。もしも,算術右シフトで,空いた上位けたに0を入れるとしたら,それを反転すると1になってしまいます。この1が10進数でいくつになるかは,けたによって違いますが,ゼロでないことは確かです。したがって,0を入れたのでは絶対値がおかしくなってしまいます。それに対して,空いた上位けたに1を入れれば,それを反転すると0です。この0がどのけたにあっても,値はゼロなのですから,絶対値の大きさに影響が与えられないというわけです。
算術右シフトで上位けたに符号ビットを入れればよいことが分かると,「符号拡張」も理解できます。符号拡張とは,8ビットのデータを16ビットに拡張したり,16ビットのデータを32ビットに拡張することです。8ビットCPU用のデータを32ビットCPUで処理するときなどに使われるのだと思ってください。
![]() |
図7●符号拡張では,上位桁を符号ビットで満たす |
データ表現というテーマは,堅苦しくてつまらないように思われたかもしれませんが,補数表現や符号拡張の仕組みは,けっこう面白いでしょう。こういうことを面白いと感じる人は,IT業界に適正があると思いますが,いかがでしょう。次回は,2進数で小数点数を表す方法を説明します。お楽しみに!