【シフト演算②】算術シフト
今回はシフト演算の算術シフトについて解説していきます。
シフト演算については↓のページを参考にしてみてください。
簡単にシフト演算について説明しますと、2進数を表すビット列を左もしくは右にずらしていく操作のことです。そのシフト演算は2種類あり、論理シフトと算術シフトに分かれます。
今回はその中で算術シフトについて解説します。また、シフト演算の理解を深めるための例題(過去の基本情報技術者試験の問題を独自で編集したもの)も取り上げているので、参考にしてみてください。
算術シフトとは
符号を考慮せずにシフト操作を行うのが論理シフトです。一方、符号を考慮してシフト操作を行うのが算術シフトです。
先頭のビットを符号ビットとして固定します。それ以降のビットを左右にずらします。
先頭の符号ビットが0であれば正の数であると判断します。1であれば負の数であると判断します。
上の図のように8ビットの場合は、符号ビット以外の7ビットに対してのみシフト演算を行います。ここが論理シフトとの違いです。
左シフト
論理シフトの場合は、左にずらすと2倍ずつ大きくなります。たとえば2ビット左にずらすと2の2乗で4倍の数になります。
では、算術シフトの場合はどのようになるか、考えてみましょう。
今回は、11100011(10進数に換算すると-29)を例にして考えることにします。2ビット左にずらしてみましょう。-29の4倍の-116になるのでしょうか?それとも別の数値が出てくるのでしょうか?
2ビット左にずらすと、上位2桁の1が左にはみ出ます。そして、下位2桁の部分が空くので、0で埋めます。
はみ出た2つの1は削除します。本来、符号ビットの枠に入っていたのは1でしたが、2ビット移動した後も符号ビットの枠に入っているのは1のままですね。つまり符号の反転などは発生しないわけです。
10001100が左に2ビットずらした結果なのですが、10進数で表すといくらでしょうか?理論通りであれば-116なのですが、、、-116になっていますね!
ちなみに、もう1ビット左にずらすとどうなるでしょうか。符号ビットの枠には0が入ることになります。そうすると、負の数が正の数になってしまうというおかしな現象が発生します。これがオーバーフローです。
論理シフトの場合は、左シフトの過程で1がはみ出てしまうとオーバーフローが発生するのですが、算術シフトの場合は本来の符号ビットの値と違う数値が符号ビットの枠に入ってしまうとオーバーフローが発生します。
右シフト
続いて右シフトです。左シフトでは、論理シフトと同様に1ビットずらすと2倍、2ビットずらすと4倍…という具合に2のn乗倍になっていったわけですが、右シフトも論理シフトと同じようになるのでしょうか。
ちなみに、論理シフトのときには右シフトを行うと1ビットであれば2分の1、2ビットであれば4分の1という具合に2のn乗分の1になっていました。算術シフトではどうでしょうか。
左シフトと同様に、11100011を2ビットずらす場合を考えましょう。符号ビットは固定したまま、以降の7ビットをずらしていきます。
2ビット目と3ビット目に空白が生まれますね。この空白は当然埋めなきゃいけないわけですが、0を入れればいいでしょうか。それとも1を入れればいいでしょうか。
では0を入れた場合を考えてみましょう。すると、2ビット左シフトにするとオーバーフローを起こしてしまうのは分かりますか?右に2ビットシフトした後左に2ビットシフトをしたら、当然ながら本来シフト操作する前の値に戻らないといけませんよね。もしここで0を入れていると…
2ビット左にずらして2ビット右にずらすなら元に戻るはずなので11100011になるはずなのですが、01100011になってしまい、負の数から正の数に変化しています。これはおかしいですよね。
ですので、右に2ビットずらしたことによってできる空白は、符号ビットと同じ値を入れましょう。正の数であれば0、負の数であれば1です。今回の例であれば負の数ですので1で埋めましょう。
すると…
1111100011というビット列ができます。ここで、下位2ビットは右シフトによってはみ出たものなので削除しましょう。すると、11111000がでてきます。10進数に直すと、理論上は-29を4分の1にした-7になっているはずですが、、、ー8になってしまいます。なぜでしょうか??おかしいですね。
いいえ。おかしくないんです。右シフトによってはみ出た数は割り算の余りという風に考えるわけです。これは論理シフトの時に少し説明しています。
余りは切り捨て
右シフトによって右にはみ出た2桁(今回であれば11)の部分は削除しました。これは割り算の余りを切り捨てるのと同じことです。
ある数について割り算を行ったときの答えが4余り3になったとしましょう。このとき、余りを切り捨てると4になります。
負の数の場合はどうでしょうか。-4余り3という答えが出た時に、余りを切り捨てると-4になると思いたいところですが、-5になるんです。数直線を書くと分かりやすくなります。
雑な数直線ですみません…(笑)。4余り3というのは4と5の間です。余りを切り捨てると4になりますね。このとき、4余り3と4を比較すると、当然ながら4の方が小さいわけです。数直線で表したときに左であればあるほど値は小さくなりますからね。
負の数の場合も同じです。-7を3で割ったら-2.33333…という数になりますね。もし手元に紙とペンがあれば、正の数の時と同じように-2.3333…についても数直線を図示して書いてみるとよいでしょう。「-2.3333」は-3と-2の間であることは言うまでもありません。割り算の余りを切り捨てることは小数部を切り捨てるということと同じですよね。切り捨てを行うと数直線上で左にずれます。負の数の場合も正の数と同じです。
したがって、-2.3333…において小数部を切り捨てると、-3になりますね。
ここまで理解できれば、-29を4分の1にして余りを無視したときに-7ではなく-8になることが納得できますね。
例題
16進数のABCDがある。この数を2ビット右に論理シフトした値はどれか。値は32ビットで表すものとする。
ア.2AF3 イ.6AF3 ウ.AF34 エ.EAF3
正解は分かりましたか?
解説をまだ見たくない方は、画面のスクロールをここで止めてくださいね。
ABCDを2進数表記にしてみましょう。すると、Aが1010、Bが1011、Cが1100、Dが1101になります。
1010 1011 1100 1101を右に2ビット論理シフトするとどうなるでしょうか。
論理シフトの計算方法は論理シフトの解説ページで解説しています。
2ビットシフトすると、、、
0010 1010 1111 0011になりますね。これを16進数に直すと…2AF3ですね。したがって、正答はアです。
いかがだったでしょうか。ではこの調子でもう1問行ってみましょう。
8ビットの2進数11010000を右に2ビット算術シフトしたものを、00010100から減じた値はどれか。負の数は2の補数表現によるものとする。
ア.00001000 イ.00011111 ウ.00100000 エ.11100000
正解は分かりましたか?
では解説していきます。
11010000を右に2ビット算術シフトするとどのような値になるでしょうか。
符号は1なので負の数ですね。2ビット右シフトすると…11110100ですね。
00010100から11110100を減じるわけですね。減じるということは2の補数を取って足せばいいわけです。11110100の2の補数は、00001100ですね。
00010100と00001100を加えると、00100000になります。したがって、正答はウです。
まとめ
今回は、算術シフトについて説明してから論理シフトと算術シフトの例題の紹介をしました。
算術シフトでは符号を考慮したシフト演算を行います。右にシフトすれば2のn乗分の1になり、左にシフトすれば2のn乗倍になります。
左シフトをするときには、符号ビットと異なる値が最上位ビットに来た場合にはオーバーフローを起こします。
右シフトをすることにより生まれる空白には符号ビットと同じ値を入れましょう。
ということで、今回はここで終わりです。何か参考になる情報があれば嬉しいです。
最後までお読みいただき、ありがとうございました。