js浮点数加减乘除
js浮点数的加减乘除存在着严重的bug,例如:在google浏览器下,0.1+0.2=0.30000000000000004
;这完全不是我们想要的结果。
对于这一问题的解决方案就是重写浮点数的加减乘除方法,其原理是现将浮点数转换为整数。进行加减乘数,再除以相应的倍数,使之成为对应的浮点数结果。
原理
浮点数的加减乘数之所以会出现bug,跟javascript的精度有关,js中所有数字都是以64位的浮点数形式存储的,包括整数也是如此。只是整数的计算时会现将浮点数形式的整数转化为32位的整数。浮点数由于位运算
而导致不够精确。
1===1.0 //true
js中数字的64位二进制存储形式如下:
- 第1位:符号位,0表示正数,1表示负数
- 第2位到第12位:指数部分
- 第13位到第64位:小数部分(即有效数字)
所以,js数值的范围为:Math.pow(2, -1023)
到Math.pow(2, 1024)
。(1024 = 2的11次方,11为指数位数)
数值的精度范围为:-Math.pow(2, 53)+1
到Math.pow(2, 53)-1
。(符号位+有效位数为53)
Math.pow(2, 53)===9007199254740992
在精度范围内的整数都可以表示,超出该范围的的将无法保证精度。例如:
9007199254740992111
//9007199254740992000,最后3位的精度丢失了。
Math.pow(2, 53) + 1
// 9007199254740992
Math.pow(2, 53) + 2
// 9007199254740994
Math.pow(2, 53) + 3
// 9007199254740996
Math.pow(2, 53) + 4
// 9007199254740996
对于超出精度范围数字的处理,这里暂不介绍,可以大概说一下思路:因为最大精度只能表示16位数字,所以对超出精度范围的数字可以做截取处理,以15位数字为一个单位,将一个大数据分为几个部分,这几个部分组合起来表示这个大数据。至于大数据的加减乘除,肯定也需要重写的,网上有封装好的工具。
解决方案
网上有很多关于浮点数加减乘数的方法,我大致看了一下,都存在或多或少的问题。下面贴上我的代码:
//小数乘法
export const floatMul = (a, b) => {
let m = 0, n = 0, //记录a,b的小数位数
d = a + "", //字符串化
e = b + "";
try {
m = d.split(".")[1].length;
} catch (error) {
console.log(error)
}
try {
n = e.split(".")[1].length;
} catch (error) {
console.log(error)
}
let maxInt = Math.pow(10, m + n); //将数字转换为整数的最大倍数
return Number(d.replace(".", "")) * Number(e.replace(".", "")) / maxInt;
}
//小数加法
export const floatAdd = (a, b) => {
let m = 0, n = 0, //记录a,b的小数位数
d = a + "", //字符串化
e = b + "";
try {
m = d.split(".")[1].length;
} catch (error) {
console.log(error)
}
try {
n = e.split(".")[1].length;
} catch (error) {
console.log(error)
}
let maxInt = Math.pow(10, Math.max(m, n)); //将数字转换为整数的最大倍数
return (floatMul(a, maxInt) + floatMul(b, maxInt)) / maxInt;
}
//小数减法
export const floatSub = (a, b) => {
let m = 0, n = 0, //记录a,b的小数位数
d = a + "", //字符串化
e = b + "";
try {
m = d.split(".")[1].length;
} catch (error) {
console.log(error)
}
try {
n = e.split(".")[1].length;
} catch (error) {
console.log(error)
}
let maxInt = Math.pow(10, Math.max(m, n)); //将数字转换为整数的最大倍数
return (floatMul(a, maxInt) - floatMul(b, maxInt)) / maxInt;
}
//小数除法
export const floatDivision = (a, b) => {
let m = 0, n = 0, //记录a,b的小数位数
d = a + "", //字符串化
e = b + "";
try {
m = d.split(".")[1].length;
} catch (error) {
console.log(error)
}
try {
n = e.split(".")[1].length;
} catch (error) {
console.log(error)
}
let maxInt = Math.pow(10, Math.max(n, m)); //将数字转换为整数的最大倍数
let aInt = floatMul(a, maxInt);
let bInt = floatMul(b, maxInt);
return aInt / bInt;
}