我将-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

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

1回复

无符号整数位域移位产生有符号整数

让我们考虑以下程序test.c : 使用gcc -Wsign-compare test.c编译时,会生成以下警告(使用gcc 4.8.1进行测试): clang -Wsign-compare test.c生成以下内容(使用clang 3.2进行测试): 因此,右操作数(移
4回复

C中的按位移位[重复]

这个问题在这里已有答案: 右移算子的奇怪行为(1 >> 32) 7答案 我得到了一些令我困惑的C代码: 输出是 代码在Ubuntu 16.04(Xenial Xerus)上运行,我使用GCC版本5.4.0的gcc -m32 ac编译它。
1回复

使用gcc进行逐位移位的意外行为

我有一个这样的测试程序: 它产生以下输出: 我期望(1)和(3)是相同的,以及(2)和(4)是相同的。 使用gcc版本:gcc.real(Ubuntu 4.4.1-4ubuntu9)4.4.1 怎么了?
4回复

有没有办法在gcc <4.4上进行128位移位?

当他们添加int128_t时, gcc 4.4似乎是第一个版本。 我需要使用位移,我已经用完了一些位字段。 编辑 :可能是因为我在32位计算机上,没有办法让它用于32位计算机(Intel Atom),是吗? 我不在乎它是否会产生棘手的慢速机器代码,如果我按照预期的工作位移。
1回复

使用typeof编译器扩展在C中实现min函数会产生错误

当我尝试在C中实现min函数时,因为C缺少此函数,编译器给出了错误。 我已经使用过像这样实现 C中的MIN和MAX 我的密码 快速参考: 产生错误:
4回复

为什么如果(0),switch(0)不会产生警告?

鉴于此代码: 我希望编译器会告诉我一些条件表达式是常量的 ( switch (0)而VS2012发出if(0) )或无法访问的代码的警告)( case 1 , case 2 )。 但是,无论是在ideone还是在Visual Studio 2012上,还是在最近的GCC上我都没有收
1回复

当出现符号时,为什么clang会产生链接问题?

我有一个奇怪的链接问题。 我有很多文件要尝试编译,但是遇到了未定义的符号错误。 下面是错误: 当我尝试将源文件编译为out可执行文件时,也会发生相同的情况。 如果查看那些目标文件(clang -c U_outsup.c U_OUTSUR.C),则会看到以下内容: 因此,我假
2回复

macOS和Linux下qsort()结果的无法解释的差异

我最近有一个学生在他们的比较器中写道: 在Ubuntu 16.04 LTS下,这没有问题,但是在macOS下运行时无法正确排序。 只需将>更改为-就足以使其在两个平台上均起作用。 那么,这是怎么回事? 我唯一能想到的是,不平等意味着-1值的损失,但是显然qsort() L
1回复

试图掌握C字节码... GNU / gcc是否可以产生像Clang / LLVM这样的C字节码?

最近我被告知要查看C函数如何编译成LLVM字节码,然后将LLVM字节码如何转换为x86 ASM。 作为常规的GNU / gcc用户,我对此有一些疑问。 说得客气一点。 GNU / gcc也编译成字节码吗? 它可以? 我的印象是gcc直接编译成ASM。 如果没有,有没有办法查看字节
3回复

对于给定的结构定义,clang和gcc是否产生相同的内存布局?

gcc和clang是否旨在为给定的结构定义生成相同的内存布局? 显然,C标准没有规定结构的精确内存布局,但是由于其他原因,gcc和clang仍可能产生相同的内存布局。 也许clang被明确设计为与gcc兼容。 也许两者都遵循其他一些标准,类似于名称修改和Itanium ABI的情况。