ここでは「加算」,「ビットのシフト」「ビットの反転」のみにより 2進数の四則演算を行います。
CPU (※1) には 四則演算のうち加算回路しかないものもあります。
減算には補数の加算を, 乗算には左シフトを, 除算には右シフトを 用いて演算します。
演算に「シフト」, あまり見慣れない言葉ですね, 本文で分かり易く説明します。
これにより加算回路を搭載するだけで, シフト回路と反転回路により 四則演算が可能となります。
OS (※2) が搭載されていないワンボードマイコンなどでは ソフトウェアの開発者が加算演算をベースにして, 四則演算のソフトウェアを独自に作る必要があります。
※1:コンピューター内臓の CPU (頭脳に相当する)【参照先】
※2:Windows, macOS, または様々な個別のOS などをさします。
加算は10進と同様なので, 簡単に説明します。
例1:「01011100b=5ch」と「00100111b=27h」の加算
以下は2進8bitの加算演算です。
2進数(xx…b), 16進数(xx…h)は前回学びました。
☞
【復習先】
繰上げすると, 桁上がりします。
\( \begin{array}{r}
& 0& 1& 0& 1& 1& 1& 0& 0& =5ch \\
+& 0& 0& 1& 0& 0& 1& 1& 1& =27h \\
\hline
& & \bt& \bt& \bt& \bt & \bt & & &←\sc{繰上げ} \\
& 1& 0& 0& 0& 0& 0& 1& 1& =83h
\end{array} \)
検算:
・上記の83hは10進では:
\(83h=8\x 16+2+1=\ul{131}\)
・10進に変換した後の加算結果:
\( \begin{array}{r}
& 5ch &=5\x 16+12 &=92 \\
+ & 27h &=2\x16+7 &=39\\
\hline
&&& \ul{131}
\end{array} \)
例2:キャリー \(\sc{\boxed{1}}\) が生じる例
\( \begin{array}{r}
& 1& 1& 1& 0& 1& 0& 0& 1& =e9h \\
+& 0& 1& 1& 0& 1& 1& 0& 1& =6dh \\
\hline
& \bt& \bt& \bt& & \bt & & & \bt& ←\sc{繰上げ} \\
\boxed{1} & 0& 1& 0& 1& 0& 1& 1& 0& =\boxed{1}56h
\end{array} \)
\(\boxed{1}\)は桁のあふれをしめす
キャリー (または
キャリーフラグ)です。
減算操作をしないで, 補数の足し算に変えて,減算の結果を得ます。
・補数とは次の桁に繰り上がるための(補う)数。
・引く数(減数)を補数に変換します。
例:\(\s{\ul{120-37=83}}\) を補数による減算を行います。
補数による減算操作:
37の10の補数は「100-37」より
63です。
(37+
63=100)
次式「被減算120に補数63を加算する」⇒減算操作です。
\(\ul{120+\bv{63}=\boxed{1}83}\)
\(\boxed{1}\)を無視して回答83を得る
理解用に式を分解してみる
\(120+(\bv{100-37})\)\(=120-37+100=83+100\)
しかし補数を得るのに減算を使いのでは…疑問ですね!
大丈夫です… 2進の "2の補数" は反転と加算のみでs得られます。
…コンピューターでは反転と加算は高速で機能します。
2進の補数は「
各ビットの反転した後、1を加算」で得られます。
例:次の減算を求める
\(\small{A-B=0111\ 1000-\ul{0010\ 0101}}\)
\(\small{A=0111\ 1000b=78h=120}\)
\(\small{B=\ul{0010\ 0101}b=25h=37}\)
\(\small{B} \sc{の反転}\) \(\ \sc{=\ \overline{ B }}\ \ \) \(\small{=\ 1101\ 1010}\)
\(\small{B} \sc{の補数}\) \(\ \sc{=\overline{ B }}\)\(\small{+1=1101\ 1011}\)
\( \begin{array}{r}
& 0& 1& 1& 1& 1& 0& 0& 0& A \\
+& 1& 1& 0& 1& 1& 0& 1& 1& \sc{B補数} \\
\hline
\boxed{1} & 0& 1& 0& 1& 0& 0& 1& 1& \sc{=\boxed{1}53h}
\end{array} \)
答えは53h(83)です。(キャリーの\(\boxed{1}\)は無視)
検算
・上記の結果(補数による減算)
\(\quad 53h=16\x 5+3=\ul{83}\)
・通常の減算
\(\quad 120-37=\ul{83}\)
論理シフト と 算術シフト
シフト演算には次の2通りあります。
\(\begin{cases}
論理シフト & 正負の符号を考慮せず \\
算術シフト & 正負の符号を考慮する
\end{cases}\)
まずは基本である
論理シフト の理解を深めていきましょう。
先に進む前に 10進数 で考えてみよう!
(無意識にシフト演算しています)
左シフトでは\(10\)倍, 右シフトでは\(10^{-1}=0.1\)倍です。…
何故か
その答えは次を見れば分かります。
例:
125 をシフトする
⋄左シフト:1250 ← 125
⋄右シフト:125 → 12.5
下の図をゆっくり見て先に進んでください。
・左シフト \(\s{1250 \Leftarrow \ul{125←0} }\)
各桁を左にずらす… 上の桁に移動する
1の桁に0が入る,5は10桁に移動,…100は千の桁に移動。
・右シフト \(\s{\ul{0→125} \Rightarrow 012.5=12.5}\)
各桁を右にずらす… 下の桁に移動する。
100の桁に0が入る,1は10桁に移動,…5は0.1の桁に移動。
以上から次のことが言えます:
10進数の基数10の乗除算は:
・\(10^n\)倍…掛け算=n回 左シフト
・\(10^{-n}\)倍…割り算=n回 右シフト
2進数の基数2の乗除算は:
・\(2^n\)倍…掛け算=n回 左シフト
・\(2^{-n}\)倍…割り算=n回 右シフト
乗 算
(左論理シフト)
2進数の乗算はビットの左シフトを使い,
左シフト
1回で2倍 ,
2回で4倍, …
n回で\(\ul{2^n}\)倍となります。
まず,右の最下位の1桁目に0 が入り, 1桁目は2桁目に移動, 2桁目3桁目に移動,…と移動する。
これをシフトの言葉を使い表すと:
シフトは全ビットが左へ移動し, 最下位ビット(LSB)(右)に 0 が入り,最上位ビット(MSB)(左)から押し出されたものは捨てられる。(※1,※2)
4ビットを例にすると:
シフトは 1, 2, 3回で \(\ul{\s{1\x 2=2}}\)倍 → \(\ul{\s{2\x 2=4}}\)倍 → \(\ul{\s{4\x 2=8}}\)倍 の乗算となります。(元の数に対して2倍,4倍,8倍)
※1:※2:この例では:
最下位ビット(\(2^0の桁\))をLSB(Least significant bit) という。
最上位ビット(\(2^3の桁\))をMSB (Most significant bit) という。
[4bitの左シフト]
\( \begin{array}{ccccc|cccc|ccC}
& && & & \sc{2^3}& \sc{2^2}& \sc{2^1}& \sc{2^0}& & &\\
\sc{左シフト}& && & ← & 0& 0& 0& 1& ←& &\\
\hline
\sc{1回=\x 2}& & & & 0\sc{←}& 0& 0& 1& 0& \sc{←}0& &\\
\sc{2回=\x 4}& & & 0& 0\sc{←}& 0& 1& 0& 0& \sc{←}0& 0&\\
\sc{3回=\x 8}& & 0& 0& 0\sc{←}& 1& 0& 0& 0& \sc{←}0& 0& 0\\
\sc{4回=\x 0}& 0& 0& 0& \sc{\bx{1}←}& 0& 0& 0& 0& \sc{←}0& 0& 0&0
\end{array} \)
4回シフトではMSBの"1" が押し出されています, これをキャリーフラグといいます。
|
キャリーフラグを捨てるか否かプログラムによる。
例:2進8bitの"0010 0111"を4倍,8倍する
題記の 2進 → 16進 →10進 変換すると:
"0010 0111"=27h \(=2\x 16+7\)=39
答えは⇒4倍は
156 , 8倍は
312 です。
[8bit数値の左シフト]
\( \begin{array}{c|ccc|cccccccc|ccc}
& & & & \sc{2^7}&\sc{2^6}&\sc{2^5}&\sc{2^4}&\sc{2^3}&\sc{2^2}&\sc{2^1}&\sc{2^0}& & & &\\
& & & & 0& 0& 1& 0& 0& 1& 1& 1& & & &\\
\hline
\x 4=& & 0& 0\sc{←}& 1& 0& 0& 1& 1& 1& 0& 0& \sc{←}0& 0& &\\
\x 8=& 0& 0& \sc{\bx{1}←}& 0& 0& 1& 1& 1& 0& 0& 0& \sc{←}0& 0& 0&\\
\end{array} \)
|
4倍(\(2^2\))は左シフトを2回:
・1001 1100=9ch=\(\sc{9\x 16+12}\)=156
8倍(\(2^3\))は左シフトを3回:
・0011 1000=38ch=\(\sc{3\x 16+8}\)=56(不正解)
オーバーフロー発生が発生,結果はエラーである。
しかし, オーバーフローを考慮し, 9ビットデータにとすると:
・1 0011 1000=138ch=\(\sc{256\x 1 +3\x 16+8}\)=312
除 算
(右論理シフト)
除算はビットの右シフトを使い,
右シフト
1回で\(\frac{1}{2}\)倍 ,
2回で\(\frac{1}{4}\)倍, …
n回で\(\frac{1}{2^n}\)倍 となります。
右シフトする度に2の乗数が"-1, -2, -3, … -n" 変化します。
\(2^{-1}=\frac{1}{2^1}=\frac{1}{2}=0.5\)
\(2^{-2}=\frac{1}{2^2}=\frac{1}{4}=0.25\)
\(2^{-3}=\frac{1}{2^3}=\frac{1}{8}=0.125\)
\(\qquad \vdots\)
\(2^{-n}=\frac{1}{2^n}\)
シフトは全ビットが右へ移動し,
最上位ビット(MSB)(左)に 0 が入り,
最下位ビット(LSB)(右)から押し出されたものは
捨てられる。
4ビットを例すると:
シフトは 1, 2, 3回で \(\ul{\frac{1}{2}}\) → \(\ul{\frac{1}{4}}\) → \(\ul{\frac{1}{8}}\) の除算となります。
元の数に対して\(\frac{1}{2}\)倍,\(\frac{1}{4}\)倍,\(\frac{1}{8}\)倍になります。
\( \begin{array}{ccccc|cccc|ccc}
& && & & \sc{2^3}& \sc{2^2}& \sc{2^1}& \sc{2^0}& & &\\
\sc{左シフト}& && & →& 1& 0& 0& 0& →& &\\
\hline
\sc{1回=2}& & & & 0\sc{→}& 0& 1& 0& 0& \sc{→}0& &\\
\sc{2回=4}& & & 0& 0\sc{→}& 0& 0& 1& 0& \sc{→}0& 0&\\
\sc{3回=8}& & 0& 0& 0\sc{→}& 0& 0& 0& 1& \sc{→}0& 0& 0\\
\sc{4回=0}& 0& 0& 0& 0\sc{→}& 0& 0& 0& 0& \sc{→\bx{1}}& 0& 0& 0&
\end{array} \)
4回シフトではMSBの"1" が押し出されています, これをキャリーフラグといいます。
|
"0100 0101"=45h \(=4\x 16+5\)=69
\(69÷4=17…余り1\)
小数を扱っていないから余りがでる。
"0100 0101" を右シフトを2回(÷4)すると以下になります。
\( \begin{array}{c|cc|cccccccc|cc}
& & & \sc{2^7}&\sc{2^6}&\sc{2^5}&\sc{2^4}&\sc{2^3}&\sc{2^2}&\sc{2^1}&\sc{2^0}& & & \\
& & & 0& 1& 0& 0& 0& 1& 0& 1& 0& 0& \\
\hline
\s{÷4=} & 0& 0\sc{→}& 0& 0& 0& 1& 0& 0& 0& 1& \sc{→}0& ①& \\
\end{array} \)
|
0001 0001=11h=\(\sc{1\x 16+1}\)=17…余り1
(押し出された ① は余りです)
小数のシフト演算
(右/左 論理シフト)
ここで 小数を含む2進数のシフトをはどうなるか:
次の例で学ぼう!
例.次は
小数 の2進数を左右にシフトします。
"0 1100.110"=0c.5h=12.75
整数5ビット+小数は3ビットです。
\( \begin{array}{c|cc|ccccccccc|cc}
& & & \sc{2^4}&\sc{2^3}&\sc{2^2}&\sc{2^1}& \sc{2^0}& .& \sc{2^{-1}}& \sc{2^{-2}}& \sc{2^{-3}}& & & \\
& & & 0& 1& 1& 0& 0& .& 1& 1& 0& 0& & \\
\hline
\s{÷2=} & & 0\sc{→}& 0& 0& 1& 1& 0& .& 0& 1& 1& \sc{→}0& & \\
\hline
\s{\x 2=} & & 0\sc{←}& 1& 1& 0& 0& 1& .& 1& 0& 0& 0\sc{←}& & \\
\end{array} \)
|
\(\ul{\s{÷2=}}\):右シフト
10進計算では\(12.75÷2=\ul{6.375}\)
シフト演算では上記より \(4+2+0.25 +0.125=\ul{6.375}\)
\(\ul{\s{\x 2=}}\):左シフト
10進計算では\(12.75\x 2=\ul{25.5}\)
シフト演算では上記より \(16+8+1+0.5=\ul{25.5}\)
注:桁あふれのとき論理シフト(符号なし演算)ではキャリーフラグが, 次に学ぶ算術シフト(符号付き演算)ではオーバーフローフラグが機能します。(当然このフラグの使い方を決めるのはソフトです)
算術シフト
2進数では「符号なし」と「符号つき」は予め決めておく内容です。
算術シフトは「符号つき」2進数の操作です。
2進数8bitデータとすると「符号つき2進数」は先頭ビットを正負の符号ビット, 負数は「2の補数」を使います。
従い表せる数は符号なしの「256」 に対し 符号付きは「-128~0~+127」です。
以下は算術シフトをまとめた内容です。
•符号ビット(0:正, 1:負)はシフトに対し固定(不動)です。
•符号ビットは最上位におき, シフトには寄与しません。
•負数(符号ビット=1)のとき数値データは 2の補数表記です。
ここでは
負数のシフト演算について説明します。
正数の算術シフトは符号ビット(0)を固定する以外は論理シフトと同じなので省略します。
•符号無2進数と符号付2進数の表現範囲
\( \begin{array}{|c|c|c|}
\hline
2進数 & \s{符号付2進数} & \s{符号付2進数}\\
& \s{(10進表示)} & \s{(10進表示)}\\
\hline
0000\ 0000 & 0 & 0 \\
0000\ 0001 & 1 & 1 \\
0000\ 0010 & 2 & 2 \\
\vdots & \vdots & \vdots\\
0111\ 1111 & 127 & 127\\
\hline
1000\ 0000 & 128 & -128\\
1000\ 0001 & 129 & -127\\
\vdots & \vdots & \vdots\\
1111\ 1110 & 254 & -2 \\
1111\ 1111 & 255 & -1 \\
\hline
\end{array} \)
ここから先は負数のシフト演算です。
乗 算
(左算術シフト)
最上位に符号ビット "1", 左シフトでは最下位に"0" が入ります。
また上位から追い出される数が符号ビットと異る数値 "0" のときはオーバーフローです。
例:10進の負数 "-39" を2倍, 4倍してみよう。
まず, 39d="0010 0111"を
負数(2の補数) に変換します。
・先頭ビット\(\Rightarrow\) " \(\boxed{1}\) ":符号ビット(負)は固定です。
符号ビットを除き "2の補数" に変換する。
・ビット反転\(\Rightarrow\) "\(\ \boxed{1}\ \ul{101\ 1000}\)"
・上記 + 1 \(\Rightarrow\) "\(\ \boxed{1}\ 101\ 100\ul{1}\)"
\(\therefore\) -39d="\(\ul{ \boxed{1} 101 1001 }\)"
10進で予め計算してみると:
-39\(\x\)2=-156㋐ -39\(\x\)4=-156㋑
(-312はオーバーフローしますね)
左算術シフトは以下の通り:
\( \begin{array}{c|ccc|cccccccc|ccc}
& & & & \sc{2^7}&\sc{2^6}&\sc{2^5}&\sc{2^4}&\sc{2^3}&\sc{2^2}&\sc{2^1}&\sc{2^0}& & & &\\
& & & & \bx{1}& 1& 0& 1& 1& 0& 0& 1& & & &\\
\hline
\x 2= & & & 1\sc{←}& \bx{1}& 0& 1& 1& 0& 0& 1& 0& \sc{←}0& & &\\
\x 4= & & \ul{\cl{0}}& 1\sc{←}& \bx{1}& 1& 1& 0& 0& 1& 0& 0& \sc{←}0& 0& &\\
\end{array} \)
|
注: [\( \x 4=\)] の \(\ul{\cl{0}}\) (※) はオーバフローを意味します。
(※)符号ビットと異なる数のときオーバフローとなる。
検算:
以下 "負の符号ビット" は省略し,7ビットで評価。
1. \(\ul{÷2:}\) 結果の "\(\ 101\ 1110\)" を2の補数に変換"\(\ 0 1 0 \ 0 0 1 0\)"
=22h=32+2=34(負)\(\Rightarrow\)正解
2. \(\ul{\x 4:}\) 結果の "\(\ 1 1 0\ 0 1 0 0\)" を2の補数に変換"\(\ 0 0 1\ 1 1 0 0\)"
\(=1c\ h\)=16+12=28 \(\rightarrow\)オーバーフロー発生
オーバーフローのビットは\(2^7=128\)の桁を足せば\(\rightarrow\) 156(負) (正解になる)
3.オーバーフローの扱いはソフトウェアによります。
除 算
(右算術シフト)
最上位に符号ビット "1", 右シフトで空くスペースには"1"
(符号ビットと同じ)が入る。
(正の数の場合は"0"が入る)
下位から追い出される数は無視するが、数値が"1" のときは余りを示します。
例1: "1011 1100"(-68)を2で割る。
上記は"0100 0100"(68)の2補数表記です。
\( \begin{array}{c|cc|cccccccc|cc}
& & & \sc{2^7}&\sc{2^6}&\sc{2^5}&\sc{2^4}&\sc{2^3}&\sc{2^2}&\sc{2^1}&\sc{2^0}& & & \\
& & & \bx{1}& 0& 1& 1& 1& 1& 0& 0& & & \\
\hline
\s{÷2=} & & 1\sc{→}& \bx{1}& 1& 0& 1& 1& 1& 1& 0& \sc{→}0& & \\
\end{array} \)
|
検算:
以下符号ビットは省略し,7ビットで評価。
\(\ul{÷2:}\) 結果の "\(101 1110\)" を2の補数に変換した "\(010 0010\)"
=22h=32+2=34\(\Rightarrow\)正解
例2: "1011 1011"(-69)を2で割る
(余りが出る例)
上記は"0100 0101"(69)の2補数表記です。
\( \begin{array}{c|cc|cccccccc|cc}
& & & \sc{2^7}&\sc{2^6}&\sc{2^5}&\sc{2^4}&\sc{2^3}&\sc{2^2}&\sc{2^1}&\sc{2^0}& & & \\
& & & \bx{1}& 0& 1& 1& 1& 1& 0& 1& & & \\
\hline
\s{÷2=} & & 1\sc{→}& \bx{1}& 1& 0& 1& 1& 1& 1& 0& \sc{→} ① & & \\
\end{array} \)
|
検算:
結果は前の例1 と同じ正解。
異なるのは, 右にはみ出た「①」 がある, すなわち「余り…1」を示している。
…以上