c++ - ~3 可以安全地自动加宽吗?

标签 c++ casting bit-manipulation

在回答 another question 时,我最终试图证明将操作数转换为 ~ 是合理的运算符,但我无法想出不转换它会产生错误结果的场景。

我问这个澄清问题是为了能够清理另一个问题,去除转移注意力的问题,只保留最相关的信息。

问题是我们要清除变量的最低两位:

offset = offset & ~3;

这看起来很危险,因为 ~3将是 int不管怎样offset是的,所以我们最终可能会屏蔽不适合 int 的位的宽度。例如,如果 int是 32 位宽并且 offset是 64 位宽的类型,可以想象这个操作会丢失 offset 的 32 个最高有效位。 .

然而,在实践中,这种危险似乎并没有表现出来。相反,~3 的结果符号扩展以填充 offset 的宽度,即使 offset未签名。

这种行为是标准强制要求的吗?我问是因为似乎这种行为可能依赖于特定的实现和/或硬件细节,但我希望能够根据语言标准推荐正确的代码。


如果我尝试删除 32. 最低有效位,我可以使操作产生不希望的结果。这是因为 ~(1 << 31) 的结果在二进制补码表示(实际上是一个补码表示)的 32 位带符号整数中将为正数,因此对结果进行符号扩展将使所有高位不设置。

offset = offset & ~(1 << 31); // BZZT! Fragile!

在这种情况下,如果 int是 32 位宽并且 offset是一个更宽的类型,这个操作将清除所有的高位。

但是,在另一个问题中提出的解决方案似乎并没有解决这个问题!

offset = offset & ~static_cast<decltype(offset)>(1 << 31); // BZZT! Fragile!

似乎1 << 31将在类型转换之前进行符号扩展,因此无论是否decltype(offset)有符号或无符号,此转换的结果将设置所有高位,这样再次操作将清除所有这些位。

为了解决这个问题,我需要在扩大之前使数字无符号,方法是使整数文字无符号( ​​1u << 31 似乎有效)或将其转换为 unsigned int :

offset = offset &
    ~static_cast<decltype(offset)>(
        static_cast<unsigned int>(
            1 << 31
        )
    );
// Now it finally looks like C++!

此更改使原始危险变得相关。当位掩码无符号时,反转的位掩码将通过将所有高位设置为零来加宽,因此在反转之前拥有正确的宽度很重要。

这让我得出结论,有两种方法可以推荐清除一些位:

1:offset = offset & ~3;

优点:简短、易于阅读的代码。

缺点:据我所知没有。但是标准保证了行为吗?

2:offset = offset & ~static_cast<decltype(offset)>(3u);

优点:我理解这段代码的所有元素是如何工作的,而且我相当有信心标准保证它的行为。

缺点:它不完全是舌头滚动。


你们能帮我弄清楚选项 1 的行为是否得到保证,或者我是否必须求助于推荐选项 2?

最佳答案

它在符号大小表示中无效。在具有 32 位整数的表示中,~3-0x7FFFFFFFC。当它扩展到 64 位(有符号)时,该值将被保留,-0x7FFFFFFFC。所以我们不会说符号扩展发生在那个系统中;并且您将错误地屏蔽掉所有 32 位和更高位。

在二进制补码中,我认为 offset &= ~3 始终有效。 ~3-4,因此无论 64 位类型是否已签名,您仍然会得到一个只有底部 2 位未设置的掩码。

但是,我个人会尽量避免编写它,因为稍后检查我的代码是否存在错误时,我将不得不再次进行所有这些讨论! (更随便的编码员有什么希望理解这里的复杂性)。我只对无符号类型进行按位运算,以避免所有这些。

关于c++ - ~3 可以安全地自动加宽吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25381581/

相关文章:

c++ - 将 std::string 分配给 char[n] 以进行 POD 初始化时如何解决不兼容的类型

c++ - 函数样式转换与调用构造函数

C# 转换枚举 InvalidCastException 错误

c++ - 获取按位不为零的无符号整数的最大值

python - 在 Python 中解析 128 字节十六进制 block 中的位

c++ - C++ 和 Scala 之间有多少互操作性?

c++ - 为什么这行得通?不合逻辑的数组访问

c - c中unsigned long long的按位运算

c++ - sfml 键盘条件或

forms - Angular 将选择值转换为 int