Javascript开发:toFixed方法保留小数最后一位出现随机偏差问题
一、问题如下
起初一直以为toFixed就是js中用于四舍五入保留小数的方法,直到最近遇到一个付款明细计算合计金额的时候出现了保留的最后一位数偏差问题。财务相关的数据是不允许出现金额不一致的问题的,即便是1分钱的差值。于是在网上搜索了许多这个方法相关的问题,发现好像比较特殊。
有的文章说toFixed遵循的是银行家算法,四舍六入五成双:位数很多的近似数当有效位数确定后,后面多余的数字应该舍去,只保留有效数字最末一位。四是指值小于等于时舍去,六是指大于等于6时进一位,五指的是根据5后面的有效数字来定,当5后面有数字时,舍5入1;当5后无有效数字时,需要分两种情况来讲:若5前面的数字时奇数,则舍5入1;若5前面的数字为偶数,则舍去不进。
后来在浏览器中测试了一下,发现貌似实际结果与逻辑并不一样:
上面是将第三位为5的数据,第二位分别从0-9测试保留两位小数后的结果。发现5的前一位为0-4的都进了位,前一位为5-9的都舍去了,显然和上面说的奇进偶不进并不一致。
而将小数点后第一位改为8后:
现在又是5前一位为0-6的都舍去了,前一位为7-9的进一位,逻辑又不同了。说明这里的逻辑并不是所谓的银行家算法。
只能去找ECMAScript规范定义的方法:https://262.ecma-international.org/6.0/#sec-number.prototype.tofixed
页面搜索toFixed,果然,并不是简单的四舍五入这种逻辑,而是一大串不明所以的操作:
意思是,toFixed方法的参数数字如果小于10的21次方的话,就要根据n/10^f-x的方式进行计算,从而决定是进位还是舍去。在参与计算的两个数据中,取更接近0的那个值。
例如:
要处理的数字为1.235,要保留两位小数。 其中,设立数值n1和n2,分别是小数保留进位后和舍去后的两个数字去除小数点后的整数值。则1.235进位后和舍去后分别是1.24和1.23,则n1 = 1.24,n2 = 1.23. f为要保留的位数,保留两位小数,则f = 2. x是要保留小数的原数值,x = 1.235. 分别用n1和n2代入公式进行计算: n1得出的数值为:1.24/10^2-1.235 = -1.2226 n2得出的数值为:1.23/10^2-1.235 = -1.2227 n1的绝对值为1.2226,比n2得出的数值1.2227要小,所以更接近0。因此1.235.toFixed(2)得出的数值为1.24,正是n1的值。
二、问题解决
既然官方的方法不适用四舍五入的业务需求,直接自己加个工具类重定义一个方法,然后全局挂载使用就可以了。
//四舍五入方法,num为处理的数值,point为保留位数 function toFixed(num,point){ //取要保留位数后的一位 var endNum = parseInt(num * Math.pow(10,(point + 1))) % 10; if(endNum <= 4){ return parseInt(num * Math.pow(10,point)) / Math.pow(10,point); }else{ return (parseInt(num * Math.pow(10,point)) + 1) / Math.pow(10,point); } }