var number1 = 10000000000000000000000000 + 11111111111111111111111111 //理论上number1的值应该是21111111111111111111111111(javascript中会表示为科学计数法:2.111111111111111e+25)
var number2 = 21111111111111111111111000
console.log(number1 === number2) //true
复制代码
这个不用算简单看一下都知道计算结果不对,最后几位确定应该都是1的,但是为何会获得一个不对的值呢?javascript
由于JavaScript
的Number
类型是遵循IEEE 754规范表示的,这就意味着JavaScript
能精确表示的数字是有限的,JavaScript
能够精确到个位的最大整数是9007199254740992,也就是2的53次方,超过这个范围就会精度丢失,形成JavaScript
没法判断大小,从而会出现下面的现象:php
Math.pow(2, 53); // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1; // true
9007199254740992 === 9007199254740992 + 1; // true
复制代码
能够从下面这张图上看到JavaScript
Number
可以精确表示的上下限: html
那当两个数据相加时,其中一个或者两个数据都超过了这个精度范围,直接相加结果就会不许了,那怎么解决呢?java
参考网上经常使用的一中方案是将Number
转为String
,而后将String
转为Array
,而且注意补齐较短的数组,将他们的长度标称同样再一一相加获得一个新数组,再讲和组成的新数组转为数字就能够了,下面是实现代码:数组
function sumString(a, b) {
a = '0' + a;
b = '0' + b; //加'0'首先是为了转为字符串,并且两个数相加后可能须要进位,这样保证了和的长度就是a、b中长的那个字符的长度
var arrA = a.split(''), //将字符串转为数组
arrB = b.split(''),
res = [], //相加结果组成的数组
temp = '', //相同位数相加的值
carry = 0, //同位数相加结果大于等于10时为1,不然为0
distance = a.length - b.length, //计算两个数字字符串的长度差
len = distance > 0 ? a.length : b.length; //和的长度
// 在长度小的那个值前加distance个0,保证两个数相加以前长度是想等的
if(distance > 0) {
for(let i = 0; i < distance; i++) {
arrB.unShift('0');
}
}else{
for(let i = 0; i < distance; i++) {
arrA.unShift('0');
}
}
// 如今获得了两个长度一致的数组,须要作的就是把他们想通位数的值相加,大于等于10的要进一
// 最终获得一个和组成的数组,将数组转为字符串,去掉前面多余的0就获得了最终的和
for(let i = len-1; i >= 0; i--) {
temp = Number(arrA[i]) + Number(arrB[i]) + carry;
if(temp >= 10) {
carry = 1;
res.unshift((temp + '')[1])
}
else{
carry = 0;
res.unshift(temp)
}
}
res = res.join('').replace(/^0/, '');
console.log(res);
}
复制代码
前面讲到,在JavaScript
中,使用浮点数标准IEEE 754
表示数字的,在表示小数的时候,在转化二进制的时候有些数是不能完整转化的,好比0.3,转化成二进制是一个很长的循环的数,是超过了JavaScript
能表示的范围的,因此近似等于0.30000000000000004。浏览器
这个是二进制浮点数最大的问题(不只 JavaScript,全部遵循 IEEE 754 规范的语言都是如此)。bash
在这里咱们要引入ES6中在Number
对象上新增的一个极小的常量Number.EPSILON
。它表示1与大于1的最小浮点数之差,等于2的-52次方。学习
Number.EPSILON其实是 JavaScript 可以表示的最小精度。偏差若是小于这个值,就能够认为已经没有意义了,即不存在偏差了。ui
因此能够用这个来判断两个数浮点数是否想等:spa
function numIsEqual(lef, rig) {
return Math.abs(lef - rig) < Number.EPSILON
}
复制代码
若是考虑浏览器兼容的话能够这样写:
function numIsEqual(lef, rig) {
let EPSILON = Number.EPSILON ? Number.EPSILON : Math.pow(2,-52)
return Math.abs(lef - rig) < EPSILON
}
复制代码
固然咱们平常使用小数的话通常精确到小数点后两位就够了,是不会有太大的问题的,可是必定要记得用toFied()
方法保留小数点后位数,不能直接取计算的值,避免偏差。
关于JavaScript
中数据的表示问题,我以前在极客时间的《深刻浅出计算机组成原理》中还听到过一些,那里面详细讲解了为何浮点数的IEEE 754
标准只能表示有限的数字,不过对于这么底层的东西我还只能勉强意会,哈哈。