bit-manipulation - 如何将单个掩码给出的一位值复制到一个字(('transplant')?

标签 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;

有更好的方法吗?

最佳答案

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

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)可能会溢出,这在 C 中未定义和C++ 。我需要将强制转换添加到我的答案中,否则 gcc 会利用此行为将其编译为不符合您预期的代码。


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

关于bit-manipulation - 如何将单个掩码给出的一位值复制到一个字(('transplant')?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41842839/

相关文章:

c++ - 按位与逻辑

c - 简单的位操作失败

binary - 对人口计数算法有所了解

java - java中的位操作,与c相比

c - 为什么 (1 >> 0x80000000) == 1?

java - 如何(便宜地)计算n个可能元素的所有可能的length-r组合

将 uint8_t 十六进制值转换为二进制

java - Java中的按位移位运算符: syntax error with "=>>"?

java - 位或运算符以不同方法工作的详细解释

c++ - 一些随机的 C 问题(ascii 魔法和位运算符)