阅读:1603回复:11
科普第11篇——14.52-14.49=0.0299999?
今天在微博上看到了这样一条内容:
[附件] 真奇怪呢,Google这么大个公司,编个计算器都会出错? 其实,事件的原因并不是Google的错,而是计算机的错! 究竟是怎么回事呢? 在《科普第3篇——2、8、10、16》里我们讲过,计算机表示数据都是用二进制的,因为这样更方便。 那篇里我们简单地讲了如何把其他进制的数转换为十进制,但只讲了整数,于是这里在补充一下小数吧。 一个数anan-1......a2a1a0.a-1a-2a-3......a-(m-1)a-m这个b进制的数(小数点在a0和a-1之间,m和n为正整数),其表示的数值就是: anan-1......a2a1a0=an×bn+an-1×bn-1+......+a2×b2+a1×b1+a0×b0+a-1×b-1+a-2×b-2+a-3×b-3+......+a-(m-1)×b-(m-1)a-m×b-m (b-m=1/bm,m为正整数) 例如,十进制3.14=3×100+1×10-1+4×10-2 二进制 110.011=1×22+1×21+0×20+0×2-1+1×2-2+1×2-3=4+2+0+0+0.25+0.125=6.375(十进制) 十六进制2C.BE=2×161+12×160+11×16-1+14×16-2=16+12+0.6875+0.0546875=28.7421875(十进制) 另外,还说过,十六进制、八进制与二进制之间也可以通过简单的对应关系转换,小数也不例外(对应关系表见《科普第6篇——字符编码》)。 二进制转换为十六进制(或八进制)是从小数点开始向左右分成四位(或三位)一节,不足的在整数部分最高位或小数部分最低位补0,每节都根据对应关系转换就行了。 如(1100101.01101)2=(0110 0101 . 0110 1000)2=(65.68)16 十六进制(或八进制)转换为二进制只要把各位对应的数写成四位(或三位)二进制数就行了。 如(A3.8C)16=(10100011.10001100)2 等等,讲的这些和文章一开始的内容有关吗? 目前看来是没关系,其实我只是想借这个机会把数制的内容再补充一下而已。接着看下去,你就知道文章一开始提到的问题的原因了。 接下来,是数制的转换,十六进制、八进制与二进制之间的转换前面已经说过了,下面说说从十进制转换吧。 原理说来很简单,从小数点左右开始,整数部分依次除以基数取余,从低位到高位,直到商为0;小数部分依次乘以基数取整,从高位到低位。 比如123.456,转换成二进制数: 整数部分: 123/2=61……1(低位) 61/2=30……1 30/2=15……0 15/2=7……1 7/2=3……1 3/2=1……1 1/2=0……1(高位) 即(123)10=(1111011)2 小数部分: 0.456×2=0.912……0(高位) 0.912×2=1.824……1 0.824×2=1.648……1 0.648×2=1.296……1 0.296×2=0.592……0 0.592×2=1.184……1 0.184×2=0.368……0 0.368×2=0.736……0 0.736×2=1.472……1 0.472×2=0.944……0 0.944×2=1.888……1 0.888×2=1.776……1 0.776×2=1.552……1 0.552×2=1.104……1 0.104×2=0.208……0 0.208×2=0.416……0(低位) 已经算了16位了,还没有结束,我们就暂且到此吧。 (0.456)10=(0.0111010010111100)2 所以(123.456)10=(1111011.0111010010111100)2 这下有没有点理解开头提到的问题的原因了呢? 没错,就是因为在数制转换中,小数是无法做到绝对精确转换的,在十进制下是有限小数的,在二进制下可能就是无限小数了。因为无法精确转换,所以计算的结果自然也有偏差了。 而且在计算机中,小数还不是直接转换来表示的,而是采用浮点数的方式来保存,浮点数是基于科学计数法的,也就是说123.456其实是表示为1.23456e2(1.2345×102)。 在《科普第9篇——为什么32位CPU不能支持大于4GB内存?》我们提到过,目前的32位处理器表示浮点数时,是1位符号位、8位指数位和23位尾数位的。 上面的尾数1.23456表示出来就是(1.0011110000001100000111)2。 可见十进制的小数点移动了两位,小二进制数的小数点却不是简单地移动,这样又会造成一些误差(因为表示原来的小数部分的位数又少了一些)。 最终的结果就是,计算结果完全不是我们想像的那样! 当然,只要我们善加处理,在一定范围内的精确度还是可以保证的。所以,虽然出现这个结果本质上不是Google程序员出错造成的,但是对这种会出现误差的地方不加以认真对待就是程序员的不对了,至少你得保证在你的可显示的范围内是正确的。 ==========之前的文章========== 科普第1篇——计算机色彩 科普第1篇补遗——CSS颜色 科普第2篇——光盘 科普第3篇——2、8、10、16 科普第4篇——电池 科普第5篇——浏览器 科普第6篇——字符编码 科普第7篇——加密解密 科普第8篇——移动通信技术 科普第9篇——为什么32位CPU不能支持大于4GB内存? 科普第10篇——智能手机简介 科普番外篇1——虽然没用但了解一下也很有趣的知识 |
|
沙发#
发布于:2011-05-05 19:57
那岂不是只要是小数运算就会出错了?
|
|
|
2楼#
发布于:2011-05-05 20:11
回 1楼(mooncrazy) 的帖子
嗷原来是这样 |
|
|
3楼#
发布于:2011-05-05 20:11
小旭上榜了……果然还是数制的原因啊
|
|
|
4楼#
发布于:2011-05-05 20:29
不是计算机专业的表示压力很大额!
|
|
|
5楼#
发布于:2011-05-05 20:33
受教了,原来是浮点运算的问题
|
|
|
6楼#
发布于:2011-05-05 21:53
嘛~~有时候也不一定都是数据转换的问题,
像数学上的拓扑学就有许多看似不相等,实际上确实相等的等式例如 1=0.9999999 什么的 |
|
7楼#
发布于:2011-05-05 22:05
《Java 解惑》里看到过,应该用BigDecimal
|
|
8楼#
发布于:2011-05-05 22:07
没办法嘛,小数的二进制转换~~~不一定除的尽2
|
|
9楼#
发布于:2011-05-06 03:01
科普系列最高.....
原来计算器的约算这么来的 |
|
|
10楼#
发布于:2011-05-06 04:36
又学到好东西了
|
|
11楼#
发布于:2011-05-06 06:57
我觉得还是设计时的问题,这完全可以避免嘛~~
|
|
|