为啥计算机里会有+0和-0,用1分别除以它们,结果让你大吃一惊!

500

撰文 | 乌其多 

审校 | 山 矾

你也许认为:0就是0。

“0”可能代表空、起点、归零、银行卡余额、精心发朋友圈后的点赞人数、买了半辈子彩票后的中奖次数、暗恋你的人数、国足夺得世界杯冠军的概率等等......

500

无论如何,它都应该只有一种形态。

然而,在大部分计算机编程语言里,0有两种表示形式:

+0和-0。

??????

消息传到股民耳朵里,立刻有人开始幻想(没错,是我):“T+0都能赚钱了,T-0岂不是直接起飞?”

很遗憾,两者没有任何关系。

这里的正负0,只是计算机为了保存一种特殊信息而设计出来的。

更具体地说,这源于大部分编程语言都遵循的一套规则——IEEE 754浮点数标准。这套标准规定了浮点数在计算机中的存储格式,正是它造就了0.1+0.2≠0.3的经典奇观,也顺带把0劈成了两半。

为啥计算机算不出0.1+0.2=0.3?

500

经过约 8 年的讨论、修改、协调,最终由威廉·卡汉主导制定了统一的IEEE 754标准| wiki

0的两张“身份证”,从何而来?

在这项标准里,一个数会被拆成三部分:符号位、指数位和尾数位。

符号位只有0和1两种状态:0表示正号,1表示负号。

指数位决定数字的数量级,相当于十进制里的“十、百、千、万”这种大小档位。只不过在计算机的二进制世界里,它真正使用的是 2 的幂。

尾数位则保存数字真正的有效数字,决定这个数具体是多少。

为了便于理解,我们先用十进制打个比方。比如-43501=-4.3501×10⁴,那么可以如下表示:

500

这时,+0和-0是怎么来的,就很好理解了:问题就在于这个“符号位”:当指数位全为0,尾数位也全为0时,这个数就是0。但它仍然必须拥有一个符号位,不要不得行。

于是:符号位为0,就是+0;符号位为1,就是-0。

500

-0的表示法如上| wiki

也就是说,在计算机眼里,0虽然数值上只有一个,却可以拥有两张不同的“身份证”。

很多人看到这里,可能会觉得:既然+0和-0数值完全一样,那保留它们还有什么意义?答案是:

为了看清0来时的路。

举个例子,假设有两个数字:一个是+0.0000001,另一个是-0.0000001。随着小数点后0不断增加,它们会越来越接近真正的0。

最终,由于已经小到超出了浮点数能够精确表示的范围,计算机会分别把它们记录成+0 和-0。虽然它们现在看起来都是0,但计算机依然知道:一个来自正数,一个来自负数。

就如同两个人乘坐电梯,一人从上到下到达0层,一个从下到上到达0层。他们最终在0层相遇,但是来的方向是不一样的。

500

自制

这种“来路信息”,在游戏动画、自动驾驶、机器人控制中至关重要。程序员常用的“ atan2( ) 函数”就是典型例子——它根据坐标来计算物体的朝向。

以游戏角色为例:假设目标位于角色的正西方,坐标是 (-1, 0)。问题在于,这个目标既可能是从上方转移至此,也可能从下方转移至此。对于数学来说,这两个位置完全一样;但对于计算机来说,它们却不是同一种情况。

如果目标是从上方靠近的,角色继续逆时针旋转才最自然。

500

如果目标是从下方靠近的,则应该继续顺时针旋转——虽然最后停留的位置相同,但运动轨迹却完全不同。

500

这时候,+0和-0就派上了用场。如果目标来自上方,计算机会把它记作 (-1,+0);如果来自下方,则会记作 (-1,-0)。正是这正负号之别,让动画保持了运动的连续性,不至于在终点处突然抽搐、掉头。

除了图形和动画,这种方向信息在科学计算中同样重要。例如,IEEE  754规定:

1÷+0 = +∞

1÷-0 = -∞

你可能又要说:等会儿,+0和-0我已经忍了,怎么还能拿1去除?数学老师不是说过,0绝对不能作除数吗?

500

没错,在数学课上,这句话完全正确。

计算机从来不是比数学更聪明,而是它必须面对一个数学课本不用面对的问题:现实。比如,一辆汽车正在减速,它的速度可能依次变成:10 米/秒、1 米/秒、0.1 米/秒、0.01 米/秒……越来越接近0。这时,如上所述,计算机会把它记成+0 或-0,分别代表越来越小的正数和越来越小的负数。

既然如此,当程序继续计算1÷这个数时:来自正数,就继续朝着+∞增长;来自负数,就继续朝着-∞增长。

这样设计,并不是为了挑战数学老师的心态,而是为了让程序能够继续运行,有其重要的实用意义。

想象一下,一辆自动驾驶汽车正在计算转向角度。某一步计算中,由于传感器误差,一个数无限接近0。如果程序此时直接像数学课本一样回答"除以0,没有意义",会发生什么?

那么整套计算就只能立刻报错终止。方向盘、制动系统、路径规划……后续所有依赖这一步结果的计算,都可能跟着停止,甚至造成车毁人亡的惨剧。

500

网络

所以,IEEE754选择返回+∞或-∞。这也是在告诉程序:这里出现了一个极端情况,但程序可以继续运行。

对于运行几个小时、几天,甚至几个月的大型科学计算来说,这一点同样重要:如果每遇到一次接近0的情况,程序都直接停止,那么前面所有计算都可能功亏一篑。

所以,+0和-0,从来不是为了让0变成两个不同的数字。它真正保存的是:这个0,是从哪里来的。而1÷±0得到±∞,保存的则是:如果继续沿着这个方向计算,它最终会走向哪里。

一个记录来路。一个记录去向。

正是这些看似不起眼的细节,让计算机在面对现实世界中无数接近边界、接近极限的计算时,依然能够保持连续、稳定,而不会因为一个小小的0就彻底停摆。

很多时候,我们后来学到的新知识,并不是在推翻课本,而是在补充课本。数学告诉我们什么是严格正确的;工程思考的则是:当现实世界没有那么理想时,怎样才能让系统继续安全、稳定地运行。

参考资料:

[1] https://en.wikipedia.org/wiki/IEEE_754?utm_source=chatgpt.com

‌[2] https://docs.oracle.com/cd/E19957-01/800-7895/800-7895.pdf?utm_source=chatgpt.com

站务

全部专栏