背景介绍
在控制台中,输入0.1+0.2,输出结果并未按照原有设想输出0.3,而是输出了
原理
原因在于两点:1. 小数加法运算会进行二进制运算。 2. js中,number用64位双精度表示:,1位符号位 11位表示指数位 52位表示数值位
小数二进制转换
文字描述该过程如下:将该数字乘以2,取出整数部分作为二进制表示的第1位;然后再将小数部分乘以2,将得到的整数部分作为二进制表示的第2位;以此类推,直到小数部分为0。
特殊情况: 小数部分出现循环,无法停止,则用有限的二进制位无法准确表示一个小数,这也是在编程语言中表示小数会出现误差的原因。
0.1 * 2 = 0.2 ---- 0
0.2 * 2 = 0.4 ---- 0
0.4 * 2 = 0.8 ---- 0
0.4 * 2 = 0.8 ---- 0
0.8 * 2 = 1.6 ---- 1
0.6 * 2 = 1.2 ---- 1
0.2 * 2 = 0.4 ---- 0
...
因此,0.1跟0.2都被转换成二进制无限小数。
js的Number在内存中怎么存储的
js中,number用64位双精度表示:,1位符号位 11位表示指数位 52位表示数值位
| 符号位 | 指数位 | 数值位 |
|---|---|---|
| +/- | 11位 | 52位 |
规定:指数位全为1或0时有特殊作用((所以上面,y的范围为(-2^10 + 1) ~ (2^10 - 1),再去掉0和1023,-1023))
问题:什么是指数位,什么是数值位?
回答:首先,需要了解科学计数法,由于数字过大或过小时,就会转换成科学计数法。指数位,表示一个值可以存储科学表示法的数量。
Math.pow(2, 1023) // 8.98846567431158e+307
Math.pow(2, 1023) // Infinity
因此,一个值最大存储起来是2^1023方
数值位,表示一个值可以展示出来的数量。
那么为啥是 53 呢,因为二进制表示中,有效数字最长为53个二进制位( 52 位尾数 + 有效数字第一位的 1[被舍弃的 1] )
举个例子:
2^53 是这么存的:符号位:0,指数:53,尾数:1.00000...000(一共52个0)
2^53 - 1是这么存的:符号位:0,指数:52,尾数:1.11111...111(小数点后52个1)
2^53 - 2的存法:符号位:0,指数:52,尾数:1.11111...110(小数点后51个1,一个0)
总结
0.1 + 0.2: 0.1跟0.2本身是二进制存储,其实是一个无限小数,然后相加,所以,截取52位的双精度浮点数来表示十进制的数字,最后就会出现偏差。