JavaScript计算进度问题小计
十进制小数转为二进制小数方法
拿 9527.8125 举例如何将之转化为二进制小数
1.针对整数部分9527,采取除2取余,逆序排列;
9527 / 2 = 4763 ... 1 4763 / 2 = 2381 ... 1 2381 / 2 = 1190 ... 1 ↑ 1190 / 2 = 595 ... 0 | 逆序排列 595 / 2 = 297 ... 1 | 297 / 2 = 148 ... 1 | 148 / 2 = 74 ... 0 74 / 2 = 37 ... 1 37/ 2 = 18... 1 18 / 2 = 9 ... 1 9/ 2 = 4 ... 1 4 / 2 = 2 ... 0 2 / 2 = 1 ... 0 1 / 2 = 2 ... 1
得整数部分的二进制为10011110110111.
2.针对小数部分0.8125,采取乘2取整,顺序排序
0.8125 * 2 = 1.625 | 0.625 * 2 = 1.25 | 顺序排列 0.25 * 2 = 0.5 | 0.5 * 2 = 1 ↓
得小数部分的二进制为1001.
3.将前面两部分的结果相加,结果为10011110110111.1001;
小心,二进制小数会丢失了精度
根据上面的知识,将十进制0.1转为二进制;
0.1 * 2 = 0.2 0.2 * 2 = 0.4 // 注意这里 0.4 * 2 = 0.8 0.8 * 2 = 1.6 0.6 * 2 = 1.2 0.2 * 2 = 0.4 // 注意这里,循环开始 0.4 * 2 = 0.8 0.8 * 2 = 1.6 0.6 * 2 = 1.2 ...
可以发现有限十进制小数0.1转成无限二进制小数0.00011001100...,可以看到精度在转化过程中丢失了!
由此得出结论能转为有限二进制小数的最后一位必然以5结尾(因为只有0.5*2才能变成整数),其他结尾的都会丢失精度。
推导 0.1 + 0.2 为何等于 0.30000000000000004
可以参考双精度浮点数
阅读后得到以下公式
// 0.1 和 0.2 都转化成二进制后再进行运算 0.00011001100110011001100110011001100110011001100110011010 + 0.0011001100110011001100110011001100110011001100110011010 = 0.0100110011001100110011001100110011001100110011001100111 // 转成十进制正好是 0.30000000000000004
解决方法小计1
// 加 Number.prototype['add'] = function (...arg) { var r1, r2, m, result = this; arg.forEach(value => { try { r1 = result.toString().split(".")[1].length } catch (e) { r1 = 0 } try { r2 = value.toString().split(".")[1].length } catch (e) { r2 = 0 } m = Math.pow(10, Math.max(r1, r2)); result = Math.round(result * m + value * m) / m; }); return result; }; // 减 Number.prototype['sub'] = function (...arg) { var r1, r2, m, result = this; arg.forEach(value => { try { r1 = result.toString().split(".")[1].length } catch (e) { r1 = 0 } try { r2 = value.toString().split(".")[1].length } catch (e) { r2 = 0 } m = Math.pow(10, Math.max(r1, r2)); var n = (r1 >= r2) ? r1 : r2; result = (Math.round(result * m - value * m) / m).toFixed(n); }); return result; }; // 乘 Number.prototype['mul'] = function (...arg) { var result = this; arg.forEach(value => { var m = 0, s1 = result.toString(), s2 = value.toString(); try { m += s1.split(".")[1].length } catch (e) { } try { m += s2.split(".")[1].length } catch (e) { } result = Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m); }); return result; }; // 除 Number.prototype['subtraction'] = function (...arg) { var result = this; arg.forEach(value => { var t1 = 0, t2 = 0, r1, r2; try { t1 = result.toString().split(".")[1].length } catch (e) { } try { t2 = value.toString().split(".")[1].length } catch (e) { } r1 = Number(result.toString().replace(".", "")); r2 = Number(value.toString().replace(".", "")); result = (r1 / r2) * Math.pow(10, t2 - t1); }); return result; };
解决方法小计2
-
传统使用,引入math.js
<!DOCTYPE HTML> <html> <head> <script src="https://unpkg.com/mathjs@7.0.1/dist/math.min.js" type="text/javascript"></script> </head> <body> <script type="text/javascript"> const ans = math.add(0.1, 0.2) // 0.30000000000000004 console.log(math.format(ans, {precision: 14})) // '0.3' console.log(math.sqrt(4).toString()) // 2 </script> </body> </html>
-
es module
npm install mathjs
import { create, all } from 'mathjs' const config = { number: 'BigNumber', precision: 20 } const math = create(all, config); export default { methods: { //开方 numberSqrt: function(arg1){ return math math.sqrt(arg1) }, //除 numberExcept: function (arg1, arg2) { return math.divide(arg1, arg2); }, //乘 numberRide: function (arg1, arg2) { return math.multiply(arg1, arg2); }, //加 numberAdd:function (arg1,arg2) { return math.add(arg1, arg2); } //减 numberSub:function (arg1,arg2) { return math.add(arg1, -arg2); } } }