复制单个位的值很简单,只需清除然后设置它:
int copy(int from, int offset, int to) {
int mask = 1 << 31-offset;
return to & ~mask | from & mask;
}
是否可以使用以下签名相当有效地执行此操作?
/* to - a word to set the bit on
* mask - mask specifying the bit to set/clear and the value of that bit:
* - if mask contains exactly one set bit, set that bit on 'to';
* - if mask contains exactly one zero, clear that bit on 'to';
*/
int copy_bit(int mask, int to);
这不仅仅是学术上的(尤其不是家庭作业;)。 我出于语法原因并将其作为二元运算符实现。 我想出了这个:
int copy_bit(int mask, int to) {
int lowestZero = ~mask & (mask+1);
//overflow 'clear' masks to zero highest bit; 0 for clear, ~0 for set.
int switch = (mask | 0x80000000 | lowestZero) +1 >> 31;
return to & (switch | mask) | (switch & mask);
}
然后我通过减少表达式来削减一些操作:
int switch = -(~mask & 0x7fffffff & ~mask-1) >> 31;
有更好的方法吗?
答案 0 :(得分:2)
这是一个在实践中产生良好的无分支代码的简短代码:
int copy_bit(int mask, int to) {
return (mask - 1 < 0) ? to & mask : to | mask;
}
copy_bit(int, int):
lea edx,[rdi-0x1]
mov eax,edi
or edi,esi
and eax,esi
test edx,edx
cmovg eax,edi
ret
所以只有6条指令(不包括ret
),包括一条cmov
1 和15字节的代码。
将它与问题中显示的方法的程序集进行比较,该程序集需要15条指令(无cmov
)和36字节代码:
copy_bit_orig(int, int):
lea eax,[rdi+0x1]
mov edx,edi
not edx
and edx,eax
mov eax,edi
or eax,0x80000000
or edx,eax
mov eax,edi
add edx,0x1
shr edx,0x1f
or eax,edx
and edi,edx
and esi,eax
mov eax,esi
or eax,edi
ret
请记住,您的解决方案涉及未定义的行为,因为操作(mask + 1)
可能会溢出,C
和C++
中未定义。我需要将演员表添加到我的答案中,否则gcc会利用这种行为将其编译为不符合您期望的代码。
1 我调出cmov
因为在某些架构上它比简单的ALU指令慢,例如2个周期。然而,在最近的英特尔CPU上它很快。