问题复现
|
|
问题说明
Javascript采用了IEEE-745浮点数表示法(几乎所有的编程语言都采用),这是一种二进制表示法,可以精确地表示分数,比如1/2,1/8,1/1024。遗憾的是,我们常用的分数(特别是在金融的计算方面)都是十进制分数1/10,1/100等。二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字,上诉代码的中的x和y的值非常接近最终的正确值,这种计算结果可以胜任大多数的计算任务:这个问题也只有在比较两个值是否相等时才会出现。
关于浮点数值计算会产生舍入误差的问题,有一点需要明确:这是使用基于IEEE754数值的浮点计算的通病,ECMAScript并非独此一家;其他使用相同数值格式的语言也存在这个问题。 —–《 JavaScript高级程序设计 》P28
javascript的未来版本或许会支持十进制数字类型以避免这些舍入问题,在这之前,你更愿意使用大整数进行重要的金融计算,例如,要使用整数‘分’而不是使用小数‘元’进行货比单位的运算 —–《 Javascript权威指南 》P37
解决方案
需要注意的是浮点数精确度丢失这个问题在简单的浮点数计算中几乎不会造成什么影响,所以适当的进行toFixed操作如 ( 0.30000000000000004 ).toFixed(2) 就能取得正确值,但 需要额外注意当浮点数进行比较时先进行数值操作。
若需要进行多位数的浮点数精确运算,需把参加运算的数先升级(10的X的次方)到整数,等运算完后再降级(0.1的X的次方)。
123456789101112131415161718192021222324252627282930313233343536//加法Number.prototype.add = function(arg) {var r1, r2, m;try { r1 = this.toString().split(".")[1].length } catch (e) { r1 = 0 }try { r2 = arg.toString().split(".")[1].length } catch (e) { r2 = 0 }m = Math.pow(10, Math.max(r1, r2));return (this * m + arg * m) / m;}//减法Number.prototype.sub = function(arg) {return this.add(-arg);}//乘法Number.prototype.mul = function(arg) {var m = 0,s1 = this.toString(),s2 = arg.toString();try { m += s1.split(".")[1].length } catch (e) {}try { m += s2.split(".")[1].length } catch (e) {}return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);}//除法Number.prototype.div = function(arg) {var t1 = 0,t2 = 0,r1, r2;try { t1 = this.toString().split(".")[1].length } catch (e) {}try { t2 = arg.toString().split(".")[1].length } catch (e) {}with(Math) {r1 = Number(this.toString().replace(".", ""));r2 = Number(arg.toString().replace(".", ""));return (r1 / r2) * pow(10, t2 - t1);}}
文中多出引自如下文章,感谢😊😊
参考资料:
JS 浮点数四则运算精度丢失问题 (3)