本质
js计算精度问题导致,js整数最大取值为2的53次方,也就是9007199254740992
需要掌握的知识点
- 进制(十进制,二进制,八进制,十六进制)
- 科学计数法
- 浮点型的存储机制
进制不做过多介绍了,主要说一下后两点
科学计数法
对于浮点型的数,小数点位置的不固定和小数点后数组的不固定,所有就有人提出了科学计数法来标识浮点数
公式:
a表示浮点数的二进制数
e标识小数点移动的位数
例如:10转换成二进制为 1010,用科学计数法表示为:1.010 * 2^3
再来解释一下科学计数法在计算中是怎么存储的
名称 | 符号位 | 指数位 | 小数位 | 指数偏移量 |
---|---|---|---|---|
单精度浮点数 | 1位 | 8位 | 23位 | 127 |
双精度浮点数 | 1位 | 11位 | 52位 | 1023 |
解释:
- 1位用来保存符号
- 11位用来保存指数
- 52位用来保存小数部分
符号位:1表示正数,0表示负数
指数位:e可以是正数,也可以使负数,转换规则,e+偏移量的结果转换成二进制
小数位:就是科学计数法小数点后的数,总数是52,不够补0,这里的52也是表示整数的最大取值为52位
指数偏移量:公式
k是指数位个数(双精度的指数位为11位,所有x = 2^11-1 = 1023)
例如:10的二进制1010,科学计数法为:1.010 * 2^3
转成二进制标准形式为:
符号位 + 指数位 + 小数位 = 1 + 10000000010 + 0100000000000000000000000000000000000000000000000000
正好64位
再举一个浮点数的例子:
0.1 + 0.2 = 0.30000000000000004;
我们来通过上面的知识点来分析这个问题:
备注:小数转二进制,通过乘2去整,顺序排列
- 推导0.1
0.1 的二进制:0.0001100110011001100110011001100110011001100110011001101 (备注:在科学计数法的形式中只能保存64位有效数字,这里进行了四舍五入末尾0变成1)
0.1 的科学计数法:1.100110011001100110011001100110011001100110011001101 * 2^(-4)
0.1 在计算机中实际存储的形式:1011111110111001100110011001100110011001100110011001100110011010 正好64位 - 推导0.2
0.2 的二进制:0.001100110011001100110011001100110011001100110011001101
0.2 的科学计数法:1.100110011001100110011001100110011001100110011001101 * 2^(-3)
0.2 在计算机中实际存储的形式:1011111111001001100110011001100110011001100110011001100110011010 - 将0.1和0.2二进制相加
0.0001100110011001100110011001100110011001100110011001101 + 0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111
转换为十进制也就是0.30000000000000004
明白了原理和基本知识点,接下来就是大数相加问题的处理了
思路1:
- 首先number类型是有对打值的超过最大值,就会不准确,最大位数为16位,所以第一步会先对大数字符窜做截取,以14位为一段(考虑到15位和15位相也可能超多最大值,这里采用了比较安全的14位截取)
- 当每段的14位先加完,判断相加后的总长度是否大于等于15位,也就是有没有进位,如果有则在下一轮计算中需要把进位1加上
function addBigNum(num1, num2){
var l1 = String(num1).length, //保存num1长度
l2 = String(num2).length, //保存num2长度
flag = false, //判断是否有进位
newNum1 = String(num1), //用来后面做截取
newNum2 = String(num2),
newSum = "",
sum = '';
if(l1 < 14 && l2 < 14){
return Number(num1) + Number(num2);
}
while (l1 >= 14 || l2 >= 14){
newNum1 = l1 && l1 > 14 ? newNum1.slice(l1-14?l1-14:0, l1): '';
newNum2 = l2 && l2 > 14 ? newNum2.slice(l2-14?l2-14:0, l2): '';
//判断时候有进位
if(flag){
newSum = String(Number(newNum1) + Number(newNum2) + 1);
flag = false;
}else {
newSum = String(Number(newNum1) + Number(newNum2));
}
if(newSum.length >= 15){
sum = String(newSum.slice(1, newSum.length)) + String(sum);
flag = true;
}else {
sum = String(newSum) + String(sum);
}
l1 -= 14;
l2 -= 14;
//最后把不足14位的部分做加法
if(l1 < 14 && l2 < 14){
if(flag){
sum = String(Number(num1.slice(0, l1)) + Number(num2.slice(0, l2)) + 1) + String(sum);
}else {
sum = String(Number(num1.slice(0, l1)) + Number(num2.slice(0, l2))) + String(sum);
}
}
}
return sum;
}
One comment
怎么收藏这篇文章?