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

时间:2014-08-19 10:53:51

标签: c++ casting bit-manipulation

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

我问这个澄清问题是为了能够清理其他问题,删除红色鲱鱼并保持最相关的信息完好无损。

问题在于我们要清除变量的两个最低位:

offset = offset & ~3;

这看起来很危险,因为无论~3是什么int都是offset,所以我们最终可能会掩盖不适合int& #39;的宽度。例如,如果int为32位宽且offset为64位宽类型,则可以想象此操作将丢失offset的32个最高有效位。

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

这种行为是否符合标准?我问,因为看起来这种行为可能依赖于特定的实现和/或硬件细节,但我希望能够根据语言标准推荐正确的代码。


如果我尝试删除最不重要的位,我可以使操作产生不希望的结果。这是因为~(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?

1 个答案:

答案 0 :(得分:4)

在符号幅度表示中无效。在具有32位整数的表示中,~3-0x7FFFFFFC。当它扩展到64位(带符号)时,将保留该值-0x7FFFFFFC。所以我们不会说在该系统中会发生符号扩展;并且你将错误地掩盖所有32位及更高位。

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

但是,我个人试图避免编写它,因为当我稍后检查我的代码中的错误时,我将不得不再次讨论所有这些讨论! (以及一个更随意的程序员对这里的错综复杂有什么希望)。我只对无符号类型进行按位运算,以避免所有这些。