« 標準C#.NETプログラミング1 C#言語構文編 | トップページ | 無謀な戦い »

2005年12月 7日 (水)

シフト演算子

C言語においてビットシフト演算子(<<や>>)は2通りに解釈される。

  • 算術シフト --- 符号を意識したシフト
  • 論理シフト --- ビット列としてのシフト

どちらが適用されるかは、型が符号を持つかに依存する。

signedである場合は算術シフト、unsignedの場合は論理シフトである。しかし、符号付整数の右シフトだけは結果が処理系に依存(※1)してしまう。浮動小数型の場合、シフト演算自体が無効である。

signedである場合は算術シフト、unsignedの場合は論理シフトになり、符号ビットを持つfloatやdoubleは算術シフトになる結局は何の違和感もないように出来ているだけだ。

---

※1 Cでは、整数の内部表現を2の補数に決めていないため、下手に規定しないほうが高速なように実装を選べる余地が残せるという事なのだろうか?もっと詳しく知りたいため参考情報募集してます。

---

[MSDN] - C++ Language Reference Shift Operators: >> and <<

---

職業としてのプログラミング - ビットシフトの落とし穴

にて詳しい解説を作られたようです。こちらも是非参考に。

---

(2006/01/05) コメントによる指摘により訂正。progerさん有難うございました。

(2006/01/09) 不適切な表現を修正。参考となるリンクを追加しました。

01:35 午前 C++ |

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/147973/8019372

この記事へのトラックバック一覧です: シフト演算子:

» ビットシフトの落とし穴 - 算術シフトと論理シフト トラックバック 職業としてのプログラミング
「不定だ・・・。犀川創平君。君の方程式の解は、今や不定だ」 森博嗣 - 『笑わない数学者―MATHEMATICAL GOODBYE』 C言語には、ビットシフト演算子というものがあります。左シフト演算子( 続きを読む

受信: 2006/01/09 2:33:22

コメント

こんにちわ。
C言語の場合、右シフト(>>)が算術シフト(符号拡張)か論理シフト(0埋め)になるかは処理系依存です。大抵はおっしゃるように型にあわせて処理が変わるようになっているようですが、移植性はないと思いますよ。

投稿: proger | 2006/01/04 12:13:52

ご指摘ありがとうございます。
当方でも確認した結果、確かに算術シフトになるのは「Microsoft Specific」と書かれておりました。
実はsar命令に変換されてるのを見て確認したのですが、それじゃ確認になっていないですね・・。
またひとつ賢くなりました。
ありがとうございました。

投稿: konuma | 2006/01/05 0:05:45

その問題を避けるため Java
では新たに論理右シフト演算子 >>> が導入された。
それでもまだ最上位ビットが 0
にならない面白いケースがあるのだが。

あと、両オペランドは汎整数型でなければならないので、シフト演算子で
float や double は使えないはず。

投稿: 水無瀬 | 2006/01/05 0:22:43

>その問題を避けるため Java では新たに論理右シフト演算子 >>> が導入された。
そうそう。「>>>」の話は知ってたハズなのになぁ。不覚。

しかもこの問題、貴方のページで「ちゃんと」指摘してましたよね・・・。今度の件でアセンブラ見る癖を辞めるべきだと思いました。

>あと、両オペランドは汎整数型でなければならないので、シフト演算子で float や double は使えないはず。
さっき気づいて慌てて修正しました。
計算系プログラム経験が殆ど無いというのがバレバレですな・・・。またいろいろお願いします。

投稿: konuma | 2006/01/05 1:09:15

トラックバック先確認しました。
うちよりかなり丁寧に解説されてます。
>ビットシフトの落とし穴 - 算術シフトと論理シフト

気になる表現は「C言語では最左ビットが符号ビット」と「除算とビットシフトの等価性」。

C言語はともかく、C++では2の補数表現。最左が符号ビットという表現は適切がどうかということ。

次に除算について「-3/2」を例にだして「切り上げ切り捨て」で解説しているが、「-3/2」だとC99以前のC言語では切り上げ切り捨ての問題で片付かない。Cの規約として整数は2の補数表現を必須とするとは見たことがない。(C++では必ず2の補数のはず)

この辺をコメントしてきました。

ちなみに、整数除算とビットシフトが等価ではないというのは既知。でも最近のコンパイラは除算を「シフト+特殊な命令」で等価にしてきますので算術計算でビットシフトを使う理由はもうないと思います。

投稿: konuma | 2006/01/09 14:39:19

TBへのコメントありがとうございました。

>C言語はともかく、C++では2の補数表現。最左が符号ビットという表現は適切がどうかということ。
確かに意味的に適切ではありませんね。まぎらわしいので修正したいと思います。

> 次に除算について「-3/2」を例にだして「切り上げ切り捨て」で解説しているが、「-3/2」だとC99以前のC言語では切り上げ切り捨ての問題で片付かない。Cの規約として整数は2の補数表現を必須とするとは見たことがない。(C++では必ず2の補数のはず)
2の補数が必須でないという話と、C99以前での負数の整数除算の話は知りませんでした。勉強不足ですね。これも修正しておきます。

御指摘ありがとうございました。


投稿: proger | 2006/01/09 20:47:00

>2の補数が必須でない
補数と決めつけて処理するなとは聞いた事があるんですが、実は私自身も良く分かっていないだけなのです。
やっぱりCの規約を一通り揃えて確認すべきなんでしょう。

>C99以前での負数の整数除算の話
読み返したらコメントが変なのですが、余りの部分をどちらに丸めるかが既定されていないということを書いたつもりです。

私自身、もうすこし勉強してみます。

投稿: konuma | 2006/01/09 21:14:55

自己フォローです。

C言語の規約を一通り確認したところ、JIS X3010の6.2.6にてバイト数、バイトの順序、表現方法については、処理系定義との記述を確認しました。

C++については、「C++ Coding Standards」という本で2の補数であると読んだため、鵜呑みにしましたが、現在確認がとれていません。

負の数の除算については、新規に記事を起こしました。シフト演算についてはまた知識不足が露呈されそうなので、しっかり勉強してからにします。

投稿: konuma | 2006/01/10 21:30:03

コメントを書く