文章目录
- 前言
- 一、问题原因
- 二、解决办法
- 三、大数相加
- 1、BigInt
- 2、大数相加
- 总结
前言
下面大家先来看一下这行诡异的代码,猜猜会输出什么
console.log(0.1 + 0.2);
是不是和所想的不太一样?下面我们来探索一下为什么它会这样吧。
一、问题原因
在计算机中数字无论是定点数还是浮点数都是以多位二进制
的方式进行存储的。
在JS中数字采用的IEEE 754
的双精度标准进行存储(存储一个数值所使用的二进制位数比较多,精度更准确)
在此标准下,无法精确表示的非常大的整数将自动四舍五入
。确切地说,JS 中的 Number
类型只能安全地表示 -9007199254740991(-(2^53-1))
和 9007199254740991(2^53-1)
之间的整数,任何超出此范围的整数值都可能失去精度。
在定点数中,如果我们以8位二进制来存储数字。
对于整数来说,十进制的35会被存储为: 00100011 其代表 2^5 + 2^1 + 2^0。
对于纯小数来说,十进制的0.375会被存储为: 0.011 其代表 1/2^2 + 1/2^3 = 1/4 + 1/8 = 0.375
对于像0.1这样的数值用二进制表示你就会发现无法整除,最后算下来会是 0.000110011…由于存储空间有限,最后计算机会舍弃后面的数值,所以我们最后就只能得到一个近似值。
0.1和0.2在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出现了精度的损失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会变成0.30000000000000004。
二、解决办法
解决方法很简单,我们只要避免0.1直接和0.2相加就够了。
let a = 0.1;
let b = 0.2;
const add = (a, b) => {
return (a * 10 + b * 10) / 10;
};
console.log(add(a, b));
我们先将两个数分别乘以10相加后再除以10就能得到结果啦。(这只是一道数学题哦)
三、大数相加
前面我们提到JS 中的 Number
类型只能安全地表示 -9007199254740991(-(2^53-1))
和 9007199254740991(2^53-1)
之间的整数,如果我们需要使用比它大的数字该怎么办呢?
1、BigInt
BigInt是一种新的数据类型,用于当整数值大于 Number数据类型支持的范围时。这种数据类型允许我们安全地对大整数执行算术操作,表示高分辨率的时间戳,使用大整数id,等等,而不需要使用库。
let a = 321984671983710923709123709123709123720917312n;
let b = 2130192379012379102309123091230912309217309127309n;
console.log(a + b);
2、大数相加
虽然BigInt已经能够解决此类问题了,但是它的兼容性并不是很好
那我们也想大数相加怎么办呢?
既然没有轮子,我们就自己造一个!
function add(str1, str2) {
//判断最大长度
const len1 = str1.length;
const len2 = str2.length;
const len = Math.max(len1, len2);
// 定义字符串
let arr = "";
// 进位数
let w = 0;
// 从最小位开始进行位运算
for (let i = 0; i < len; i++) {
// 根据位次取出相应数字
let num1 = str1.charAt(len1 - i - 1);
let num2 = str2.charAt(len2 - i - 1);
// 两数相加
let sum = Number(num1) + Number(num2);
// 两数之和加上进位数
sum = sum + w;
// 重置进位数
w = 0;
// 对两数之和进行判断
if (sum >= 10) {
w++;
sum = sum - 10;
}
arr = sum + arr;
if (i === len - 1 && w === 1) {
arr = w + arr;
}
}
return arr;
}
console.log(add('709123907123170293012930790192321','79124309123091273093217092137091270217123121'));
大功告成!