负数的比特表示法

6

这是关于有符号整数位表示的疑问。例如,当您想要表示-1时,它等同于(+1)的二进制补码。因此,-1表示为0xFFFFFFF。现在,当我将我的数字向左移31位并打印结果时,它返回的值是-1。

signed int a = -1;
printf(("The number is %d ",(a>>31));//this prints as -1

请问有人可以解释一下负数在比特中如何表示吗?

谢谢。


从另一个角度来看:将任何N位(符号扩展)右移视为除以2^N,向下舍入(朝负无穷大而不是朝0)。因此,-1进行带符号扩展的右移任意次数都将继续产生-1。 - vladr
6个回答

10

当最高位为0时,这个数是正数。当最高位为1时,这个数是负数。

右移负数时,会在最高位继续保留“1”,以保持数字的负性。这就是为什么你得到那样的答案。

关于二进制补码的更多信息,请参见这个Stackoverflow问题


@Stobor指出,一些C编译器可能把0替换成最高位,而不是1。[在维基百科上验证过。]在Java中,它始终是算术移位。

但是,提问者给出的输出表明他的编译器正在进行算术移位。


但为什么它应该打印为-1,它应该打印为1,因为最上面的but是1,表示一个负数。 - Mjack
1
@Nosredna:你还应该提到,使用符号位填充的行为是与实现相关的。 - Stobor
但是,由于它通常被传递给处理器的移位操作码,它取决于您使用的CPU类型... x86处理器的SAR操作码具有此处描述的行为。请参见http://siyobik.info/index.php?module=x86&id=285。 - Stobor
LSR和ASR对于无符号数来说是相同的,因此选择是任意的。只有有符号数的情况才是重要的,对吗? - Nosredna
1
@Nosredna:CPU寄存器不知道有符号和无符号的区别——如果你将寄存器中的位解释为无符号数,则LSR执行“你所期望的”操作,如果你将其解释为有符号数,则ASR执行“你所期望的”操作。 - Stobor
显示剩余7条评论

8

C标准没有明确规定对负数(必然是有符号的)进行右移操作时是否将零位(逻辑右移)或者符号位(算术右移)移入最高位。这由具体实现决定。

因此,可移植的代码应确保不对负数进行右移操作。要么在移位之前将其转换为相应的无符号值(这可以保证使用逻辑右移,将零填充到空出的位),要么确保该值为正数,要么容忍输出结果的差异。


在移位之后,它可能会在最高位(或几个最高位)上进行“或”运算。 - Nosredna
非常感谢您提供的所有答案。我猜我还没有完全澄清我的疑问。那么这是编译器还是硬件特定的问题? - Mjack
2
@Maddy:这取决于硬件和编译器。例如,如果某个CPU只有逻辑右移(LSR)而没有算术右移(ASR),那么针对该机器的编译器将使用LSR而不是不存在的ASR。这还取决于编译器;即使一台机器同时具有ASR和LSR,编译器的编写者可能会决定对于所有有符号和无符号数量都使用LSR,原因有很多。这可能更快;它可能与同样运行该编译器的其他平台保持一致;也可能是编写编译器的人不喜欢ASR。 - Jonathan Leffler

1
这是一个算术移位操作,它保留符号位并移动有符号数的尾数部分。
干杯

1

基本上有两种类型的右移操作。一种是无符号右移,另一种是有符号右移。无符号右移将位向右移动,导致最低有效位丢失,最高有效位被替换为0。有符号右移将位向右移动,导致最低有效位丢失,最高有效位被保留。有符号右移通过2的幂(对应于移动的位数)来除以数字,而无符号移位是一个逻辑移位操作。

">>" 运算符在它所操作的数据类型为无符号时执行无符号右移操作,在它所操作的数据类型为有符号时执行有符号右移操作。因此,您需要在执行位操作之前将对象转换为无符号整数类型,以获得所需的结果。


对于有符号整数来说,并不一定是“有符号移位”(尽管通常是这样做的)- 这取决于编译器和/或硬件。 - Jonathan Leffler
是的,没错。我已经在良好表现的笔记本电脑/台式机/服务器上编程很长时间了,这些机器运行着某种形式的x86并使用GCC进行编译,以至于往往很容易忘记当前应用程序合理的假设和标准实际上保证的差异。 - Michael Aaron Safyan

0

编辑: 下面的内容是在问题中代码被写成如下时编写的:

unsigned int a = -1;
printf(("The number is %d ",(a>>31));//this prints as -1

如果无符号整数至少有32位宽度,则编译器不允许将-1作为该变量的输出结果(稍微注意一下,在将无符号值传递给printf之前,应将其转换为int类型)。
由于a是无符号整数,将-1分配给它必须赋予它UINT_MAX的值(作为模UINT_MAX+1下与-1同余的最小非负值)。只要您的平台上的无符号整数具有至少32位,那么将该无符号数量向右移31位的结果将是UINT_MAX除以2^31,这个值必须适合int范围内。(如果无符号整数是31位或更短,则可以产生任何喜欢的结果,因为移位的结果是未指定的)。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接