c - MSB 为 1 时算术右移 0s
问题描述
作为练习,我必须编写以下函数:
将 x 乘以 2,如果溢出则饱和到 Tmin / Tmax,仅使用按位和位移操作。
现在这是我的代码:
// xor MSB and 2nd MSB. if diferent, we have an overflow and SHOULD get 0xFFFFFFFF. otherwise we get 0.
int overflowmask = ((x & 0x80000000) ^ ((x & 0x40000000)<<1)) >>31;
// ^ this arithmetic bit shift seems to be wrong
// this gets you Tmin if x < 0 or Tmax if x >= 0
int overflowreplace = ((x>>31)^0x7FFFFFFF);
// if overflow, return x*2, otherwise overflowreplace
return ((x<<1) & ~overflowmask)|(overflowreplace & overflowmask);
现在什么时候overflowmask
应该0xFFFFFFFF
是 1,这意味着算术>>31
位移在 0 中而不是 1 中移动(MSB 异或到 1,然后移到底部)。
x 有符号且 MSB 为 1,因此根据 C99,算术右移应填充 1。我错过了什么?
编辑:我只是猜测这段代码不正确。要检测溢出,第二个 MSB 为 1 就足够了。
但是,我仍然想知道为什么移位填充为 0。
编辑:
示例:x = 0xA0000000
x & 0x80000000 = 0x80000000
x & 0x40000000 = 0
XOR => 0x80000000
>>31 => 0x00000001
编辑:
解决方案:
int msb = x & 0x80000000;
int msb2 = (x & 0x40000000) <<1;
int overflowmask = (msb2 | (msb^msb2)) >>31;
int overflowreplace = (x >>31) ^ 0x7FFFFFFF;
return ((x<<1) & ~overflowmask) | (overflowreplace & overflowmask);
解决方案
>>
即使在二进制补码机器上,负操作数上的右移 ( ) 行为也是由实现定义的。
一种更安全的方法是使用无符号类型并在 MSB 中显式地 OR-。
当您使用它时,您可能还想使用固定宽度类型(例如uint32_t
),而不是在不符合您期望的平台上失败。