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