論理演算子とビット演算子の違いは?「&&」と「&」や、「||」と「|」の違いや短絡評価による効率化まで解説

論理演算子とビット演算子の違いは?「&&」と「&」や、「||」と「|」の違いや短絡評価による効率化まで解説
論理演算子とビット演算子の違いは?「&&」と「&」や、「||」と「|」の違いや短絡評価による効率化まで解説

 

 
読了目安:059

Are you want to translate this page to English? Please click this link to translate via ‘©Google Translate'!

ganohr.net favicon

みなさんこんにちは、ガノー(Twitter:Ganohr)です。

この記事では「&&||などの論理演算子」と「&|などのビット演算子」の違いを把握するために、サンプルコードを用いて解説します。

※ サンプルコードはPHPで記述していますが、JavaScript、Java、C、C#で共通する事項です。

更新履歴
2023/04/01 解説を追加
2023/02/17 公開

 

ビット演算子とは?

ビット演算子’(-えんざんし)は、字面の通りビット演算のために用いる演算子です。‘演算子’とは「+や÷(/)などの四則演算子などを含め、計算式におけるを扱うためのルール」です。

この記事ではビット演算子の中でも「&」演算子(ビット積演算子)と「|」(ビット和演算子)を扱います。

その他のビット演算子は以下の記事を参照されてください。

サムネイルPHP: ビット演算子 - Manual ... 外部サイトへアクセスwww.php.net

論理演算子とは?

論理演算子’(ろんりえんざんし)とは、特にプログラミングにおいて、条件式が真か偽かを判定するための演算子です。

論理演算子のより詳細な解説は以下の記事を参照されてください。

サムネイル条件文・条件式の論理和と論理積と論理否定を分かりやすく解説!コーディングスタイルも解説PHPやJava/JavaScript/C/C#等のコーディングにおいて,条件式を分かりやすく記述することは重要.そもそも条件式に用いる論理否定/論理和/論理積/排他的論理和とは何か?バグの少ないコーディングのためのコーディングスタイルを解説! ... 続きを読むganohr.net2023-02-17

 

この記事では論理演算子の中でも「&&」(論理積演算子)と「||」(論理和演算子)を扱います。

&&と&とandと||と|とorの違い

さて本題ですが、&&&and|||orの違いは何でしょうか。

これらの違いは、以下3点に要約されます。

  • ビット演算子か論理演算子かの違い
  • 優先順位の違い
  • 短絡評価を行うか否かの違い

なお、「ビット演算子か論理演算子かの違い」については既に解説済みのため割愛します。

その他2つについて違いを見ていきましょう。

優先順位の違い

演算子には優先順位があることはご存知でしょう。優先順位という言葉がピンと来ない方もいるかも知れませんが、四則演算において「×」(乗算子)と「÷」(除算子)の優先順位は高く、それに比べて「+」(加算子)と「-」(減算子)の優先順位は低いと小学校~中学校までの算数・数学にて習っているはずです。

例として以下の計算をしてみてください。Google検索で「3 + 4 × 5 ÷ 5 - 4 × 3」を計算してみる

3 + 4 × 5 ÷ 5 - 4 × 3
正解:-5
 (4 × 5 ÷ 5) … 4
 (4 × 3) … 12
 ∴ 3 + 4 - 12 = -5
誤答例
誤答例:9
 3 + 4 … 7
  7 × 5 … 35
   35 ÷ 5 … 7
    7 - 4 … 3
     3 × 3 … 9

この結果が9ではなく-5であるように、演算子にはそれぞれ優先順位が決まっています。

一般的なプログラミング言語においては、

ビット演算子は論理演算子よりも優先順位が高く、また積演算子(ビット積・論理積)は和演算子(ビット和・論理和)よりも優先順位が高くなっています。

 

加えてPHPでは「and」や「or」などの演算子では「&&」や「||」よりも優先順位が引く設定されています。

※ PHPにおける演算子の優先順位は以下を参考にされてください

