一、問題如下
起初一直以爲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); } }