C语言 为什么在 C 中右移负数会在最左边的位上带来 1?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17358445/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 06:49:31  来源:igfitidea点击:

Why does right shifting negative numbers in C bring 1 on the left-most bits?

cbit-manipulationbit-shift

提问by Nikunj Banka

The book "C The Complete Reference" by Herbert Schildt says that "(In the case of a signed, negative integer, a right shift will cause a 1 to be brought in so that the sign bit is preserved.)"

Herbert Schildt 所著的“C The Complete Reference”一书说:“(在有符号的负整数的情况下,右移将导致引入 1,以便保留符号位。)”

What's the point of preserving the sign bit?

保留符号位有什么意义?

Moreover, I think that the book is referring to the case when negative numbers are represented using a sign bit and not using two's complement. But still even in that case the reasoning doesn't seem to make any sense.

此外,我认为这本书指的是使用符号位而不是使用二进制补码来表示负数的情况。但即使在那种情况下,推理似乎也没有任何意义。

回答by caf

The Schildt book is widely acknowledged to be exceptionally poor.

希尔特的书被广泛认为是非常糟糕的。

In fact, C doesn'tguarantee that a 1 will be shifted in when you right-shift a negative signed number; the result of right-shifting a negative value is implementation-defined.

事实上,当你右移一个负符号数时,C并不能保证 1 会被移入;右移负值的结果是实现定义的。

However, if right-shift of a negative number isdefined to shift in 1s to the highest bit positions, then on a 2s complement representation it will behave as an arithmetic shift- the result of right-shifting by N will be the same as dividing by 2N, rounding toward negative infinity.

然而,如果负数的右移位定义在1s至移位到最高位的位置,然后在一个2的补码表示将表现为一个算术移位-的结果右移N值将是相同的分频按 2 N,向负无穷大舍入。

回答by Jonathan Leffler

The statement is sweeping and inaccurate, like many a statement by Mr Schildt. Many people recommend throwing his books away. (Amongst other places, see The Annotated Annotated C Standard, and ACCU Reviews— do an author search on Schildt; see also the Definitive List of C Bookson Stack Overflow).

就像希尔特先生的许多声明一样,该声明笼统且不准确。许多人建议扔掉他的书。(在其他地方,请参阅The Annotated Annotated C StandardACCU Reviews— 在 Schildt 上进行作者搜索;另请参阅Stack Overflow 上的 C 书籍最终列表)。

It is implementation defined whether right shifting a negative (necessarily signed) integer shifts zeros or ones into the high order bits. The underlying CPUs (for instance, ARM; see also this class) often have two different underlying instructions — ASR or arithmetic shift right and LSR or logical shift right, of which ASR preserves the sign bit and LSR does not. The compiler writer is allowed to choose either, and may do so for reasons of compatibility, speed or whimsy.

实现定义是否右移负(必须带符号)整数将零或一移入高位。底层的CPU(例如,ARM;也参见此)通常具有两种不同的底层指令- ASR或算术右移和LSR或逻辑右移,其中ASR保留符号位和LSR没有。编译器编写者可以选择其中之一,并且可能出于兼容性、速度或奇思妙想的原因而这样做。

ISO/IEC 9899:2011 §6.5.7 Bitwise shift operators

?5 The result of E1 >> E2is E1right-shifted E2bit positions. If E1has an unsigned type or if E1has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2. If E1has a signed type and a negative value, the resulting value is implementation-defined.

ISO/IEC 9899:2011 §6.5.7 位移运算符

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

回答by Jonathon Reinhart

The point is that the C >>(right shift) operator preserves1the sign for a (signed) int.

关键是 C >>(右移)运算符将 a (signed) 的符号保留为1int

For example:

例如:

int main() {
  int a;
  unsigned int b;

  a = -8;
  printf("%d (0x%X) >> 1 = %d (0x%X)\n", a, a, a>>1, a>>1);

  b = 0xFFEEDDCC;
  printf("%d (0x%X) >> 1 = %d (0x%X)\n", b, b, b>>1, b>>1);

  return 0;
}

Output:

输出:

-8 (0xFFFFFFF8) >> 1 = -4 (0xFFFFFFFC)                    [sign preserved, LSB=1]
-1122868 (0xFFEEDDCC) >> 1 = 2146922214 (0x7FF76EE6)      [MSB = 0]

If it didn't preserve the sign, the result would make absolutely no sense. You would take a small negative number, and by shifting right one (dividing by two), you would end up with a large positive number instead.

如果它不保留符号,结果将完全没有意义。你会取一个小的负数,通过右移一个(除以二),你最终会得到一个大的正数。

1 - This is implementation-defined, but from my experience, most compilers choose an arithmetic (sign-preserving) shift instruction.

1 - 这是实现定义的,但根据我的经验,大多数编译器选择算术(符号保留)移位指令。

回答by Lundin

In the case of a signed, negative integer, a right shift will cause a 1 to be brought in so that the sign bit is preserved

在有符号负整数的情况下,右移将导致引入 1,以便保留符号位

Not necessarily. See the C standard C11 6.5.7:

不必要。参见 C 标准 C11 6.5.7:

The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.

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

This means that the compiler is free to shift in whatever it likes (0 or 1), as long as it documents it.

这意味着编译器可以自由地转换它喜欢的任何东西(0 或 1),只要它记录它。