🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 一、问题 在 JavaScript 中整数和浮点数都属于`Number`数据类型,所有数字都是以 64 位浮点数形式储存。 当浮点数做数学运算的时候,你经常会发现一些问题。 ``` 1. // 加法 ===================== 2. 0.1 + 0.2 = 0.30000000000000004 3. 0.7 + 0.1 = 0.7999999999999999 4. 0.2 + 0.4 = 0.6000000000000001 5. 2.22 + 0.1 = 2.3200000000000003 7. // 减法 ===================== 8. 1.5 - 1.2 = 0.30000000000000004 9. 0.3 - 0.2 = 0.09999999999999998 11. // 乘法 ===================== 12. 19.9 \* 100 = 1989.9999999999998 13. 19.9 \* 10 \* 10 = 1990 14. 1306377.64 \* 100 = 130637763.99999999 15. 1306377.64 \* 10 \* 10 = 130637763.99999999 16. 0.7 \* 180 = 125.99999999999999 17. 9.7 \* 100 = 969.9999999999999 18. 39.7 \* 100 = 3970.0000000000005 20. // 除法 ===================== 21. 0.3 / 0.1 = 2.9999999999999996 22. 0.69 / 10 = 0.06899999999999999 ``` ### 二、原因 在 JavaScript 中计算`0.1 + 0.2`时,0.1 和 0.2 会转成二进制,但由于浮点数用二进制表达时是无穷的。例如 ``` 1. 0.1 \-> 0.0001100110011001...(无限) 2. 0.2 \-> 0.0011001100110011...(无限) ``` IEEE 754 标准的 64 位双精度浮点数的小数部分最多支持 53 位二进制位,所以两者相加之后得到二进制为:0.0100110011001100110011001100110011001100110011001100,因浮点数小数位的限制而截断的二进制数字,再转换为十进制,就成了`0.30000000000000004`。所以在进行算术计算时会产生误差。 整数也存在类似的问题。 ``` 1. console.log(19571992547450991); //=> 19571992547450990 2. console.log(19571992547450991\===19571992547450992); //=> true ``` 当然这个问题并不只是在 Javascript 中才会出现,几乎所有的编程语言都采用了 IEEE-745 浮点数表示法,任何使用二进制浮点数的编程语言都会有这个问题,只不过在很多其他语言中已经封装好了方法来避免精度的问题,而 JavaScript 是一门弱类型的语言,从设计思想上就没有对浮点数有个严格的数据类型。 ### 三、相关问题的解决方案 1、引入库,Math.js decimal.js big.js都能很好解决js计算精度问题。 2、格式化数字,保留二位小数等需求。可以用以下封装的函数 ``` function number_format(number, decimals, dec_point, thousands_sep,roundtag) { /* * 参数说明: * number:要格式化的数字 * decimals:保留几位小数 * dec_point:小数点符号 * thousands_sep:千分位符号 * roundtag:舍入参数,默认 "ceil" 向上取,"floor"向下取,"round" 四舍五入 * */ number = (number + '').replace(/[^0-9+-Ee.]/g, ''); roundtag = roundtag || "ceil"; //"ceil","floor","round" var n = !isFinite(+number) ? 0 : +number, prec = !isFinite(+decimals) ? 0 : Math.abs(decimals), sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, dec = (typeof dec_point === 'undefined') ? '.' : dec_point, s = '', toFixedFix = function (n, prec) { var k = Math.pow(10, prec); console.log(); return '' + parseFloat(Math[roundtag](parseFloat((n * k).toFixed(prec*2))).toFixed(prec*2)) / k; }; s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.'); var re = /(-?\d+)(\d{3})/; while (re.test(s[0])) { s[0] = s[0].replace(re, "$1" + sep + "$2"); } if ((s[1] || '').length < prec) { s[1] = s[1] || ''; s[1] += new Array(prec - s[1].length + 1).join('0'); } return s.join(dec); } console.log(number_format(2, 2, ".", ","))//"2.00" console.log(number_format(3.7, 2, ".", ","))//"3.70" console.log(number_format(3, 0, ".", ",")) //"3" console.log(number_format(9.0312, 2, ".", ","))//"9.03" console.log(number_format(9.00, 2, ".", ","))//"9.00" console.log(number_format(39.715001, 2, ".", ",", "floor")) //"39.71" console.log(number_format(9.7, 2, ".", ","))//"9.70" console.log(number_format(39.7, 2, ".", ","))//"39.70" console.log(number_format(9.70001, 2, ".", ","))//"9.71" console.log(number_format(39.70001, 2, ".", ","))//"39.71" console.log(number_format(9996.03, 2, ".", ","))//"9996.03" console.log(number_format(1.797, 3, ".", ",", "floor"))//"1.797" ``` number_format 函数可以设置保留几位小数、保留小数取整方向。 3、封装函数解决数学运算问题 ``` 1. parseFloat((数学表达式).toFixed(digits)); // toFixed() 精度参数须在 0 与20 之间 2. // 运行 3. parseFloat((1.0 - 0.9).toFixed(10)) // 结果为 0.1 4. parseFloat((0.3 / 0.1).toFixed(10)) // 结果为 3 5. parseFloat((9.7 * 100).toFixed(10)) // 结果为 970 6. parseFloat((2.22 + 0.1).toFixed(10)) // 结果为 2.32 ``` 参考[https://www.html.cn/archives/7340](https://www.html.cn/archives/7340)