正负数的位运算符

8
    -5 / 2 = -2

    -5 >> 1 = -3

我从我的老师那里学到, >>1 通过2来除以一个数。这适用于正数但不适用于负数。有人可以解释一下吗?

谢谢


这取决于具体的实现方式。但通常会向下取整(朝负无穷方向)。 - Mysticial
1
这是在C语言中的移位运算符的可能重复吗? - moooeeeep
这完全取决于数字在二进制中的表示方式,听说过补码吗?可能就是这样。http://zh.wikipedia.org/wiki/补码 - Jodrell
移位运算符将所有位向左或向右移动,因为二进制表示是基于2的,每个位代表一个连续的2的幂。 - Jodrell
你的老师告诉了你正确的事情,但是你误解了它。再次理解一下,只需进行位运算即可。 - rajesh6115
7个回答

6
作为BЈовић和神秘状态所说,对负数使用位移运算符是实现定义的。这是因为C语言不区分逻辑位移和算术位移。(算术补齐最高有效位,逻辑补齐0)对于正数来说,这并不重要,因为无论是算术位移还是逻辑位移,都会将最高有效位保留为0:
算术 5>>1 0000 0000 0000 0101 = 5 变成 0000 0000 0000 0010 = 2
逻辑 5>>1 0000 0000 0000 0101 = 5 变成 0000 0000 0000 0010 = 2
然而,对于负数(2's comp),情况就不同了:
算术 -5>>1 1111 1111 1111 1011 = -5 变成 1111 1111 1111 1101 = -3
逻辑 -5>>1 1111 1111 1111 1011 = -5 变成 0111 1111 1111 1101 = 32,765
或者至少,这是我理解的。

我不了解数学和逻辑,但在C语言中,每当您使用有符号整数时,一个东西就会出现,那就是符号位复制。这是每个整数的最高有效位。 - rajesh6115
@Davoyzuk 为什么我得到的是-3而不是32765? - Computernerd
@Lim 抱歉,我的算术 -5>>1 是错误的。1111 1111 1111 1101 是-3,而不是-2。你在原问题中得到的结果是算术位移。逻辑位移是为什么你不应该在负数上使用 >> 位移的原因。我的帖子已经被编辑以修复错误。 - Daboyzuk

5
它适用于正数,但不适用于负数。
在负整数上使用位移运算符是实现定义的。
[expr.shift]/3 告诉我们:
E1 >> E2 的值是将 E1 向右移动 E2 位。如果 E1 有无符号类型或者 E1 有带符号类型并且非负值,则结果的值是 E1/2E2 商的整数部分。如果 E1 有带符号类型并且有负值,则结果的值是实现定义的。

2
错误,它是实现定义的。不是未定义的。 - Mysticial
为什么这是未定义行为? - Computernerd
实现定义行为和未定义行为有什么区别? - Computernerd
1
@Lim Undefined 意味着任何事情都可以发生。实现定义意味着编译器必须选择一种一致的行为。 - Mysticial
@BЈовић 这段话与问题(<< vs >>)无关。 - Luchian Grigore
发布的摘录与左移相关。右移是什么。E1 >> E2的结果是将E1向右移动E2位。如果E1具有无符号类型或者E1具有带符号类型和非负值,则结果的值为E1 / 2E2商的整数部分。如果E1具有带符号类型和负值,则结果的值是实现定义的。是与右移相关的部分。 - P.P

1
首先,
5的二进制表示为0000 0000 0000 0101, 那么-5呢?它的二进制表示如下:
  1. 将1变为0,0变为1,得到1111 1111 1111 1010
  2. 然后将该数字加1,得到1111 1111 1111 1011

现在我们得到了:-5= 1111 1111 1111 1011(以二进制补码形式表示)
因此,以下是如何计算-5>>1:

  1. 将-5的每个位从左到右移动( >> ),我们得到111 1111 1111 1101(只剩下15位)。
  2. 因为-5是负数,所以我们必须在第一位填充“1”以使其成为16位。然后我们得到1111 1111 1111 1101(它仍处于2的补码形式)。
  3. 现在通过将0变为1,1变为0(除了第一位,因为它定义了2的补码中的负数),然后加上“1”来将其转换为正常的二进制形式。所以我们得到1000 0000 0000 0011 = -3。

1
我从老师那里学到, >>1 可以将数字除以2。
它不是将整数除以二,而是根据值执行(根据情况)一个 逻辑算术右移 一位操作。在某些情况下,它恰好相当于除以二的运算。
它对正数有效,但对负数无效。它的确会在两种情况下工作,但其确切行为并非由标准规定,而是由实现定义。通常它会将结果除以二并向负无穷截断,与普通的除法向零的方式不同。
供参考:

0

我猜-5>>1的答案是-3。

对于正数,比如5,除以2得到2.5,四舍五入到最接近的小整数即为2。

但是当我们考虑负数-5时,除以2得到-2.5。将其四舍五入到最接近的整数即为-3。


0
在 C 语言中,右移操作符会保留符号位。因此,将带符号的位向右移动并再次保留符号位会得到一个负数,该负数以二进制补码形式表示。

这并不是100%正确的,编译器可以自由选择算术运算(保留符号位)或逻辑运算(不保留符号位),只要它选择一种并坚持使用;有符号类型的右移是实现定义的。 - Dennis Meng

0

我认为答案是正确的。因为“/”(除法)运算符会生成商(除法的结果)。

在你的问题中:

-5/2 = -3(quotient) and 1(remainder ). 

所以这对于正数和负数都可以。

正数:

5/2 = 2(quotient) and 1(remainder ). 

所以对于正数来说是没问题的。

注意

余数永远不会是负数。它总是正数。


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