我试图理解有符号运算符在有符号和无符号类型上的行为。根据ISO / IEC文件,以下是我的理解。
E1 << E2
的结果是E1左移E2位位置
左移帐户中腾出的位将由零填充。
E1已签名为非否定: E1 << E2
将导致E1乘以E2的2次幂,如果该值可由结果类型表示。
Q1:签署否定项怎么样?
Q2:在以下情况下,我无法理解“减少模数”的含义。 “如果E1具有无符号类型,则结果的值为E1×2E2,模数减少 比结果类型“。
E1 >> E2
的结果是E1右移E2位位置。
E1签名为非负/无符号:结果的值是E1 / 2E2商的不可分割部分
问题3:对于有符号的负整数,我看到,有些书定义了空位将用1
填充。请详细说明使用右移运算符对有符号的负整数。
答案 0 :(得分:8)
Q1:左移位运算符对有符号整数类型的负值的行为是未定义的,当结果E1 * 2^E2
在类型中无法表示时,有符号整数类型的正值的行为也是如此。
标准(n1570草案)第6.5.7节第4和第5段明确提到:
4
E1 << E2
的结果是E1
左移E2
位位置;腾出的位用零填充。如果E1
具有无符号类型,则结果的值为E1 × 2^E2
,减去模数比结果类型中可表示的最大值减1。如果E1
具有签名类型和非负值,则和E1 × 2^E2
可在结果类型中表示,那么这就是结果值;否则,行为未定义。
Q2:减少模数比无符号整数类型中可表示的最大值多一个意味着左边移出的位被简单地丢弃。
在数学上,如果无符号类型的最大值是2^n - 1
(并且它始终是那种形式),则将E1
左移E2
位的结果是值{ {1}}范围从0到V
,以便差异
2^n - 1
可以被(E1 * 2^E2 - V)
整除,也就是说,它是将2^n
除以E1 * 2^E2
时得到的余数。
问题3:在转换有符号整数类型的正负值时的行为是实现定义的。最常见的行为(至少在两个补码机器上)是算术移位,也就是说,结果是向下舍入(朝向负无穷大)。
5
2^n
的结果是E1 >> E2
右移E1
位位置。如果E2
具有无符号类型或E1
具有有符号类型和非负值,则结果的值是E1
的商的不可分割部分。 如果E1 / 2^E2
具有签名类型和负值,则结果值是实现定义的。
答案 1 :(得分:5)
回复:Q1
如果E1为负数,则行为未定义。
回复:Q2
无符号算术是“循环的”,也就是说,它是环绕的,因此UINT_MAX + 1
再次为0
。就好像每个计算都是以模UINT_MAX + 1完成的。考虑它的另一种方法是简单地删除左边不适合的多余位。
回复:Q3
如果E1为负数,则结果为实现定义。也就是说,它取决于您的机器/编译器/选项,但必须在某处记录(“定义”)行为,通常是编译器手册。两种流行的选择是用左(1)算术移位或0(逻辑移位)填充左边的输入位。
答案 2 :(得分:2)
如果你真的想了解按位移位运算符。看看这些简单的规则:
1)在左移,E1&lt;&lt; E2,右侧的所有空位都将用零填充,无论数字是有符号还是无符号都无关紧要,总是将零移入。
2)在左移,E1>&gt; E2,左侧的所有空位,如果数字为正,则为0;如果数字为负,则为1。请记住,无符号数永远不会为负数。一些实现也可能在某些机器上用0填充,即使数字是负数,所以永远不要依赖于此。
所有其他方案都可以通过这两个简单的规则来解释。 现在,如果您想知道移位后结果的值,只需写入数字的位表示并在纸上手动移位,并使用这两个规则在空位处输入位。然后你就能更好地理解位移的工作原理。
For example lets take int i = 7;
i<<2
now i = 0000 0000 0000 0000 0000 0000 0000 0111
perform two left shits according to rule 1 would be:
0000 0000 0000 0000 0000 0000 0001 1100
,它的值为28(E1 * 2E2 = 7 * 2 * 2 = 28),与位模式表示的相同。
现在让我解释“减少模数”的事情,如果你移动一个大数字,那么左边的位将会丢失,“减少模数”补偿它,所以如果你的结果值大于数据类型可以容纳的最大值,左边的位将丢失,然后: result =(E1 * 2 * E2)%(maximumValue + 1)。
对于其他各种情况,请记住以上规则,并且你很好:)
答案 3 :(得分:1)
Q2:“减少模数X”是指数学中的“值mod X”,可以写成C中的“value%X”。这部分只是解释整数溢出是如何工作的。