サムネイルPHP: 演算子の優先順位 - Manual ... 外部サイトへアクセスwww.php.net

 

こうした演算子の優先順位は難解であり、また様々なバグの要因となってきたため留意が必要です。

 

実用的には「条件式の中ではビット演算子を使わない、andやor演算子を使わない」という制約を付けてプログラムしていきます。

$ng = true and false;
var_dump($ng);

$ok = true && false; var_dump($ok);
$better = (true && false); var_dump($better);
出力例
bool(true)
bool(false)
bool(false)
 
ノート

解説

$ngの計算は、実直に読むと「true 且つ false」は通常であれば「false」となるはずですが、「PHPのand演算子は代入演算子(=)よりも優先順位が低いため、「$ng = true」の代入結果にfalseandを取る動作をします(その結果を代入しないため無視される)。そのためvar_dump$ngを確認するとtrueの値になっており、多くのバグの温床になってきました。

 

2つ目の$okは、私達プログラマーが通常想定した通りの挙動を示します。要するに代入演算子よりも&&演算子は優先順位が高いため、まず&&演算を行った後、その結果を$okへ代入します。$ngと違い、ちゃんと「true 且つ false」の結果である「false」が代入されています。

 

よりベターなのは代入演算子や各種演算子を利用する際は、優先順位を括弧で定義することです。これなら優先順位によるバグは介入しようがありません。とはいえ、通常問題になるのはビット演算子や三項演算子などであるため、ある程度習熟したメンバー同士で開発できる場合はそこまで厳密に行う必要はありません。

論理演算子とビット演算子の違い:短絡評価を行うか否かの違い

短絡評価’(たんらくひょうか)とは、論理演算において、以降を評価する必要のないパターンが指定された場合に、評価を行わずに効率化する仕組みのことです。

 

「以降を評価する必要のないパターン」とは、具体的には論理積結合においてfalseが出現した位置以降の判定、及び論理和結合においてtrueが出現した以降の判定です。

var_dump( false || true || false || true );
var_dump( false || true && false && true );

 

出力例
bool(true)
bool(false)

 

この条件では、短絡評価され、式の途中で評価が確定します。

論理演算子による短絡評価の解説画像
論理演算子では評価が途中で確定する場合、それ以降の処理(判定)を呼びださないため、効率的

 

では実際に短絡評価が正しく動いているのか確認するコードを用いて、動作を確認してみましょう。

function d($val) {
    echo "\t" . var_export($val, true) . PHP_EOL;
    return $val;
}

echo "d関数のテスト" . PHP_EOL; d(false); d(true); echo PHP_EOL; var_dump(d(false)); echo PHP_EOL; var_dump(d(true));
出力例
d関数のテスト
false
true

false
bool(false)

true
bool(true)

 

d関数は、短絡評価を確認するために定義したデバッグ用の関数です。

引数で指定された値を順々に、画面にタブ文字を先頭に付与し、且つ、改行して出力します。

 

このd関数を用いて、実際に短絡評価を確認しましょう。

echo PHP_EOL; var_dump( d( false ) || d( true ) || d( false ) || d( true ) );
echo PHP_EOL; var_dump( d( false ) || d( true ) && d( false ) && d( true ) );
出力例
    false
true
bool(true)

false
true
false
bool(false)

 

実行結果を確認すると、たしかに途中で判定が終わり、残りの判定がスキップされていることが確認できます。

このように「短絡評価をうまく使うことで、パフォーマンス・チューニングに役立つ」ことがわかるでしょう。

これをビット演算子(|及び&)に置き換えて実行してみると、より理解がはかどります。
echo PHP_EOL; var_dump( d( false ) | d( true ) | d( false ) | d( true ) );
echo PHP_EOL; var_dump( d( false ) | d( true ) & d( false ) & d( true ) );
出力例


    false
    true
    false
    true
int(1)

    false
    true
    false
    true
int(0)

 

論理演算子を用いたパフォーマンス・チューニングでは、

  • 判定速度が早い条件を前に出す
  • 多くの内容を弾ける条件を前に出す

