khurata’s blog

khurata’s blog

「2の補数」の引き算を、きっちり理解する!

(もともと「Yahoo!知恵袋」の「知恵ノート」だったものを転載しています)
(最終更新日時:2016/5/27)投稿日:2011/10/17

 

はじめに

 現代のコンピュータでは、負の整数を表現する・整数の引き算をするために、「2の補数(complement)」が、よく使われます。
 「知恵袋」では、時折り、「補数」についての質問を見かける事がありますが、質問文を見ますと、質問者の方々は、どうにも「掴めていない」という悩みをお持ちのようです。

 しかし、私に言わせれば、それは質問者のせいではなく、教え方や、テキストの書き方が悪いのです。 補数による引き算は、決して難しくなく、分かってしまえばとても簡単なものです。

 高級な数学理論はさておいて、簡単な言葉だけで「2の補数」による引き算を説明し、きっちり理解していただくのが、このノートの目的です。 いったん挫折してしまった方、いまひとつ自信が持てない方でも、きっと、大丈夫です。
(ただし、すでに2進数での数値の書き方や、足し算、10進数との相互変換などを学んだ方が対象です) 

前提
 補数を考える時、大前提となるのは、「有効桁数を決める」ことです。 補数という言葉が出てきた時点で、必ず「有効桁数が決まっているんだな」と思わなければなりません。 これは常に心してください。 

 

2進数の「2の補数」

 まず、2進数の「2の補数」について、ざっとおさらいしてみましょう。
(「それは、どういうものかは分かってる」と言いたい方もいらっしゃるでしょうが、後の説明で納得をするために、目を通しておいてください)

 2進数の「2の補数」は、先頭ビット(MSB)が1なら負数、0なら正数であり、正負を反転させるには、「1の補数」を求めて1を足します。

 2進数の「1の補数」は、各ビットが反転した値です。

 ここでは仮に、有効桁数8桁の2進数の「2の補数」について考えてみましょう。 たとえば10011001は MSB が1ですから、「2の補数の世界」では負数を表現しています。 これが何の負数なのかを知るには、まず正数にして……つまり符号を反転させれば良いわけです。

 

10011001
↓各ビット反転(1の補数を求める)
01100110
↓1を足す(2の補数にする)
01100111

 

 この手順を逆にすると、元の10011001が得られます(正→負の符号反転)。

 

01100111
↓各ビット反転(1の補数を求める)
10011000
↓1を足す(2の補数にする)
10011001

 

 つまり、「2の補数の世界」においては、正数01100111を符号反転して負数にしたものが10011001になります。 正数01100111は10進数では103ですから、10011001は10進数の-103を表す事になります。
(なぜ、こんなものが「負数」として使えるのか? 今のところは、理解出来ていなくても構いません。2の補数、1の補数というものが、どうやって得られるか、それだけ憶えてしまってください)

 

10進数の「10の補数」

 実は、補数という考え方は、私たちが使い慣れている10進数でも、そのまま使えます。 上記の例では有効桁数8桁の2進数でしたが、たとえば有効桁数3桁の10進数の補数について考えてみましょう。

 103という10進数があるとします。これの各桁を反転させると、どうなるでしょう。

 

0123456789

9876543210

 

上記の「反転」に従うと、103は896になります。 これをとりあえず「9の補数」と呼びましょう(反転の「上下」を足すと9になるから)。

 これに1を足すと897になります。 これをとりあえず「10の補数」と呼びましょう(「9」に1を足したから……安易ですが)。

 先ほどの2進数でやってみたような操作、「反転させて1を足す」と、どうなるのか、実際に確かめてみましょう。

 

103
↓各桁反転(9の補数を求める)
896
↓1を足す(10の補数にする)
897

 

897
↓各桁反転(9の補数を求める)
102
↓1を足す(10の補数にする)
103

 

 先ほどの2進数と同様、「反転させて1を足す」と、ちゃんと「戻り」ました。

 さてさて……「えっ?」と思われるかも知れませんが、有効桁数3桁の10進数の「10の補数」において、897は「-103」を表します!

 ……「何を言っているのか分からない!」、ですか? それでは実際に試してみましょう。 適当な数、たとえば496について、496-103を計算してみます。 当然ですが、496-103=393です。

 次に、496+897を計算してみましょう。496+897=1393です。

 今、扱っていたのは、有効桁数が3の「10の補数」でした。1393は、有効桁数が3ならば、393です。

 なんということでしょう、「10の補数」を使って、ちゃんと496-103=393が計算出来ました! 897は、-103だったのです!(「有効桁数3の『10の補数』の世界」においては)

 何だかよく分からないかも知れませんが、でも、これは、ごく当たり前の計算に過ぎません。 次の計算をご覧下さい。


