是对定义良好的有符号整型进行位操作的结果

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

本文关键字:整型 结果 位操作 符号 定义      更新时间:2023-10-16

考虑以下代码:

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表示负数?

位和、位或和位异的结果目前在标准中没有明确规定,特别是术语从未定义。我们有缺陷报告1857:关于比特的附加问题,它涵盖了这个问题,并说:

5.11 [exp .bit.and]中按位操作的规范,5.12 [expr。Xor],和5.13 [expr。Or]在描述操作时使用未定义的术语"按位",而不指定它是否是视图中的值或对象表示。

解决这个问题的部分方法可能是将" bit "(在c++中目前没有定义)定义为给定2的幂的值。

,分辨率为:

CWG决定重新制定操作描述它们自己避免引用比特,分裂出较大的关于定义"位"之类的问题,1943年再说吧考虑。

这导致了一个合并的缺陷报告1943:"bit"的未指定含义。

左移有符号类型的结果将取决于底层表示。我们可以从缺陷报告1457中看到这一点:左移中的未定义行为使得左移到符号位并说:

当前的措辞5.8 [expr。第2段使它没有定义创建给定类型的最大负整数的行为左移a(有符号)1到符号位,即使这不是不寻常的完成和工作正确的大多数(twos-complement)架构:

…如果E1具有带符号类型且非负值,且E1在结果类型中可表示,则该值为结果值;否则,行为是未定义的。

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

注意语句的重点在大多数情况下是正确的 (twos-complement)架构。因此,它依赖于底层表示,例如two -complement。

关于左移和右移操作符,来自c++标准第5.8节:

如果右操作数为负或大于负,则该行为未定义小于或等于提升后左操作数的位长度

左移运算符E1 <<当满足以下所有条件时,E2将导致未定义行为:

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

对于右移操作符E1>> E2,如果左操作数为带符号类型且为负值,则其行为与实现相关。

对于所有整型都定义了位与、异或和或操作符。这分别在第5.11、5.12和5.13节中指定。

但是,请注意,有符号整数值的表示可以是2的补数、1的补数或有符号幅度。大多数编译器使用Two的补码表示。包括gcc、vc++、icl和Clang。

操作符&, |^是按位的,并且处理单个位,因此它们将完全按照您所写的操作:应用mask

左移<<操作符有点棘手。如果将负值移位,或者将1移位到符号位或其他位置,将导致未定义行为。

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

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