为什么对于带符号数,使用二进制补码比符号-大小更好?

238

我只是好奇为什么要使用二进制的补码来表示-1:翻转位并加上1?

-1用11111111(二进制补码)表示,而不是(对我来说更直观的)10000001,这是二进制1,第一个位作为负标志。

免责声明:我不依靠二进制算术来工作!


7
就您提到的内容而言,“直觉”方法使用符号位在某些情况下是常见的,例如,大多数计算机在表示浮点数时使用符号位。 - Adisak
2
@Adisak 这被称为有符号数。 - Cole Tobin
2
我一直将符号-大小表示法与整数联系在一起,因为浮点数包含三个组成部分:符号、指数和尾数(通常带有一个隐式的“1”)。但我想只要意识到它们不是严格线性的,就足够容易将指数和尾数视为大小。 - Adisak
这里有一篇文章讨论了浮点数如何以二进制形式存储,供那些对@Adisak的评论感到好奇的人参考。文章链接为:http://kipirvine.com/asm/workbook/floating_tut.htm - GDP2
刚刚看了一个很好的视频,解释了这个问题。 https://www.youtube.com/watch?v=dHB7jFjESLY - allenlinli
19个回答

2
有三种常见的表示负数的方法,如下所示:
- 符号位 - 1的补码 - 2的补码
为了理解为什么2的补码优于其他两种方法,让我们看看其他方法存在的限制。
使用符号位时的问题:
- 在符号位表示中,首位(最左边的位)决定数字是负数还是正数。如果首位是1,则表示负数;如果是0,则表示正数。 - 这种方法的问题是会出现两个零的表示,即负零和正零。这将减少范围并在算术运算中造成混淆。
使用1的补码时的问题:
- 在1的补码中,负数通过反转其正数表示的位来表示。 - 问题与上述相同,会出现两个零的表示,即负零和正零。这将减少范围并在算术运算中造成混淆。
2的补码如何解决这个问题:
2的补码意味着负数通过颠倒其正数表示的位,并加1来表示。因此,使用2的补码只有一种零的表示注意:在计算2的补码时,任何末尾的进位位都会被丢弃。
由于2的补码形式是从0-x(其中x是数字的正形式)的思想派生出来的,所以算术运算更容易执行
负数的最高位始终为1,正数的最高位始终为0。

1
通过补码方法进行减法的优势在于硬件复杂度的降低。不需要为加法和减法使用不同的数字电路,加法和减法都只需使用加法器来执行。

1
值得注意的是,在数字计算机出现之前的一些早期加法机上,减法是通过操作员使用每个键上不同颜色的图例输入值(因此每个键将输入九减去要减去的数),并按下一个特殊按钮来执行进位计算。因此,在一个六位数字的机器上,要从一个值中减去1234,操作员将按下通常表示“998,765”的键,并按下一个按钮将该值加一加到正在进行的计算中。二进制补码算术就是那种早期的“十进制补码”算术的二进制等效形式。

1
我有一个小补充,在某些情况下很重要:在这些限制条件下,二进制补码是唯一可能的表示方法:
  • 无符号数和二进制补码是带单位元的可交换环。它们之间存在同态。
  • 它们共享相同的表示方式,对于负数有不同的分支切割(因此加法和乘法在它们之间是相同的)。
  • 高位确定符号。
为了理解原因,可以将基数降低;例如,Z_4。

Two different Z_4 representations.

“符号加绝对值法”和“反码法”都不能形成具有相同元素数量的环,其中一个症状就是双零。因此,在边缘处很难处理;为了数学上的一致性,需要检查溢出或陷阱表示法。

0

不同类型的表示法有:

  1. 无符号数表示法
  2. 带符号数表示法
  3. 反码表示法
  4. 补码表示法

- 无符号数表示法用于表示正数。

- 带符号数表示法用于表示正数和负数。在带符号数表示法中,MSB位表示符号位,其余位表示数字。当MSB为0时,表示数字为正;当MSB为1时,表示数字为负。

带符号数表示法的问题在于0有两个值。

反码表示法的问题在于0有两个值。

