可以〜3安全地自动加宽?

在回答另一个问题时,我最终试图将操作数转换为~运算符,但我无法想出一个不会导致错误结果的场景。

我问这个澄清问题,以便能够清理其他问题,删除红鲱鱼,并保持最相关的信息不变。

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

offset = offset & ~3;

这看起来很危险,因为~3将是一个int无论什么offset是,所以我们最终可能会屏蔽不适合进位int的宽度。 例如,如果int是32个位宽, offset是64位宽的类型,可以想象,该操作将失去的32最显著位offset

但是,实际上这种危险似乎并没有表现出来。 相反, ~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)进行符号扩展,所以无论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-0x7FFFFFFC 。 当这被扩展到64位(带符号)时,该值被保留,为-0x7FFFFFFC 。 所以我们不会说在那个系统中发生符号扩展; 你会错误地掩盖掉所有的32位和更高的位。

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

但是,我个人会尽量避免编写它,因为之后在检查我的代码以查找错误时,我将不得不再次进行所有这些讨论! (并且更偶然的编码者在这里理解错综复杂的情况有什么希望)。 我只对无符号类型进行按位操作,以避免所有这些。

链接地址: http://www.djcxy.com/p/80949.html

上一篇: Can ~3 safely be widened automatically?

下一篇: How to allow URLs to contain dots in ASP.NET MVC5?