如果设置var
的第1,3,5,7,9,11,13或15位中的一个,我希望FALSE
不等input
。< / p>
一个似乎相当常见的解决方案是:
int var = 1 & (input >> 1) ||
1 & (input >> 3) ||
1 & (input >> 5) ||
1 & (input >> 7) ||
1 & (input >> 9) ||
1 & (input >> 11) ||
1 & (input >> 13) ||
1 & (input >> 15);
但是,我担心这会导致编译器生成不必要的长代码。
以下代码也会产生所需的结果。它会更有效率吗?
int var = input & 0b1010101010101010;
谢谢!
答案 0 :(得分:3)
您的第二个示例不等同于。
你想要的是(使用非标准的二进制文字):
int var = !!(input & 0b1010101010101010));
或者使用hex-literals(那些是标准的):
int var = !!(input & 0xaaaa));
更改:使用十六进制常量和双重否定(相当于!= 0)
这预先假定input
不是volatile
,也不是原子类型。
一个好的编译器应该优化两个相同的指令(大多数现代编译器都足够好)。
最后,测试和测量,大多数编译器将输出生成的汇编代码,你甚至不需要反汇编程序!
答案 1 :(得分:2)
如果input
是易失性的,如果设置了第1位,编译器将需要读取一次,第1位的两次清除但设置为3,如果第1位和第3位清除但是5则是编译器可能有办法优化代码进行单独的位测试,但必须分别测试这些位。
如果input
不是易失性的,编译器可以优化代码,但我不会特别期望它。我希望任何编译器,无论多么古老,都要优化
int var = (input & (
(1 << 1) | (1 << 3) | (1 << 5) | (1 << 7) |
(1 << 9) | (1 << 11) | (1 << 13) | (1 << 15)
) != 0);
这似乎是你所追求的。
答案 2 :(得分:1)
它将取决于处理器以及可用的指令,以及优化编译器的优点。我怀疑在你的情况下,这些代码行中的任何一行都会编译成相同的指令。
但我们可以做得比怀疑更好,你可以自己检查。使用gcc,使用-S
编译器标志让它输出它生成的程序集。然后你可以自己比较一下。
答案 3 :(得分:1)
正统的解决方案应该是使用遗忘的位域来映射你的标志,比如
struct
{
bool B0: 1;
bool B1: 1;
bool B2: 1;
bool B3: 1;
bool B4: 1;
bool B5: 1;
bool B6: 1;
bool B7: 1;
bool B8: 1;
bool B9: 1;
bool B10: 1;
bool B11: 1;
bool B12: 1;
bool B13: 1;
bool B14: 1;
bool B15: 1;
} input;
并使用表达式
bool Var= input.B1 || input.B3 || input.B5 || input.B7 || input.B9 || input.B11 || input.B13 || input.B15;
我怀疑优化编译器会使用单程掩码技巧,但老实说我还没有尝试过。
答案 4 :(得分:1)
处理效果取决于编译器。
我测试了此代码的一个小变体:
int test(int input) {
int var = 1 & (input >> 1) ||
1 & (input >> 3) ||
1 & (input >> 5) ||
1 & (input >> 7) ||
1 & (input >> 9) ||
1 & (input >> 11) ||
1 & (input >> 13) ||
1 & (input >> 15);
return var != 0;
}
对于x64,全部使用-O2编译
GCC
xor eax, eax
and edi, 43690
setne al
ret
非常好。这正是你所希望的转变。
锵:
testw $10922, %di # imm = 0x2AAA
movb $1, %al
jne .LBB0_2
andl $32768, %edi # imm = 0x8000
shrl $15, %edi
movb %dil, %al
.LBB0_2:
movzbl %al, %eax
ret
是的,这有点奇怪。大多数测试都在一起进行..除了一个。我认为没有理由这样做,也许其他人可以对此有所了解。
真正的惊喜,ICC:
movl %edi, %eax #7.32
movl %edi, %edx #8.26
movl %edi, %ecx #9.26
shrl $1, %eax #7.32
movl %edi, %esi #10.26
shrl $3, %edx #8.26
movl %edi, %r8d #11.26
shrl $5, %ecx #9.26
orl %edx, %eax #7.32
shrl $7, %esi #10.26
orl %ecx, %eax #7.32
shrl $9, %r8d #11.26
orl %esi, %eax #7.32
movl %edi, %r9d #12.25
orl %r8d, %eax #7.32
shrl $11, %r9d #12.25
movl %edi, %r10d #13.25
shrl $13, %r10d #13.25
orl %r9d, %eax #7.32
shrl $15, %edi #14.25
orl %r10d, %eax #7.32
orl %edi, %eax #7.32
andl $1, %eax #7.32
ret #15.21
好的,所以它优化了一点 - 没有分支,1 &
&#39;被卷起来。但这令人失望。
您的里程可能会有所不同。为了安全起见,您当然可以直接使用简单版本,而不是依赖于编译器。