シフト演算(論理シフトと算術シフト)


目次に戻る


先週の説明の続きです。とりあえず、先週の例をもう一度書きます。
シフト演算を使うときに注意する事があります。それは、シフトする変数が signed か unsigned かで右シフトの結果が変わる場合があるということです。これは、 signed の変数をシフトするときは算術シフトが、unsigned の変数をシフトするときは論理シフトが 用いられる為起こります。
int main(int argc, char* argv[])
{
    unsigned int a = 0xf0ffffff; /* 処理系によって桁が異なる。 sizeof(int)=2byteの時は 0xf0ff で行う*/
    int b = (int)(b);

    printf("a = %08x , b = %08x\n",a,b);
    a >>= 4;
    b >>= 4;
    printf("a = %08x , b = %08x\n",a,b);
    return 0;
}

論理シフトはかなり直感的でわかりやすいものになります。次に論理シフトを行う前と行 った後の、メモリの中に保存されているデータのビットイメージを書きます。
論理シフト 0x78 << 1
シフト前(16進:0x78 10進:120)
  01111000
 
シフト後(16進:0xF0 10進:240)
  11110000
論理シフト 0xF0 >> 1
シフト前(16進:0xF0 10進:240)
  11110000
 
シフト後(16進:0x78 10進:120)
  01111000
ビット列が右に1移動したのがわかりますね。全体を右にシフトし、空いた左に0を無条件 で挿入するのが論理シフトです。
 
次に算術シフトを見てみましょう。
算術シフト 0x78 << 1
シフト前(16進:0x0F 10進:120)
  01111000
 
シフト後(16進:0x60 10進:96) 注:オーバーフローが起きている
  01110000
算術シフト 0xF0 >> 1
シフト前(16進:0xF0 10進:−16)
  11110000
 
シフト後(16進:0xF8 10進:−8)
  11111000
ビットの変化には法則があります。シフトしても、最左端のビットは変化しないのです。
 
なぜ、このような面倒くさい事をするのかというと、現在のマシンの殆どがマイナスの 数字を表すのに、2の補数という物を使っている事が関係します。2の補数表記(厳密に は違います)では、最左端のビットが1の時はマイナスの数字を表し、最左端のビットが0 の時はプラスの数字を表します。シフトした結果、符号がひょろひょろと変わってしまうの では困るので、算術シフトという考えがうまれたのです。
 
かなりアセンブラ(機械語)よりの話でしたが、シフト演算には論理シフトと算術シフト がある。という事を頭の隅にとどめておくと、後で良いことがあるかもしれません。


目次に戻る