在js中,数字是如何存储的,为啥在小数相加的时候,会出现精度不准确。如:
原因分析
我们在现实生活中,数字是以
10 进制
(0,1,2,3,4,5,6,7,8,9)的形式来进行数学运算, 但是在计算机中是以2 进制
( 0,1)的数据进行存储的。那么10 进制
与2 进制
是怎么进行转换的呢?
整数10进制
转2进制
十进制在转二进制,将十进制的数据除以2,得出余数和商,然后继续用商来除以2,知道商为0,得到所有的余数。
例如: 将十进制的10
转成二进制的数据是 1010
表达式 | 商 | 余数 |
10 / 2 | 5 | 0 |
5 / 2 | 2 | 1 |
2 / 2 | 1 | 0 |
1 / 2 | 0 | 1 |
然后把余数反过来读, 1010
就是 10
的二进制的数据
小数 10进制
转 2 进制
将10进制的小数部分乘以2,然后判断结果的小数部分是否为0,为0结束,不为0继续乘以2
例如: 将十进制的10.25
转成二进制的数据是 1010.01
表达式 | 积 | 小数部分 | 整数部分 |
0.25 * 2 | 0.5 | .5 | 0 |
0.5 * 2 | 1.0 | 0 | 1 |
然后把整数部分是结果自上而下获取结果 01
就是 10进制的0.25
整数2进制
转10进制
二进制的整数转成十进制是,从右往左 的每个数字 乘以 2n2^{n} (n >=0 && n < 字符串长度-1),然后将每一位的和进行相加。
例如: 将二进制的 1000 010
转成十进制 66
小数2进制
转10进制
二进制的小数转十进制是从小数点从左往右的每个数字乘以2−n2^{-n} (n >=1 && n <= 字符串长度-1),然后将每一位的和进行相加。
例如将1000 010.1001
转成10进制的数是
回到问题: 0.1 + 0.2
这是十进制的数字,在做运算的时候需要转成二进制来进行运算。
0.1转二进制的数据如下:
表达式 | 积 | 小数部分 | 整数部分 |
0.1 * 2 | 0.2 | .2 | 0 |
0.2 * 2 | 0.4 | .4 | 0 |
0.4 * 2 | 0.8 | .8 | 0 |
0.8 * 2 | 1.6 | .6 | 1 |
0.6 * 2 | 1.2 | .2 | 1 |
0.2 * 2 | 0.4 | .4 | 0 |
0.4 * 2 | 0.8 | .8 | 0 |
0.8 * 2 | 1.6 | .6 | 1 |
0.6 * 2 | 1.2 | .2 | 1 |
… 此处是 0011无限循环,所以得出的结果是 |
0.1 转换的成二进制的小数是无限循环小数,但是我们的计算机保存的二进制的数据是有限的位数, 而 js中存储数据的长度是固定为
64位
,所以这下明白了,为啥有的小数相加会出现问题了吧。
数字存储的方式
每种语言都有不一样的存储方式,例如:
整数法
,浮点法
等。但是在js
中,存储的数据的方式是使用浮点法。
浮点法存放的数字,叫做浮点数(float),浮点数分位单精度和双精度。 JS中,使用双精度存放浮点数,来源 IEEE 754。
浮点数存储数字如下:
上图是64位的双精度浮点数,最高位是符号位S(sign)
,中间的11位是指数E(exponent)
,剩下的52位为尾数(有效数字)M(mantissa)
浮点数科学计数法 根据IEEE 754标准,任意一个浮点数的二进制都可以用如下公式进行表示:
- S为符号位:表示浮点数的正负(0代表正数,1代表负数);
- E为指数位:存储指数,该数都会加上一个常数(偏移量),用来表示次方数(需要把二进制的数据转成十进制),长度是11位,取值范围是为0~2047。因为科学计数法中的指数是可以为负数,所以约定减去一个中间数(偏移量)1023,[0,1022] 表示为负,[1024,2047] 表示为正。;
- M为尾数位:表示有效位(尾数),超出的部分自动进1舍0,默认节省1位有效数字;
表示Infinity
现在我们知道了数字的存储方式,那么我们Infinity
是怎么样表示的呢?
Infinity : 0 1111111111 0000000000……………………=Infinity
只要我们的指数最大,后面的数字是 1.00000……(此处是52个0) 那就规定是 Infinity
复制代码
表示-Infinity
同理,我们也可以知道最小的-Infinity
数字的表示方式
Infinity : 1 1111111111 0000000000……………………= -Infinity
只要我们的指数最大,后面的数字是 1.00000……(此处是52个0) 前面符号位是负数,那就是规定是-Infinity
复制代码
表示NaN
还有一个就是NaN
是咋样表示的呢?
Infinity : 0 1111111111 101010……………………= NaN
只要我们的指数最大(2047),后面的尾数随便,表示的就是NaN
复制代码
表示最大数字
当我们的指数是 2046(1111111110)然后尾数全是1 那么这个就是最大的数字
1 1111111110 111111……………………(52个1)
复制代码
表示最小数字
当我们的指数为0
(0000 0000 000), 然后尾数 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
,这个时候最小。
0 00000000000 000000000000000000000000000000000000000000000000001
复制代码
0.0000000000000000000000000000000000000000000000000001 转换成十进制特别小,所以我们就姑且 默认为 2−51 2 ^ {-51}
表示最大安全整数
那么如何表示最大的安全整数呢?(安全整数是连续的数字,就是说该数字从
1
到它和它的下一位存在并且是连续的)
想要是一个连续数字,那就是尾数取满,全是1, 指数来取整数
表示最小安全整数
同理,最小的安全整数我们只要保证符号位是1就行
引用
这篇文章是我找遍全网的资料,然后经过大量的测试得出的一个结果。感谢那些前辈,站在巨人的肩膀上,加油!!!
developers.weixin.qq.com/community/d…
www.jianshu.com/p/c4bf75048…
安装掘金浏览器插件
打开新标签页发现好内容、GitHub、Dribbble、ProductHunt 等站点内容轻松获取。快来安装浏览器插件获取高质量内容吧!