可以〜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