二进制除法移位相减_二进制 - C中的移位运算符(<<,>>)是算术还是逻辑?

TL; DR

考虑>>>和>>分别是移位运算符的左右操作数; <

| Direction | Type | Value (i) | Result |

| ---------- | -------- | --------- | ------------------------ |

| Right (>>) | unsigned | ≥ 0 | −∞ ← (i ÷ 2ⁿ) |

| Right | signed | ≥ 0 | −∞ ← (i ÷ 2ⁿ) |

| Right | signed | < 0 | Implementation-defined† |

| Left (<

| Left | signed | ≥ 0 | (i * 2ⁿ) ‡ |

| Left | signed | < 0 | Undefined |

†大多数编译器将其实现为算术移位

‡如果值溢出结果类型T,则为undefined; 推广类型的我

首先是从数学角度看逻辑和算术转换之间的差异,而不必担心数据类型的大小。 逻辑移位总是用零填充丢弃的位,而算术移位仅用左移位用零填充它,但是对于右移,它复制MSB从而保留操作数的符号(假设对负值进行二进制补码编码)。

换句话说,逻辑移位将移位的操作数视为一个比特流并移动它们,而不必担心结果值的符号。 算术移位将其视为(带符号)数字,并在移位时保留符号。

X乘以n的左算术移位相当于将X乘以2n,因此相当于逻辑左移; 逻辑转变也会产生相同的结果,因为MSB无论如何都会失败,而且没有什么可以保留的。

如果X是非负的,则数字X乘以n的正确算术移位相当于X的整数除以2n! 整数除法不过是数学除法,而是向0(截断)舍入。

对于由二进制补码编码表示的负数,右移n位具有数学上将其除以2n并向-∞(floor)舍入的效果; 因此,对于非负值和负值,右移是不同的。

对于X≥0,X>&gt; n = X / 2n = trunc(X÷2n)

对于X&lt; 0,X&gt;&gt; n =楼层(X÷2n)

其中>>>是数学除法,>>是整数除法。 我们来看一个例子:

37)10 = 100101)2

37÷2 = 18.5

37/2 = 18(向18.5舍入为0)= 10010)2 [算术右移的结果]

-37)10 = 11011011)2(考虑二进制补码,8位表示)

-37÷2 = -18.5

-37 / 2 = -18(向18.5舍入为0)= 11101110)2 [不是算术右移的结果]

-37&gt;&gt; 1 = -19(舍入18.5朝向-∞)= 11101101)2 [算术右移的结果]

正如Guy Steele指出的那样,这种差异导致了多个编译器中的错误。 这里非负(数学)可以映射到无符号和有符号的非负值(C); 两者都被处理相同,右移它们是通过整数除法完成的。

所以逻辑和算术在左移和右移的非负值中是等价的; 它正在改变它们不同的负值。

操作数和结果类型

标准C99§6.5.7:

每个操作数应具有整数类型。

对每个操作数执行整数提升。 结果的类型是提升的左操作数的类型。 如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

short E1 = 1, E2 = 3;

int R = E1 << E2;

在上面的代码片段中,两个操作数都变为>>>(由于整数提升); 如果>>为负或<

左移

E1&lt;&lt;&lt;&lt; E2是E1左移E2位位置; 腾出的位用零填充。 如果E1具有无符号类型,则结果的值为E1×2E2,比结果类型中可表示的最大值减少一个模数。 如果E1具有带符号类型和非负值,并且E1×2E2在结果类型中可表示,那么这就是结果值; 否则,行为未定义。

由于左移对于两者都是相同的,所以空出的位简单地用零填充。 然后它指出,对于无符号和有符号类型,它都是算术移位。 我将其解释为算术移位,因为逻辑移位不必担心由比特表示的值,它只是将其视为比特流; 但是标准不是根据比特来说,而是根据E1与2E2的乘积得到的值来定义。

这里需要注意的是,对于有符号的类型,该值应该是非负的,并且结果值应该在结果类型中可表示。 否则操作未定义。 结果类型将是应用整数提升后的E1类型,而不是目标(将保留结果的变量)类型。 结果值隐式转换为目标类型; 如果它在该类型中不可表示,则转换是实现定义的(C99§6.3.1.3/ 3)。

如果E1是具有负值的带符号类型,则左移位的行为是未定义的。 这是一个容易被忽略的未定义行为的简单途径。

右转

E1&gt;的结果&gt; E2是E1右移E2位位置。 如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是E1 / 2E2的商的整数部分。 如果E1具有带符号类型和负值,则结果值是实现定义的。

无符号和有符号非负值的右移非常简单; 空位用零填充。 对于有符号的负值,右移的结果是实现定义的。 也就是说,像GCC和Visual C ++这样的大多数实现都通过保留符号位来实现右移作为算术移位。

结论

与Java不同,Java有一个特殊的运算符>>>用于逻辑移位,除了通常的>>和<

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值