在Java中,我试图通过除以2而不是右移(>>1)来将整数的位右移一位,并且遇到了这个问题,即-1(负数)除以2时无法正确移动。有人能解释一下为什么负数除以2不起作用吗?
我尝试过使用正数并且它们有效,但我不明白为什么除以 2 不适用于负数
计算机是二进制的。例如,1 和 0 是他们唯一可以写的字母。这实际上是“唯一的”——因此,负数“不可能存在”。或者至少,减号是不存在的。这并不是说计算机可以秘密地表示 3 个东西(一、零和减号)。这只是 1 和 0。仅此而已。
这意味着您需要一个表示负数的计划。没有明显的答案(假设 -
在最低级别不存在)。
在大多数计算机系统中,java 在规范中“硬编码”此行为,所选择的解决方案称为 2 的补码。
2 的补码通过切换每一位(所有 1 变为 0,所有 0 变为 1)产生任何给定数字的负版本,然后将 1 添加到最终结果。这听起来很奇怪,但是它有许多非常有用的属性:
与使用单独的“符号”位(0 = 正,1 = 负)或只是翻转所有位而不在末尾添加 1 相比,2 的补码只有 1 表示 0。也就是说,- 0 与+0 相同。我们来试试:0 位表示为
0000 0000
1111 1111
,然后添加一个:
1 0000 0000
,但前 1 位会消失,因为计算机忽略任何溢出,所以我们回到开始的地方:0000 0000
。这是好事。 0 也是唯一一个具有等于自身负数的性质的数字。
-1 变为全 1 位。 (1 是 0000 0001
1111 1110
,然后添加 1:
1111 1111
)。这有一定的优势。
第一位表示符号。如果第一位是 1,则该数字必然为负数。那就太好了。
可以表示的负数比正数多 1 个。这很奇怪,也不是一件好事,但这是将 0(从技术上讲既不是正数也不是负数)与正数分组的自然结果。零“吃掉”一格的积极因素。
2 的补码的
关键输入是带符号还是无符号的2的补码。因为公式是一样的。假设我有 2 个无符号字节,我想将 200 和 50 加在一起。这是以位 1100 1000 + 0011 0010
为单位的,即 1111 1010
。将其转换回十进制,结果是 250。这是预期的答案。然而,实际上,这些是 2 的补码有符号数,所以,
0011 0010
仍然是 50,但 1100 1000
不是:那就是(翻转所有位并加 1):负 0011 1000
是 -56。 -56 + 50 是 -6,2 的补码是 0000 0110
(即 6,现在翻转所有位并加 1): 1111 1010
这是...哇,这与我们处理2 个数字作为无符号正数。
太棒了。这就是 2 的补码的原因。
用整数来思考,这是完全有道理的:负一除以 2 当然是负二分之一。 -1 的二进制表示是
1111 1111
。当你右移一位时(就像除以 2 得到正数),你最终会得到 0111 1111
“除以 2 与右移 1 相同”根本不适用于负数(当然不适用于 2 的补码中的负数)。