考虑以下代码:
using integer = int; // or any other fundamental integral type
using unsigned_integer = typename std::make_unsigned<integer>::type;
constexpr integer bits = std::numeric_limits<unsigned_integer>::digits;
integer value = -42; // or any value
integer mask = static_cast<integer>(1)<<static_cast<integer>(bits-1);
bool result_and = value & mask;
bool result_or = value | mask;
bool result_xor = value ^ mask;
我想知道根据标准定义的这些操作有多好。我是否有保证在所有架构上获得相同的结果?我确定对所有体系结构上的符号位进行操作,对于正数,此符号位为0
,对于负数,此符号位为1
?
答案 0 :(得分:3)
标准中的按位和,按位或按位xor的结果目前未指定,特别是从未定义术语按位。我们defect report 1857: Additional questions about bits 涵盖了这个问题并说:
5.11 [expr.bit.and]中按位操作的规范, 5.12 [expr.xor]和5.13 [expr.or]在描述操作时使用未定义的术语“按位”,而不指定它是否是 视图中的值或对象表示。
部分解决方案可能是将“bit”(在C ++中当前未定义)定义为给定功率为2的值。
,决议是:
CWG决定重新制定运营描述 他们自己避免引用比特,分裂更大 定义“位”之类的问题,以便进一步发布1943年 考虑。
这导致合并defect report 1943: Unspecified meaning of “bit”。
左移有符号类型的结果将取决于底层表示。我们可以从defect report 1457: Undefined behavior in left-shift看到这一点,这使得它被明确定义为左移到符号位并说:
5.8 [expr.shift]第2段的当前措辞使其未定义 用于创建给定类型的最负整数的行为 将(签名)1左移到符号位,即使不是 在大多数情况下,不常见并且正常工作 (二次补充)架构:
...如果E1具有有符号类型和非负值,并且E1⨯2E2在结果类型中可表示,那么这就是结果值; 否则,行为未定义。
因此,此技术不能用于常量表达式, 这将打破大量的代码。
注意强调语句在大多数情况下都能正常工作 (二次补充)架构。所以它依赖于底层表示,例如二进制补码。
答案 1 :(得分:1)
关于左右移位运算符,来自C ++标准第5.8节:
如果右操作数为负数或更大,则行为未定义 大于或等于提升的左操作数的位数。
然后它说左移位算子E1&lt;&lt;当满足以下所有条件时,E2会导致未定义的行为:
关于右移位运算符E1&gt;&gt; E2,如果左操作数有签名类型和负值,则行为依赖于实现。
按位AND,XOR和OR运算符是针对所有整数类型定义的。这分别在第5.11,5.12和5.13节中规定。
但是,请注意,有符号整数值的表示可以是二进制补码,一个补码或有符号大小。大多数编译器使用二进制补码表示。这些包括gcc,VC ++,icl和Clang。
答案 2 :(得分:1)
运算符&
,|
和^
是按位的,处理各个位,因此它们将完全按照您所写的内容执行:应用mask
。
左移<<
运算符有点棘手。如果您移动负值或者将1移位到符号位或更高位置,它将导致未定义的行为。
static_cast<integer>(1)<<static_cast<integer>(bits-1);
似乎你将1
转移到那里的位位置,这是未定义的行为。