发表于2016/1/17 13:27:34 638人阅读php
分类: JavaScripthtml
先看看下面的程序:面试
var sum = 0; for(var i = 0; i < 10; i++) { sum += 0.1; } console.log(sum);
上面的程序会输出1吗?编程
在 你有必要知道的 25 个 JavaScript 面试题 一文中,第 8 个题浅显的说了下 js 为何不能正确处理小数运算的问题。今天重拾旧题,更深层次的剖析下这个问题。数组
但要先说明的是,不能正确处理小数的运算并非 JavaScript 语言自己的设计错误,其它高级编程语言,如C,Java等,也是不能正确处理小数运算的:markdown
#include <stdio.h> void main(){ float sum; int i; sum = 0; for(i = 0; i < 100; i++) { sum += 0.1; } printf('%f\n', sum); //10.000002 }
咱们都知道,用高级编程语言编写的程序须要通过解释、编译等操做转变成 CPU(Central Processing Unit) 能够识别的机器语言才能运行,而对 CPU 来讲,它不识别数的十进制、八进制和十六进制等,咱们在程序中声明的这些进制数都会被转成二进制数进行运算。编程语言
为何不是转换成三进制数进行运算呢?ui
计算机内部是由不少的 IC (Integrated Circuit: 集成电路) 这种电子部件构成的,它的长相大概是这样子:spa
IC 有不少种形状,在其两侧或内部并排排列着不少引脚(图示只画出了一侧)。IC 的全部引脚,只有直流电压 0V 或 5V 两个状态,即一个 IC 引脚只能表示两个状态。IC 的这个特性就决定了计算机内部的数据只能用二进制数处理。设计
因为 1 位(一个引脚)只能表示两个状态,因此二进制的计算方式就变成了 0、一、十、十一、100….这种形式:
因此,在数的运算中,全部操做数都会被转成二进制数参与运算,如39,会被转换成二进制 00100111
如前文所说,程序中的数据都会被转换成二进制数,小数参与运算时,也会被转成二进制,如十进制的11.1875 会被转换成1101.0010。
小数点后 4 位用二进制数表示的数值范围是 0.0000~0.1111,所以,这只能表示 0.五、0.2五、0.12五、0.0625 这四个十进制数以及小数点后面的位权组合(相加)而成的小数:
二进制数 | 对应的十进制数 |
---|---|
0.0000 | 0 |
0.0001 | 0.0625 |
0.0010 | 0.125 |
0.0011 | 0.1875 |
0.0100 | 0.25 |
0.1000 | 0.5 |
0.1001 | 0.5625 |
0.1010 | 0.625 |
0.1011 | 0.6875 |
0.1111 | 0.9375 |
从上表能够看出,十进制数 0 的下一位是 0.0625,因此,0~0.0625 之间的小数,就没法用小数点后 4 位数的二进制数表示;若是增长二进制数小数点后面的位数,与其相对应的十进制数的个数也会增长,但不管增长多少位,都没法获得 0.1 这个结果。实际上,0.1 转换成二进制是 0.00110011001100110011……
注意 0011
是无限重复的:
console.log(0.2+0.1); //操做数的二进制表示 0.1 => 0.0001 1001 1001 1001…(无限循环) 0.2 => 0.0011 0011 0011 0011…(无限循环)
js 的 Number 类型并无像 C / Java 等分整型、单精度、双精度等,而是统一表现为双精度浮点型。按照 IEEE 的规定,单精度浮点数用 32 位表示全体小数,而双精度浮点数用 64 位表示全体小数,而浮点数由符号、尾数、指数和基数组成,因此并非全部的位数都用来表示小数,符号、指数等也要占据位数,基数不占据位数:
双精度浮点数的小数部分最多支持 52 位,因此二者相加以后获得这么一串 0.0100110011001100110011001100110011001100…因浮点数小数位的限制而截断的二进制数字,这时候,再把它转换为十进制,就成了 0.30000000000000004。
js 不能正确处理小数运算,包括其它高级编程语言同样,这不是语言自己的设计错误,而是计算机内部自己就不能正确处理小数的运算,对小数的运算每每会获得意想不到的结果,由于并非全部的十进制小数能被二进制表示。