496-103=393
↓両辺に1000を足してみる
496-103+1000=393+1000
↓それって実は・・・「10の補数」!
496+897=393+1000
↓有効桁数3桁だけ見れば・・・
496+897=393+1000

 

 要するに、「3桁を反転させて1を足す」、という操作は、 1000(4桁)から引いた数を求める事と同じなのです。 3桁を反転させたもの同士を(つまり「元の数」と「9の補数」を)足せば999になるのですから、元の数と「10の補数」を足せば当然、1000になります。

 これが、「負符号を使わず、10の補数で引き算が出来る」理由です。 2進数だろうが10進数だろうが、その理屈は同じです。

 なお、2進数の「2の補数」は、MSB が0か1かで正負を見分けられますが、10進数の「10の補数」は、最上位桁が0~4なら正で、5~9なら負となります。

 

まとめ

 いかがでしょう、10進数で考えてみれば、全く難しい事ではなかったと思います。

 10進数が「10の補数」で引き算が出来るように、2進数は「2の補数」で引き算が出来る、ただそれだけの事だったのです。

 先ほど書いた事を繰り返しますが、「3桁を反転させて1を足す、という操作は、 1000(4桁)から引いた数を求める事」と等しく、これが、10の補数で引き算が出来る理由です。 引いた数を足せば、それは引き算と同じだからです。 もちろん、これは、2進数であっても、全く同じ事です。

 2進数だと、「反転して1を足す」という操作のうち、「反転」が、ちょっと鮮やかすぎて、行為の意味が分かりづらく、それで、「なんだか今ひとつ分からない」のではないでしょうか……私自身、なぜ反転するのかを、きちんと説明しているテキストを、見たことがありません。

 また、10進数で考えてみれば全く難しくはないのですが、10進数で説明しているテキストも、私はほとんど見た事がありません。

 「反転して1を足す意味は何なのか」、「10進数ではどうなのか」、この2点さえちゃんと説明すれば、「補数で引き算」は、スッキリ理解出来るはずだと私は思います。

 

付記

 

【付記.1…呼び方について】
 2進数の場合、「1の補数」とか「2の補数」だとか言っていますが、これらは、「10-1の補数」「10の補数」と言う方が、より良い呼び方だと私は考えています。
 そもそも2進数には「2が無い」ので、「2の補数」という呼び方は、あまり良いものとは思えません。

 2進数の10-1は1です。 1の補数とは、各桁の和が1になる数であり、2の補数とは、1の補数に1を加えた数です。
 10進数の10-1は9です。 9の補数とは、各桁の和が9になる数であり、10の補数とは、9の補数に1を加えた数です。

 つまり、何進数であろうが、「10-1の補数」「10の補数」と言えば、必ず通用します。
 ……が、この呼び方は、私の知る限り、ほとんど使われていません。 今後、「1の補数」「2の補数」という言葉を聞いたら、「ああ、これは『10-1の補数』『10の補数』という意味なんだな」と考えていただければ、より分かりやすいと思います。 

【付記.2…なぜ「2の補数」を使うのか】

 ご存じの通り、コンピュータの中身は、電子回路です。 電子回路で、2進数1桁の足し算を作るのは、難しい事ではありません。 この回路を連結すれば、多ビットの足し算回路になります(ビットを反転させる回路は、さらに簡単です)。
 しかし、引き算回路をまじめに作ろうとすると、桁借りがあったり、負符号をどうするのかという問題が出てきます。

 「2の補数」の考え方を使えば、足し算回路と反転回路だけで、引き算回路が簡単に実現出来ます。 これは便利で、しかも高速なので、現在のコンピュータは、2進数の「2の補数」をよく使うようになったのです。 

【付記.3…他の方法】

 負数を表現する方法は、他にもいくつかあります。 「符号に1ビットを割り当てて、他のビットは絶対値だけを扱う」のも1つの方法ですし、「有効桁数の中で、全ての整数を『ずらして』しまい、負数を扱わない」(下駄を履かせる、バイアスをかける、などという)方法もあります。
 また、小数を含む実数を扱うには浮動小数点がよく使われており、多くの浮動小数点は符号を数値と独立して保持しています。
 しかし、2進数回路を基本とするコンピュータにおいては、整数の加減算には「2の補数」を使うのが簡単で高速です。

 

【追記(2012/10/16)】

 monhanpapaia さんから、「変換方法だけでなく『なぜ2の補数を使うと足し算で減算が出来てしまうのか』、その理論が欲しい」とアドバイスをいただきました。
 本ノートの「まとめ」の直前に、「3桁を反転させて1を足す、という操作は、 1000(4桁)から引いた数を求める事と同じ」だと私は書いております。 この「3桁」とか「4桁」とか「1000」というのは、2進数だろうが3進数だろうが10進数だろうが同じ事なのです。
 補数を使う時に不可欠なのは、本ノートの「前提」ですでに書いた通り、有効桁数を決めておく事です。 上記で書いた事は、「有効桁がnならば、n桁の数値を反転させて1を足すのは、n桁の最大値+1から引いた数を求めるのと同じ」、と言い換えられます(たとえば10進数におけるn桁の最大値+1は、nが3なら、999+1すなわち1000です)。
 「引いた数」を足す、という計算は、減算と同義です。 従って、2の補数(私は10の補数と呼ぶことを提案しております)の加算は、減算と同義になるのです。

 

(転載以上)