编译器是如何实现位域运算的
How does the compiler implement bit field arithmetics?
当问一个关于如何做N位符号减法的问题时,我得到了以下答案:
template<int bits>
int
sub_wrap( int v, int s )
{
struct Bits { signed int r: bits; } tmp;
tmp.r = v - s;
return tmp.r;
}
这很简洁,但是编译器如何实现呢?从这个问题中我得出结论,访问位字段或多或少与手工操作相同,但是如果像本例中那样与算术结合使用呢?它会和手动操作一样快吗?
在"编译器"的角色中回答"gcc"将是伟大的,如果有人想要得到具体的。我已经尝试阅读生成的程序集,但它目前超出了我。
正如在另一个问题中所写的,无符号包装数学可以这样做:
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位)。
这正是编译器对位域所做的。无论你是手工编码还是作为位域,都取决于你,但只要确保你得到了正确的神奇数字。
(我没有读过标准,但我怀疑依靠位域在溢出时做正确的事情可能不安全?)
编译器知道示例中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的幂来模拟移位,并且可能这些也可以用来过滤掉不需要的位。
相关文章:
- C++Union/Struct位域的实现和可移植性
- 具有位域的结构的 Constexpr 构造函数
- 如何解释 #if/#else 位域?(VC++菜鸟)
- 枚举位域和聚合初始化
- 在有符号基础类型枚举的位域上溢出
- 如何检查位域是否跨越所有位
- 访问位域联合是C++标准中常见的初始数据未定义行为
- 锈迹位域和枚举C++样式
- C/C++:uint8_t位域的行为不正确且不一致
- 如何将位域写入二进制文件
- 字节序和大小为 1 的位域
- 面向对象编程 - 位域私有结构的公共参考
- 使用位域的结构大小不正确
- 在 mingw 的 gcc-5.3 下具有混合位域和枚举递减的结构的意外大小
- 如何将位域成员的所有位设置为 1
- 带有 C++ 位域的匿名 typedef 的前向声明
- 通过套接字C++发送时,位域值会更改
- 有符号的 int 会中断较大位域的某些部分
- 位域.为什么没有输出
- 编译器是如何实现位域运算的