在询问如何做wrapped N bit signed subtraction的问题时,我得到了以下答案:
template<int bits>
int
sub_wrap( int v, int s )
{
struct Bits { signed int r: bits; } tmp;
tmp.r = v - s;
return tmp.r;
}
这很简洁,但编译器将如何实现呢?从this question开始,我认为访问位字段或多或少与手动操作相同,但与本例中的算术结合时又如何呢?它会像一个好的手动比特法一样快吗?
如果有人想要具体,那么“编译器”角色中“gcc”的答案会很棒。我已经尝试过阅读生成的程序集,但它目前已超出我的范围。
答案 0 :(得分:2)
编译器了解示例中r
的大小和确切位置。假设它就像
[xxxxrrrr]
然后
tmp.r = X;
可以,例如被扩展为(b-后缀表示二进制文字,&
是按位,|
是按位或)
tmp = (tmp & 11110000b) // <-- get the remainder which is not tmp.r
| (X & 00001111b); // <-- put X into tmp.r and filter away unwanted bits
想象一下你的布局是
[xxrrrrxx] // 4 bits, 2 left-shifts
扩张可能是
tmp = (tmp & 11000011b) // <-- get the remainder which is not tmp.r
| ((X<<2) & 00111100b); // <-- filter 4 relevant bits, then shift left 2
X
实际上是什么样的,无论是复杂的表述还是仅仅是文字,实际上都是无关紧要的。
如果你的体系结构不支持这样的按位运算,那么仍然存在乘法和除以2的幂来模拟移位,并且这些也可能用于滤除不需要的位。
答案 1 :(得分:2)
如另一个问题所述,无符号包装数学可以完成:
int tmp = (a - b) & 0xFFF; /* 12 bit mask. */
将写入(12位)位域将完全执行,有符号或无符号。唯一的区别是您可能会从编译器收到警告消息。
对于阅读,你需要做一些不同的事情。
对于无符号数学,这足以做到这一点:
int result = tmp; /* whatever bit count, we know tmp contains nothing else. */
或
int result = tmp & 0xFFF; /* 12bit, again, if we have other junk in tmp. */
对于有符号数学,额外的魔力是符号扩展:
int result = (tmp << (32-12)) >> (32-12); /* asssuming 32bit int, and 12bit value. */
所有这一切都是在更广泛的int中复制位域的最高位(第11位)。
这正是编译器对位域所做的工作。无论您是手动编码还是作为位域编码都取决于您,但只需确保您获得正确的数字。
(我没有阅读标准,但我怀疑依靠bitfields在溢出时做正确的事情可能不安全?)