我将-109右移5位,并且我期望-3,因为-109 = -1101101(二进制)右移5位-1101101 >> 5 = -11(二进制)= -3

但是,我得到的是-4。 有人可以解释怎么了吗?

我使用的代码:

int16_t a = -109;
int16_t b = a >> 5;
printf("%d %d\n", a,b);

我在Linux上使用GCC,在osx上使用clang,结果相同。

===============>>#1 票数:4 已采纳

问题是您没有正确考虑负数表示。 右移时,移位的类型(算术或逻辑)取决于要移位的值的类型。 如果将值转换为无符号值,则可能会得到期望的结果:

int16_t b = ((unsigned int)a) >> 5;

您在示例中使用的是-109 (16位)。 109位为:

00000000 01101101

如果取109 2的补数,则得到:

11111111 10010011

然后,您将数字11111111 10010011右移5:

__int16_t a = -109;
__int16_t b = a >> 5; // arithmetic shifting
__int16_t c = ((__uint16_t)a) >> 5; // logical shifting
printf("%d %d %d\n", a,b,c);

将产生:

-109 -4 2044

===============>>#2 票数:2

右移负值的结果是实现定义的行为,来自C99草案标准第6.5.7节“ 按位移位运算符”5段,其中( 强调我的观点 ):

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

如果我们在Integers部分下查看gcc C实现定义的行为文档,它将说:

对有符号整数(C90 6.3,C99和C11 6.5)进行一些按位运算的结果。

按位运算符作用于包括符号位和值位的值的表示形式,其中符号位被认为紧邻最高值位。 带符号的“ >>”通过符号扩展作用于负数。

===============>>#3 票数:1

这很清楚发生了什么,当表示带符号整数时,负整数具有符号扩展属性,而最左边的有效位是符号位。

因此,您可以表示的最大数字为1000 ... 0000(32位),为32位。

因此,当您有一个负数并向右移动时,会发生一个称为符号扩展的事情,这意味着最左边的有效位被扩展了,简单来说,这意味着对于-109这样的数字,会发生这种情况:

移位之前,您需要(16bit):

1111 1111 1001 0011

然后,您将5位右移(在管道被丢弃的位之后):

1XXX X111 1111 1100 | 1 0011

X是出现在整数位表示形式中的新空格,由于符号扩展,它们用1填充,这将为您提供:

1111 1111 1111 1100 | 1 0011

因此,通过转换:-109 >> 5,您得到-4(1111 .... 1100),而不是-3。

用1的补码确认结果:

+3 = 0 ... 0000 0011

-3 =〜(0 ... 0000 0011)+ 1 = 1 ... 1111 1100 + 1 = 1 ... 1111 1101

+4 = 0 ... 0000 0100

-4 =〜(0 ... 0000 0100)+1 = 1 ... 1111 1011 +1 = 1 ... 1111 1100

注意:请记住,1的补码与2的补码一样,只是必须先对正数位求反,然后才对+1求和。

===============>>#4 票数:0

Pablo的回答本质上是正确的,但是有两个小片段(没有双关语!)可以帮助您了解发生了什么。

C(几乎与所有其他语言一样)使用所谓的二进制补码,这只是表示负数的一种不同方式(它用于避免以其他方式处理具有固定位数的二进制负数所带来的问题)。 有一个转换过程可以将正数转换为二进制补码(看起来就像二进制中的任何其他数字一样-除了最左边的最正位必须为正数为0;基本上是符号占位符)非常简单计算上:

拿你的电话

00000000 01101101(由于它是16位,所以在其左边填充0。如果它很长,则将填充更多的0,以此类推。)

翻转位

11111111 10010010

加一。

11111111 10010011。

这是Pablo所指的补码。 这就是C按位保持-109的方式。

当您将其逻辑右移五位时,您会出现

00000111 11111100。

此数字绝对不是-4。 (它的第一位没有1,所以它不是负数,而且太大了,不能达到4。)那么,为什么C给你负4?

原因基本上是C的ISO实现没有指定给定的编译器需要如何处理负数位移位。 GCC做所谓的符号扩展:想法是基本上用1s(如果在移位之前初始数字为负)或0s(如果初始数字在移位之前为正)填充左位。

因此,您将得到:而不是上述移位中发生的5个零。

1111111111111100。该数字实际上为负4! (这就是您一直得到的结果。)

要查看实际上是-4,您可以再次使用二进制补码方法将其转换回正数:

00000000 00000011(位翻转)00000000 00000100(加一)。

那是四个,所以您的原始号码(11111111 11111100)为-4。

  ask by smhx translate from so

未解决问题?本站智能推荐: