对有符号整数类型的按位运算的结果是否定义明确?

时间:2015-10-21 05:15:05

标签: c++ bit-manipulation standards c++14 signed

考虑以下代码:

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

3 个答案:

答案 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×2 ^ 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转移到那里的位位置,这是未定义的行为。