如何将单个掩码给出的一位的值复制('移植')给一个单词?

时间:2017-01-25 03:21:17

标签: bit-manipulation bitwise-operators

复制单个位的值很简单,只需清除然后设置它:

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;

有更好的方法吗?

1 个答案:

答案 0 :(得分:2)

这是一个在实践中产生良好的无分支代码的简短代码:

int copy_bit(int mask, int to) {
  return (mask - 1 < 0) ? to & mask : to | mask;
}

这里是assembly generated on gcc

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)可能会溢出,CC++中未定义。我需要将演员表添加到我的答案中,否则gcc会利用这种行为将其编译为不符合您期望的代码。

1 我调出cmov因为在某些架构上它比简单的ALU指令慢,例如2个周期。然而,在最近的英特尔CPU上它很快。