但是,如果我们使用补码表示法,则只有一个值为0,这就是为什么我们使用补码形式表示负数的原因。

来源:为什么负数以二进制补码的形式存储


0

好的,你的意图并不是要反转二进制数的所有位。实际上,你想要做的是从1中减去每个数字。很幸运,从1中减去1的结果是0,从1中减去0的结果是1。因此,翻转比特实际上就是进行这种减法。

但是,为什么你要计算每个数字与1之间的差异呢?实际上并不是这样的。你真正的意图是计算给定的二进制数与另一个二进制数之间的差异,两个二进制数具有相同的位数,但只包含1。例如,如果你的数字是10110001,当你翻转所有这些位时,你实际上是在计算(11111111 - 10110001)。

这解释了计算二进制补码的第一步。现在让我们把第二步——加1——也纳入考虑。

将上述二进制方程式加1:

11111111 - 10110001 + 1

你会得到什么?这个:

100000000 - 10110001

这就是最终的方程式。通过执行这两个步骤,你试图找到这个最终差异:将二进制数从另一个具有一个额外位数且只在最高有效位位置包含零的二进制数中减去。

但我们为什么真的渴望这种差异呢?嗯,从现在开始,我想如果你阅读维基百科文章会更好。


0
两补数表示法的一个主要优点是,尚未在此提到的是,两补数的和、差或积的低位仅取决于相应操作数的对应位。之所以8位有符号值为-1的原因是,从任何最低8位为00000001的整数中减去任何其他最低8位为0000000的整数将产生一个最低8位为11111111的整数。在数学上,值-1将是一个无限长的1字符串,但特定整数类型范围内的所有值都将在某个点过后成为全1或全0,因此计算机方便地将数字的最高有效位作为代表无限数量的1或0进行“符号扩展”。

二进制补码是处理大于二进制机器自然字长的类型时唯一有效的带符号数表示法,因为在执行加法或减法时,代码可以获取每个操作数的最低块,计算结果的最低块,并存储它,然后加载每个操作数的下一个块,计算结果的下一个块,并存储它,以此类推。因此,即使处理器要求所有加法和减法都通过单个8位寄存器进行,也可以相对高效地处理32位带符号数(当然比使用32位寄存器慢,但仍可行)。

当使用C标准允许的任何其他带符号表示时,结果的每个位都可能受到操作数的任何位的影响,这使得必须将整个值一次性保存在寄存器中,否则在计算后需要跟随额外的步骤,在至少某些情况下需要读取、修改和重写结果的每个块。


@SurajJain:这样好一些吗? - supercat
是的,比之前好多了。我想问你一个问题,signed char a = 1 和 unsigned char a = 1 有什么区别,它们在内存中如何表示? - Suraj Jain
@SurajJain:在二进制补码系统中,“char”比“int”小(即绝大多数系统),有符号和无符号的char类型行为相同,唯一的区别是读取时有符号类型会进行符号扩展,而无符号类型不会。在这样的系统上,将值194或-62存储到有符号char中将写入与将194或-62存储到无符号char中相同的位模式(即11000010)。从有符号char中读取该位模式将得到-62,而从无符号char中读取将得到194。 - supercat
符号扩展是什么意思? - Suraj Jain
@SurajJain:二进制补码适用于小于零或超出整数类型正数范围的值。在有符号整数类型范围内的非负值将简单地作为数字处理。 - supercat
显示剩余6条评论

0

对于加法和减法,我们仅执行加法操作。 对于加法,我们将第二个操作数添加到第一个操作数。 对于减法,我们将第二个操作数的2的补码添加到第一个操作数。

使用2的补码表示法,我们不需要单独的数字组件来进行减法运算-仅使用加法器和补码器即可。


-1

实际上,如果您有一个小数点并明确说明所有位:“0..0000.1111..1”表示所有最左边未指定的位都为0,而所有最右边未指定的位都为1,因此“..1”表示触发了进位。 因此,它是(机械地)“0.0001.0000..0”。这意味着“1..1111.1111..1”等于零! 这也意味着要否定一个整数,您只需翻转其位。 但现在它适用于可表示的分数。 - Rob

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