【问题标题】:Are the result of bitwise operations on signed integral types well-defined?有符号整数类型的按位运算结果是否定义明确?
【发布时间】:2016-01-19 23:39:17
【问题描述】:

考虑这段代码:

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++ bit-manipulation standards c++14 signed


【解决方案1】:

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

5.11 [expr.bit.and] 中的位运算规范, 5.12 [expr.xor] 和 5.13 [expr.or] 在描述操作时使用了未定义的术语“按位”,但没有说明它是否是 可见的值或对象表示。

解决方案的一部分可能是将“位”(目前在 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 在结果类型中是可表示的,那么这就是结果值; 否则,行为未定义。

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

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

【讨论】:

    【解决方案2】:

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

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

    然后它说当满足以下所有条件时,左移运算符 E1

    • 左操作数具有带符号类型。
    • 左操作数要么是负值,要么是非负值,因此 E1 × 2^E2 无法在结果类型中表示。

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

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

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

    【讨论】:

      【解决方案3】:

      运算符 &amp;|^ 是按位的,并且处理各个位,因此它们将完全按照您编写的操作:应用 mask

      左移&lt;&lt; 运算符有点棘手。如果您移动负值或将 1 移动到符号位或更多位置,则会导致未定义的行为。

      static_cast&lt;integer&gt;(1)&lt;&lt;static_cast&lt;integer&gt;(bits-1);

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

      【讨论】:

        猜你喜欢
        • 2012-11-12
        • 1970-01-01
        • 2019-04-30
        • 1970-01-01
        • 1970-01-01
        • 2023-04-11
        • 1970-01-01
        • 2015-03-24
        • 2018-10-08
        相关资源
        最近更新 更多