ビット演算と論理演算と高速化と。


みむらです。

「私と小鳥と鈴と」って中学か小学校かでやりませんでしたかね。
どうでもいいですね。ごめんなさい。

C でコードを書いていると、AND 演算や OR 演算をよく使うんですが、
よく “&” と “&&” だったり、 “|” と “||” を間違えていたりします。

や、眠たいときや全力で \バリバリバリバリ/ みたいなことしているときによくやるんですが、
自戒を込めてメモ。 たまにテストで填まった時によく確認するとこれ、ってこともあるので。

 

サンプル

#include <stdio.h>

int main(void){
	int a = 0xFF;
	int b = 0x64;

	printf("BITWISE = 0x%x, LOGICAL = 0x%x",a&b,a&&b);
	return 0;
}

実行するとわかりますが、結果として、

BITWISE = 0x64, LOGICAL = 0x1

こうなるかと思います。

 

つまり、

演算子 意味
& ビットごとの論理AND
&& 論理AND

となるわけで、意味が異なります。
時に、C ですと、結果が 0 以外であれば TRUE として処理するように考えていますと、
演算子を書き間違えていたときに問題が起きるので、

該当箇所は func() != 0 というような感じで書いておくといいのかも、とふと。

 

OR 演算も同じような感じで。

#include <stdio.h>

int main(void){
	int a = 0x100;
	int b = 0x64;

	printf("BITWISE = 0x%x, LOGICAL = 0x%x",a|b,a||b);
	return 0;
}

もちろん結果も、

BITWISE = 0x164, LOGICAL = 0x1

こんな感じで変わってきます。

 

 

最後に高速化について。

C や C++ ではコンパイラによると思うので、私はよくわかりませんが、
C# においては、演算子の使い分けで高速化ができるようです。

& 演算子 (C# リファレンス)
http://msdn.microsoft.com/ja-jp/library/vstudio/sbf85k1c.aspx

&& 演算子 (C# リファレンス)
http://msdn.microsoft.com/ja-jp/library/vstudio/2a723cdk.aspx

こちらのウェブサイトを見比べるとわかるのですが、

& 演算子の場合は双方を評価して結果を出しますが、
&& 演算子の場合は単なる論理 AND のため、最初の演算で結果が確定する場合は2個目以降は演算しません。

要は、
funcA() & funcb()
とあった場合は、結果にかかわらず両方を演算し、

funcA() && funcb()
とあった場合、 funcA() が False を返す場合、 funcb() は実行されません。

これを応用すれば演算が最小限で済み、演算が高速になることが容易に考えられます。
(ただ、評価順を最適化する必要があるかとは思います。)

 

またやろうと思えば、

if(hoge.IsEnable() && hoge.type == Hoge.HOGEHOGE) { }

のような、有効なら細かく調査していく みたいなことも書けるのだとは思いますが、
私のような人には危険な気がしますが。

 

 

そんなこんなで特に私の場合、ビットごとの論理演算をしているかどうか。
これを間違えないようにコードを書きたいですね。

ってことで、よく間違いをやらかすのでメモ。

 


追記(2012/11/04 7:07):

記事についてコメントをいただきました。

最後の部分について、
世間では「ショートサーキット評価」と言うそうで、言語仕様で明確に定義されているとのこと。

http://ja.wikipedia.org/wiki/%E7%9F%AD%E7%B5%A1%E8%A9%95%E4%BE%A1

また、返値もブーリアンではなく最後に評価した値となる言語もあるようで、いろいろとおもしろそうです。


 

関連記事