|程序员应知应会之二进制小数的计算

|程序员应知应会之二进制小数的计算

作为一名程序员 , 大家都知道计算机里的数字都是用二进制来表示的 。 那么具体是怎么表示的 , 很多人可能就不太清楚了 。 毕竟大家都已经习惯了定义一个float或double就觉得万事大吉了 。 然而虽然通常情况下这样并不会出什么问题 , 但是在特殊的情况下 , 还是有可能出问题的 。
例如在1991年2月25日 , 第一次海湾战争期间 , 美国设置在沙特阿拉伯达摩地区的爱国者导弹 , 在拦截伊拉克的飞毛腿导弹的过程中 , 就因为一个底层数字的计算不精确 , 导致拦截失败 , 最终飞毛腿导弹击中了美国的一个兵营 , 造成了28名美军士兵的死亡 。 事后 , 美国总审计局对失败原因进行了详细的分析 , 最终定位到底层的原因就是在为一个数字精度的问题 。
【|程序员应知应会之二进制小数的计算】
下面我们先来看看小数在二进制里是怎么表示的 。 二进制小数在浮点型数字里面 , 以0.101的形式表示 , 二进制小数点向右移动一位相当于将该数乘2 , 向左移一位相当于将该数除以2 。 因此二进制数101.11表示5.75 。 1011.1相当于11.5 。
了解了这些以后 , 我们再回过头来看下前面那个爱国者导弹的问题 。 经过调查分析 , 爱国者导弹中有一个内置的时钟 , 其实现类似于一个计数器 , 每隔0.1秒就加1 。 因为时间计数是以秒为单位的 , 所以程序用了一个24位近似于十分之一的二进制小数来乘以这个计数器的值 。 因为0.1的二进制是一个无限循环小数0.000110011[0011...
, 对于24位的二进制数来说 , 只需要考虑二进制小数点右边的前23位 , 即0.00011001100110011001100 。 那么真实的0.1与真值之间的误差即为0.1减去这个数 , 这个误差约为0.0000000954 。
而在出错的爱国者导弹发射之前 , 系统已经运行了100小时 , 那么因为这个时钟的偏差就已经达到了0.0000000954*3600*100 , 约为0.343秒 。 而飞毛腿导弹的速度为2000米/秒 , 那么误差距离即为2000*0.343 , 达到686米 。 因此导致拦截失败 。
由此可见 , 如果不深入了解计算机系统的具体实现 , 就会在工作中出现意想不到的后果 。 甚至出现灾难性的后果 。
现在中国已经很多年没有打仗了 , 一旦打仗 , 暴露出来的系统问题可能会比美国多得多 。 到时候 , 不知道会有多少解放军战士会因为信息系统的开发者的专业能力问题而白白牺牲 。