3つの真偽値のうち2つ以上が真であるかを調べる
元ネタ
Check if at least 2 out of 3 booleans is true
http://stackoverflow.com/questions/3076078/check-if-at-least-2-out-of-3-booleans-is-true/
一番単純な例
変数2つが真であるかを検査すれば良く,順列を考えない2変数の組み合わせの数はとなり,3通りの組み合わせについて全て検査すれば良い事になる.
つまり,以下のようになる.
int at_least2(int a, int b, int c) { return ((a && b) || (a && c) || (b & c)); }
これのアセンブリコードは以下の通り.なおコンパイラはMacOS X付属のgcc 4.2.1となる.
.text .globl _at_least2 _at_least2: pushl %ebp movl %esp, %ebp subl $24, %esp cmpl $0, 8(%ebp) # a == 0 なら je L2 # L2 へジャンプ cmpl $0, 12(%ebp) # b != 0 なら jne L4 # L4 へジャンプ L2: cmpl $0, 8(%ebp) # a == 0 なら je L5 # L2 へジャンプ cmpl $0, 16(%ebp) # c != 0 なら jne L4 # L4 へジャンプ L5: cmpl $0, 12(%ebp) # b == 0 なら je L7 # L7 へジャンプ cmpl $0, 16(%ebp) # c == 0 なら je L7 # L7 へジャンプ L4: movl $1, -12(%ebp) jmp L9 L7: movl $0, -12(%ebp) L9: movl -12(%ebp), %eax leave ret .subsections_via_symbols
判定までに必要な最大のインストラクション数は12個(#でコメントのある行)となっている.ただし,a と b が真の場合は,一気に L4 までジャンプするので,最短では4インストラクションで結果がわかることになる.
別の解答例
もっと効率的にすると,以下のようになるとの答えがあった.
int at_least2(int a, int b, int c) { return a ? (b || c) : (b && c); }
このコードをアセンブリにしてみると以下のようになる.
.text .globl _at_least2 _at_least2: pushl %ebp movl %esp, %ebp subl $24, %esp cmpl $0, 8(%ebp) # a == 0 なら je L2 # L2 へジャンプ cmpl $0, 12(%ebp) # b != 0 なら jne L4 # L4 へジャンプ cmpl $0, 16(%ebp) # c == 0 なら je L6 # L6 へジャンプ L4: movl $1, -16(%ebp) jmp L7 L6: movl $0, -16(%ebp) L7: movl -16(%ebp), %eax movl %eax, -20(%ebp) jmp L8 L2: cmpl $0, 12(%ebp) # b == 0 なら je L9 # L9 へジャンプ cmpl $0, 16(%ebp) # c == 0 なら je L9 # L9 へジャンプ movl $1, -12(%ebp) jmp L12 L9: movl $0, -12(%ebp) L12: movl -12(%ebp), %eax movl %eax, -20(%ebp) L8: movl -20(%ebp), %eax leave ret .subsections_via_symbols
ここでは,判定までに必要な最大のインストラクション数は10個となり,先の例よりも2インストラクション減少している.ただし,判定に最小限必要なインストラクション数は4であり,これは変わっていない.
もう少し減ってるんじゃないかと思ったけれど,2インストラクションしか減少していなかった.まぁ,割合にすると17%のパフォーマンス向上なので多いのかな?