【C言語(2)】負数の表現
前回の記事では、2進数が必要な理由として「スイッチ*1のON(1)とOFF(0)を考えやすくするため」であることを解説しました。
0と1しか使えない制約のなかで、どのようにしてマイナスの数(負数)を表現するのか、今回も基礎的な内容ですがしっかり習得していきましょう。
今回も最後に問題を用意していますので、理解度の確認に役立ててください。
はじめに
マイナス記号(-)は使えません
負の数を表現するなら『-』記号を使えば良いのでは?と考えたくなりませんか。
たとえば-3は-0011(2)みたいな。
私は初めてプログラムの勉強をした時に思いました。だって、プログラムで引き算の計算を書くときには『-』記号を使うのですから。
だけど負の数を表現するのに『-』記号は使えないのです。
ここで前回の記事を思い出してください。
マイコンのプログラムはメモリに書き込まれていて、メモリにはON(1)とOFF(0)しかできないスイッチが数十~数百万個あるのでした。
このスイッチの組み合わせによって数値を表現します。
さて、この記事では簡単のためにスイッチ4個*2で話をしていきたいと思います。つまり、4個のスイッチだけで負数を表現しなければなりません。
初心者の頃の私・・・『スイッチでマイナス記号…どやって表現するねん!』と絶望しましたとさ。
スイッチ4個でいくつ数えられる?(復習)
前回の記事の復習になりますが、スイッチ4個でいくつ数えられるでしょうか?
0000(2)~1111(2)までですね。10進数で数えれば、0~15までです。
負数を表現しなくても良い場合には0~15まで数えることが出来ますが、負数を表現するようになるとどう変わるのか、そのあたりも注目してもらえればと思います。
それでは、負数の表現法を習得していきましょう!
負数の表現法
さっそくスイッチ4個で負数を表現する方法を紹介したいと思います。
私の知る限りでは3種類の表現法があります*3ので、それぞれ紹介したいと思います。
ただし、現在のマイコンで実際に使われているのは最後に紹介する『2の補数』と呼ばれる表現法です。古いマイコンではその他の表現法も使われていたようです。
① 符号仮数部
素人がどうやって負数を表現するかを考えたときに、最初に思いつきそうな表現法です。
その表現法とは、符号(+/-)の役割を1番左のスイッチに与えるというものです。
つまり、1番左の数字を『+の時には0』、『-の時には1』として符号を表現します。このような表現法を『符号仮数部(ふごうかすうぶ)表現』と呼びます。
たとえば、+3は0011(2)、-3は0011(2)の一番左を1(-)に置換えて1011(2)とします。
下の表を見てもらえると良く分かりますが、符号仮数部表現の特徴として、表せる数値の範囲は-7~+7です。
また、0の表現が+0:0000(2)と-0:1000(2)の2種類存在します。
なんというか…無駄ですね。
10進数 | 符号仮数部 |
---|---|
+7 | 0111(2) |
+6 | 0110(2) |
+5 | 0101(2) |
+4 | 0100(2) |
+3 | 0011(2) |
+2 | 0010(2) |
+1 | 0001(2) |
+0 | 0000(2) |
-0 | 1000(2) |
-1 | 1001(2) |
-2 | 1010(2) |
-3 | 1011(2) |
-4 | 1100(2) |
-5 | 1101(2) |
-6 | 1110(2) |
-7 | 1111(2) |
② 1の補数
『1の補数(ほすう)表現』は、負の数を表す場合に全てのスイッチを反転させる表現法です。
『反転させる』とは、1を0に、0を1に変換する操作のことです。
たとえば、+3は0011(2)、-3は0011(2)を反転させて1100(2)とします。
先と同様に下の表を見てもらえると良く分かりますが、1の補数表現の特徴として、表せる数値の範囲は-7~+7です。
また、0の表現が+0:0000(2)と-0:1111(2)の2種類存在します。符号仮数部と同じような特徴を持っています。
一番左のスイッチが1であれば必ず負の数なので、符号の判定は容易です。
10進数 | 1の補数 |
---|---|
+7 | 0111(2) |
+6 | 0110(2) |
+5 | 0101(2) |
+4 | 0100(2) |
+3 | 0011(2) |
+2 | 0010(2) |
+1 | 0001(2) |
+0 | 0000(2) |
-0 | 1111(2) |
-1 | 1110(2) |
-2 | 1101(2) |
-3 | 1100(2) |
-4 | 1011(2) |
-5 | 1010(2) |
-6 | 1001(2) |
-7 | 1000(2) |
③ 2の補数
『2の補数(ほすう)表現』は、まず1の補数と同様に全てのスイッチを反転させ、その後1を足す表現法です。
反転させるだけであれば『1の補数』、反転させた後に1を足すと『2の補数』です。
たとえば、+3は0011(2)、-3は0011(2)を反転させた1100(2)に1を足して1101(2)とします。
なんだか先に紹介した2つと比較すると理解しにくい表現法に感じますが、2の補数の良いところは後で解説したいと思います。
ここで、2の補数の面白い特徴を紹介します。
先に紹介した2つの表現法では0の表現は2種類存在することが分かりました。では、2の補数を使って0000(2)の負数を計算するとどうなるか、試してみましょう。
0000(2)を反転すると1111(2)となります。
それに1を足すと、10000(2)となりますが、今は4つのスイッチしかありませんので、はみ出た1*4は無視して下位4桁のみを答えとします。
したがって、0000(2)の2の補数は0000(2)となり変化しません。
つまり、2の補数では0の表現は1種類のみとなります。
下の表を見てもらえると良く分かりますが、2の補数表現の特徴として、表せる数値の範囲は-8~+7です。
また、0の表現は0000(2)の1種類しか存在しません。
先に紹介した2つの表現法と同様に、一番左のスイッチが1であれば必ず負の数なので、符号の判定も容易です。
もう少し後の記事でも解説したいと考えていますが、+7である0111(2)に1を足すと1000(2)となり、いきなり-8まで振り切るので注意したいところです。これも2の補数表現の特徴として押さえておきましょう。
10進数 | 2の補数 |
---|---|
+7 | 0111(2) |
+6 | 0110(2) |
+5 | 0101(2) |
+4 | 0100(2) |
+3 | 0011(2) |
+2 | 0010(2) |
+1 | 0001(2) |
+0 | 0000(2) |
-1 | 1111(2) |
-2 | 1110(2) |
-3 | 1101(2) |
-4 | 1100(2) |
-5 | 1011(2) |
-6 | 1010(2) |
-7 | 1001(2) |
-8 | 1000(2) |
引き算は足し算? ~2の補数の良いトコロ~
実はマイコンが引き算を行うときには、足し算*5を行っています。
たとえば『3 - 2』の計算式は『3 + (-2)』と書けますよね。3という値と-2という値を足し算して引き算を実現しています。
それでは、これまでに紹介した負数の表現法を使用して、この計算を行ってみます。
答えは『1』となって欲しいですがどうでしょうか。
ここからは2進数の筆算を行いますが、計算の仕方は簡単です。
0と0を足せば0、0と1もしくは1と0を足せば1、1と1を足せば1繰り上がって0となります。
では、はりきって計算していきましょう!
符号仮数部
『3 + (-2) = 0011(2) + 1010(2)』
1101(2)は符号仮数部表現では『-5』を表します。
残念ながら、符号仮数部では『1』にはならないようです。
1の補数
『3 + (-2) = 0011(2) + 1101(2)』
桁の溢れが発生していますが、今は4桁で考えていますので、溢れた1は無視して下4桁を答えとします。
0000(2)は『+0』ですね。
残念ながら、1の補数においても『1』にはならない*6ようです。
2の補数
『3 + (-2) = 0011(2) + 1110(2)』
今回も1の補数と同様に桁の溢れが発生していますが、下4桁を答えとします。
0001(2)は『1』ですね。
2の補数で計算した結果のみが正しい答えとなりました!
他の値で計算してみても正しく答えが算出されます。
2の補数を使えば負の値を足し算することで、引き算を実現することが可能です。これが2の補数の良いトコロです。
理解度チェック
この記事の内容をおさらいしておきましょう。
【答えを表示】をクリックすると回答例を表示します。
(1)0101(2)を2の補数で負数に変換してください。
1011(2)
((1011)2、0b1011でもOK!)
0101(2)を反転させて1010(2)、それに1を足して1011(2)です。
(2)2の補数で表された1001(2)を正数(プラスの数)に変換してください。
0111(2)
((0111)2、0b0111でもOK!)
正数 → 負数に変換する手順と全く同じ手順で負数 → 正数に変換できます。
1001(2)を反転させて0110(2)、それに1を足して0111(2)です。
(3)0x55を2の補数で負数に変換し、16進数で答えてください。
0xAB
((AB)16、AB(16)、AB'h、ABh、ABHでもOK!)
0x55は01010101(2)ですね。反転すると10101010(2)、それに1を足して10101011(2)となります。これを16進数に変換して0xABが答えです。
次回はデータの単位について解説します!
/*** このサイトでは脚注を豊富に記載します ***/
*1:スイッチ:bit(びっと)と呼ぶのが正確ですが、簡単のためにスイッチとして紹介しています。
*2:スイッチ4個:4bitと呼ぶのが正確です。bitの話は、次の記事で紹介したいと考えています。
*3:3種類の表現法があります:本当はもう1つ、『ゲタ履き表現』というのもありますが、ここではややこしいので紹介しません。いつか浮動小数点と呼ばれる数値表現を解説することがあれば、『ゲタ履き表現』も紹介したい(紹介せざるを得ない)です。
*4:はみ出た1:計算を行って桁が溢れた1のことをキャリーと呼んだりします。
*5:足し算:マイコン内のCPUには、『加算器』と呼ばれる足し算を行う機構を備えてます。加算器だけで減算ができることを説明しています。
*6:1の補数においても『1』にはならない:実は1の補数においては、溢れた1(キャリー)を最後に足してあげることで正しい答えが出ます。