といった観点でプログラミングを行うことで、効率的なコードを作成できます。

 

ただし、こうした効率化はC言語の時代から引き継がれていますが、最近ではJITコンパイラーの技術革新により、実行時に自動的に最適化されることが多く、あまり意識する必要がなくなってきています。

とはいえ異様に遅いコードがある場合は、見直してみると良いでしょう。

条件式の中では、論理演算子(&&||)のみを用い、ビット演算子の混在や、and演算子やor演算子を使用しないようにしましょう

以下のコードを見てください。このd関数は、文字列で指定された計算を行い、与えられた式とその答えを画面に出力します。

$b = ['false', 'true'];
$ret = null;

function d($s) {     global $b;     global $ret;     echo str_pad($s, 36) . ":";     eval('$ret=(' . $s . ');');     echo " $b[$ret]\n"; }
d("false && false && false || true"); d("false && false && true || false"); d("false && false | true && true"); d("false && false || true && true"); d("false | true | true and false"); d("false | true | false and true");

 

この12 ~ 17行目に連なる各条件判定の結果が、trueになるかfalseになるか初見で且つ迅速に判別できるでしょうか?

PHPでは先述の通り|||&&&や、あまつさえorandの演算子の優先順位それぞれ異なります。

 

出力例
false && false && false || true     : true
false && false && true || false : false
false && false | true && true : false
false && false || true && true : true
false | true | true and false : false
false | true | false and true : true

 

これは極端な例ではありません。

プログラミングを行っていく中では、複数の条件を複雑に組み合わせなければならない例はいくらでもあります。

 

したがって、条件式では必ず「||&&のみを利用」する必要があります。

また次の節にて解説している「半角丸括弧による条件式の重み付け」を用いることで、簡単に理解しやすいコードを記述するよう気をつけねばなりません。

カッコで条件式の重み付けを明示しよう

先程のコードにおいて、以下の部分を見てください。

d("false && false | true && true");
d("false | true | true and false");
d("false | true | false and true");

これはビット演算子と論理演算子が混在していたり、条件式でありながらビット演算子が利用されているため理解しにくいコードになっています。

加えてビット演算子の優先順位が論理演算子よりも高い点に注意が必要です。

 

例題として、ビット演算子とand演算子の使用をやめて、論理演算子(&&演算子及び||演算子)のみを使用するようコードを書き換えてみましょう。

その際に活用するのが()(半角丸括弧)を用いた条件式の重み付けです。

方法は簡単で、優先順位を上げたい項と演算子の組み合わせを半角丸括弧で囲っていくだけです。複数の条件が入れ子になっている場合は、内側から外側に括弧で囲っていきます。

 

※ ご自身で実践したあと、折りたたまれた内容を展開して答え合わせをしてください。

回答例
d("false && (false || true) && true");
d("((false || true) || true) && false");
d("((false || true) || false) && true");

 

このように各演算の実行順序の違いは半角丸括弧を用いることで自由に制御できます

そしてビット演算子の実行順序の違いなどを意識する必要がなくなるため、判定順序が明確になります。

最後に

この記事はプログラミングを行う上で比較的初歩の内容ながら、あまり正しく理解されていない論理演算子(&&||)とビット演算子(&|)の違いを解説しました。

参考になったら、SNSへシェアしたり、ブックマークしてくだされば励みになります。

WordPressの不具合対応/カスタマイズ¥15,000~

PC歴25年超、SE歴10年超、WordPress運営歴7年超、WordPressエンジニア歴5年超のスキルとノウハウを提供します

当サイト管理人の「ガノー」(Ganohr)は、日本最大手且つ東証一部上場企業が運営するクラウドソーシングサイト『Lancers』にて、認定ランサーとして活動しています。


※ 認定ランサーとはLancersにより様々な能力 ( 高い仕事遂行率・高い顧客満足度・多くの実績、など ) を評価したプロフェッショナルを認定する制度です。

 

Cカテゴリの最新記事