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

Are the result of bitwise operations on signed integral types well-defined?

考虑这段代码:

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?

关于左移和右移运算符,来自C++标准第5.8节:

The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.

然后它表示当满足以下所有条件时,左移运算符 E1 << E2 会导致未定义的行为:

  • 左操作数是有符号类型。
  • 要么左操作数具有负值,要么具有非负值,使得 E1 × 2^E2 在结果类型中不可表示。

关于右移运算符 E1 >> E2,如果左操作数具有带符号类型和负值,则行为取决于实现。

按位 AND、XOR 和 OR 运算符对所有整数类型都有明确的定义。这分别在第 5.11、5.12 和 5.13 节中指定。

但是,请注意,有符号整数值的表示可以是二进制补码、个数补码或有符号大小。不过,大多数编译器都使用二进制补码表示。这些包括 gcc、VC++、icl 和 Clang。

运算符 &|^ 是按位的,并且处理单个位,因此它们将完全按照您编写的方式执行:应用 mask.

左移<<运算符有点棘手。如果您移动负值或将 1 移动到符号位位置或超出符号位,将导致未定义的行为。

static_cast<integer>(1)<<static_cast<integer>(bits-1);

您似乎将 1 移到那里的符号位位置,这是未定义的行为。

按位与、按位或和按位异或的结果目前在标准中未指定,特别是术语 按位 从未定义。我们有 defect report 1857: Additional questions about bits 涵盖了这个问题并说:

The specification of the bitwise operations in 5.11 [expr.bit.and], 5.12 [expr.xor], and 5.13 [expr.or] uses the undefined term “bitwise” in describing the operations, without specifying whether it is the value or object representation that is in view.

Part of the resolution of this might be to define “bit” (which is otherwise currently undefined in C++) as a value of a given power of 2.

决议是:

CWG decided to reformulate the description of the operations themselves to avoid references to bits, splitting off the larger questions of defining “bit” and the like to issue 1943 for further consideration.

这导致合并 defect report 1943: Unspecified meaning of “bit”

左移有符号类型的结果将取决于底层表示。我们可以从 defect report 1457: Undefined behavior in left-shift 中看到这一点,它很好地定义为左移到符号位并表示:

The current wording of 5.8 [expr.shift] paragraph 2 makes it undefined behavior to create the most-negative integer of a given type by left-shifting a (signed) 1 into the sign bit, even though this is not uncommonly done and works correctly on the majority of (twos-complement) architectures:

...if E1 has a signed type and non-negative value, and E1 ⨯ 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

因此,该技术不能用于常量表达式, 这将破坏大量代码。

注意语句 的强调在大多数情况下都正确 (二进制补码)架构。所以它取决于底层表示,例如二进制补码。