如何以可移植的方式在C中执行算术右移?

Cal*_*ius 5 c bit-manipulation

我们正在编写一个模拟器,我们需要在该模拟器中传播右移信号。仿真系统使用2的补码。

我读到>>C中有符号整数的运算符是实现定义的。因此,我不能依靠它会在所有平台上产生正确位模式的事实。

这意味着我将需要使用位操作来重现算术右移,并且我将尽可能避免不必要的分支。

编辑:

回应评论:

“缺少的位是当在x >> y中的x中设置符号位时,OP需要定义什么结果是“正确的””

我基本上想重现SAR x86指令的行为。负数用2的补码表示。右移基本上也应意味着除以2即可得到负数。

这意味着对于以1开头的位模式。因此,对于1xxxxxxx,向右移位应为11xxxxxx。对于以0开头的位模式,因此0xxxxxxx右移应为00xxxxxx。因此,MSB是“粘性”的。没有定义超过字长的移位。

Fri*_*ich 5

int s = -((unsigned) x >> 31);
int sar = (s^x) >> n ^ s;
Run Code Online (Sandbox Code Playgroud)

这需要 5 次按位运算。

编辑:正如 user16217248 所指出的,s也可以计算为int s = -(x<0);。对于下面的 godbolt 示例,编译器确实生成了相同的代码。

解释

正如已经提到的,算术右移x >> n对应于除法x / 2**n。如果系统只支持逻辑右移,可以先将负数转换为正数,然后再将其符号复制回来sgn(x) * (abs(x)/2**n)。这相当于右移前后乘以+/-1sgn(x) * ((sgn(x)*x)/2**n)

整数乘以 +/-1 可以用条件无分支否定s^(s+x)或来模拟(x^s)-s。当s是 时0,什么也没有发生并且x保持不变,因此与 1 相乘。当s是 时-1,我们得到-x,因此与 相乘-1

代码片段的第一行-((unsigned) x >> 31)提取符号位。在这里,unsigned转换确保编译成逻辑右移(汇编中的 SHR)。因此,立即结果是 0 或 1,求反之后s0-1

通过轮班前后的两次无分支否定,我们得到((s^s+x) >> n) + s ^ s。这将执行除法并将结果四舍五入到零(例如-5>>1 = -2)。然而,算术右移(汇编中的 SAR)会降低结果(即-5>>1 = -3)。为了实现这一行为,必须放弃该+s操作。

演示如下: https: //godbolt.orghttps://onlinegdb.com/Hymres0y8

PS:我到达这里是因为 gnuplot 只有逻辑转换。


Rak*_*ish -1

我在使用中没有看到任何主要问题>>,但如果你想做算术右移,那么你可以将数字除以幂2x其中x是你想要做的右移量,因为将数字除以二是等效的到一个右移。

假设您想做a >> x。那么通过做也可以实现a / (int)pow(2,x)pow(2,x)是数学幂,或者您也可以将其视为2x

  • “pow”实际上是浮动的,对于这样一个简单的任务来说成本相当高。它很可能也不会像左移那样被编译器针对常量 x 进行优化。 (4认同)