一行代码引发惨案,这似乎有点儿夸张,但看完文章后你可能就会改变看法。
时间回到1991年2月25号,在一个月黑风高的夜晚,一枚飞毛腿导弹,悄无声息地飞临沙特的达兰美军军营上空,而已经连续作战4天的美军爱国者导弹防御系统,没能识别出这一危险的目标。这个大杀器直扑美军营地,造成了28名士兵死亡,100多人受伤,可谓惨烈!要知道整个海湾战争美军一共才战死了148人啊。
在战争中大放异彩,被吹嘘的神乎其神的爱国者防御系统,是如何犯下这个致命的错误的呢?这个起因倒是不复杂,其实在2月11号,以色列军方就已经发现,系统存在隐患。他们发现在爱国者系统连续工作8小时后,目标捕获精度会下降20%,在连续工作20小时以后,系统看起来似乎失效了。
在接到这一上报后,美国军方大意了,他们觉得反导系统不会连续工作这么长时间,虽然开始给软件打补丁,但由于是在战时状态,进度比较慢,于是军方发出命令,让系统不要工作太长时间。但是这个太长时间是多久?并没有说个明白。于是墨菲定律又起作用了:如果事情有变坏的可能,不管这种可能性有多小,它总会发生。于是就发生了文章开头的一幕。那么问题出在哪儿了呢?
随后的调查显示,问题的根源,在软件中一个隐藏很深的Bug。爱国者系统软件,使用了一个3字节,也就是24bit的变量存储一个0.1秒的单位时间,存储时间值和真实时间之间,有一个微小的差值,这个时间差值在系统运行时逐渐累积,在系统不间断长时间运行后,积累的时间差值过大,最终导致了严重问题。
原来不像我们平常计算使用10进制,计算机系统用2进制存储数据,0.1秒变成2进制是0.0001100110011001100110011001100....,这是一个无限循环小数,当用24bit 的变量存储它时,精度只保留到前24bit,后面的都被舍弃掉了,那这个误差有多少呢?换算成10进制是0.000000095秒,这看起来是一个很微不足道的误差,但是工作100小时后,这个误差会积累到0.000000095×100(小时)×60(分钟)×60(秒)×10(次/秒)=0.34秒。这似乎仍然是一个非常非常短的时间,然而,这足以让以1676米/秒高速飞行的飞毛腿导弹,飞过569.84米,轻松突破本以为坚不可摧的防线了。
我们在编写代码时,一定要注意每一个变量的位数,而且需要注意的是,在不同的操作系统,或者使用不同的编译器时,同一个类型的变量长度可能都是不同的。这在移植代码时尤其要注意,原来工作正常的代码,换个平台,换个编译器可能就不同了。
需要注意计算过程有没有造成结果精度的下降,有没有产生累积误差。计算过程有没有可能造成结果溢出,即结果小于0,或大于变量所允许最大值的可能。
整型,无符号型,浮点型等变量类型,不要混用,否则强制类型转换可能导致不可预知的结果。