为什么Python可以在float中表示负零,而不能在int中表示负零?

进一步来说:

a = 0.0
print(a)
# 0.0

b = -a
print(b)
# -0.0


但:

a = 0
print(a)
# 0

b = -a
print(b)
# 0


(我知道这里negative zero in python关于负浮点零的讨论,但是int在那里并没有真正讨论过)。

最佳答案

从历史上看,有一些整数格式可以同时表示-0和+0。符号和幅值以及补码都可以表示−0和+0。事实证明,这些方法没有两个人的补语有用,后者赢得了人们的青睐,并且在今天无处不在。

二进制补码具有一些数值特性,使其在硬件中更易于实现,而具有两个零的值则对程序员造成了麻烦。 (我听说过这样的错误,例如帐户余额为-0而不是+0会导致某人在不应该收到帐单的情况下收到帐单。)

浮点使用符号和大小,因此它既可以表示−0,也可以表示+0。由于浮点数的性质,二进制补码的算术属性不会对浮点数实现有多大帮助,并且具有两个零的值使程序员在某些情况下可以使用一些额外的信息。

因此,整数和浮点格式的选择是由实用程序而非数学上的必要性所驱动的。

看整数算法

让我们考虑使用四个位在计算机硬件中实现一些整数运算。本质上,我们要做的第一件事是实现无符号二进制算术,因此我们设计了一些逻辑门来构成加法器和其他算术单元。因此,加法器的输入0101和0011产生输出1000。

接下来,我们要处理负数。在写作中,我们通过在前面加一个符号来处理负数,因此我们的第一个想法可能是对位进行相同的操作:在前面使用一位表示负数。现在我们有了一个符号和幅度表示。 0001表示+1,而1001表示-1。 0010表示+2,而1010表示-2。 0111代表+7,1111代表-7。并且,当然,0000表示+0,而1000表示-0。这是一个想法,然后我们必须执行它。我们已经有一个加法器,如果将其加到0010(2)和0011(3),它会正确输出0101(5)。但是,如果我们将其送入0011(3)和1001(-1),则输出1100(-4)。因此,我们必须对其进行修改。好吧,还算不错,我们有一个无符号二进制的减法单元,所以我们可以看一下第一位,如果我们要加上一个负数,我们要减去而不是加。这适用于某些操作;对于0011和1001,观察第二个操作数上的前导1并将011和001馈入减法单元,将得出010(2),这是正确的。但是,如果我们有0010和1011,则将010和011馈入减法单元可能会产生一些错误指示(它最初是为无符号二进制设计的),或者可能会“包装”并产生111(因为这样的包装以及“借用”输出”位,使减法单元作为用于减去较宽数字的设计的一部分工作。无论哪种方式,这对于我们的签名号码都是错误的;我们希望0010(2)加1011(−3)的输出为1001(−1)。因此,我们必须设计新的算术单元来处理此问题。也许,当增加数量的混合符号时,他们找出哪个值较大,从较大的值中减去较小的值,然后应用较大的符号位。无论如何,我们仅设计加减单元就要做大量工作。

另一个建议是,使数字为负,将每一位取反。这就是所谓的补语。这很容易理解并且符合否定的概念,只需否定一切即可。让我们考虑一下它如何影响我们的算术单位。对于+3或-3与+2或-2的组合,我们希望得到以下结果:0011(3)+ 0010(2)= 0101(5),0011(3)+ 1101(-2)= 0001( 1),1100(-3)+ 0010(2)= 1110(-1)和1100(-3)+ 1101(-2)= 1010(-5)。经过检查,有一种简单的方法可以使我们的二进制加法器适应这一要求:对所有四个位进行加法,就好像它们是无符号二进制一样;如果前导位有进位,则将其加回到低位。在无符号的无符号二进制0011 + 0010 = 0101中,因此最终输出为0101。带进位的0011 + 1101 = 0000,因此最终结果为0001。1100 + 0010 = 1110(无进位),因此最终结果为1110 1100 + 1101 = 1001(带进位),因此最终结果为1010。

很好我们的补码加法器比符号加法器简单。它不需要比较幅度,也不需要进行减法处理负数。我们可以使其更便宜并获得更多利润。

然后有人想到两个补码的想法。从概念上讲,我们不是从每一位取反,而是从2n中减去数字,其中n是位数。因此10000-0001 = 1111表示-1,1110是-2,1101是-3,依此类推。这对我们的加法器有什么作用?

在无符号二进制中,0010(2)+ 1101(13)= 1111(15)。用两个补数表示,即0010(2)+ 1101(−3)= 1111(−1)。这些位是相同的!这实际上适用于所有两个补码。添加无符号数字的位模式会产生与添加两个补码数字相同的结果。对于无符号二进制和二进制补码,我们可以使用完全相同的逻辑门。那太好了,给那个雇员加薪。这就是现代硬件所做的;相同的算术单位用于加或减两个补码数,而用于加或减无符号数。

这就是为什么两个数字的补码赢得代表负整数的很大一部分原因。这样可以使计算机更简单,更容易,更便宜,更快速,更高效。

(无符号加法和二进制补码加法之间有一个区别:如何检测溢出。在无符号加法中,如果高位进位,则发生溢出。在二进制补码加法中,如果有进位,则发生溢出加法器单元通常通过以一种或另一种形式报告两个指示来处理这一点,如果需要,该信息将在后面的指令中进行测试;它不会影响加法本身)

10-02 08:27