JS中toFixed()方法四捨五入的精度問題詳解
目錄
- 踩的坑
- 填坑方法
- 什麼樣的坑?
- 總結
踩的坑
最近工作中,在計算一個商品的折扣價格,有時候總是出現價格會有一分錢的差異,涉及錢的問題都是比較敏感的,經過排查,最後發現竟然是 原生的 toFixed 方法的問題。
好傢伙,這都啥規律啊。。。(⊙o⊙)
填坑方法
先不著急去探究其中的問題,既然發現了問題,那就先把 Bug 修復了先,原生方法用不了,就自己寫一個唄,還不是分分鐘的事情,哈哈哈!
/** * 保留小數點幾位數,自動補零,四捨五入 * @param num: 數值 * @param digit: 小數點後位數 * @returns string */ function myFixed(num,digit) { if(Object.is(parseFloat(num),NaN)) { return console.log(`傳入的值:pmHKFaaW${num}不是一個數字`); } num = parseFloat(num); return (Math.round((num + Number.EPSILON) * Math.pow(10,digit)) / Math.pow(10,digit)).toFixed(digit); }
什麼樣的坑?
好了,既然 Bug 解決完了,下面我們就來探索一下 toFixed 其中的奧祕。
呃...首先,Em...百度一下吧,面向百度工程師,果然一查大把結果,看來是個經典問題了。
經過一番瞭解得知,原來是 toFixed 方法採用的四捨五入,並不是我們理解的字面上的四捨五入。而 toFixed 方法它是採用一種詭異的方法 "四捨六入五取偶" ,也叫銀行家演算法,這是什麼個意思呢?
完整說法:"四捨六入五考慮,五後非零就進一,五後為零看奇偶,五前為偶應捨去,五前為奇要進一"。
大概意思是:當捨去位的數值 ≤4 時捨去,當它 ≥6 時加上,可當它 =5 時,則根據 5 後面的數字來定;當 5 後有非零數字時,舍 5 入 1;當 5 後無有效數字時,需要再分兩種情況:5 前為偶數,舍 5 不進;5 前為奇數,舍 5 入 1 。
根據這個規則,我在瀏覽器上又跑了一些測試,但卻還是感覺不對。
// 五前是偶數,沒有捨去? console.log(1.00000065.toFixed(7)); // 1.0www.cppcns.com000007 錯誤 console.log(1.000000065.toFixed(8)); // 1.00000007 錯誤 // 五前是奇數,沒有進一? console.log(1.00000015.toFixed(7)); // 1.0000001 錯誤 console.log(1.000000015.toFixed(8)); // 1.00000001 錯誤
這到底為啥?真是讓人摸不著頭腦。。。(︶︿︶)
再經過一番探索,總算是有點收穫了,下面就得來看看 ECMAScript 規範對該方法的定義了,有時候迴歸規範才是最靠譜的方式。
上圖是關於整個 toFixed 方法的定義,不過是翻譯後的版本,會有出入但差別不大,也可以點選上面的連結檢視原文,我們主要關注圖中紅框部分,通過公式來計算捨去位數值。
我們下面兩個來舉個栗子,測試一下。
console.log(1.0000005.toFixed(6)); // 1.000001 正確 console.log(1.00000005.toFixed(7)); // 1.0000000 錯誤
首先,根據紅框的條件,x<10^21,1.0000005 與 1.00000005 都是小於 10^21 的,所以我們直接可以使用公式 n / 10^ - x 來玩耍。
我們先用 x=1.0000005 代入公式來看看情況:
// 假設n1 var n1 = 1000000; var x = 1.0000005; var f = 6; console.log((n1 / Math.pow(10,f) - x)); // -5.00000000069889e-7 // 假設n2 var n2 = 1000001; var x = 1.0000005; var f = 6; console.log((n2 / Math.pow(10,f) - x)); // 4.999999998478444e-7
由結果可知,當 n1=1000001 時,得到的結果取最接近 0 的值,故:
console.log(1.0000005.toFixed(6)); // 1.000001 正確
再來試試當 x=1.00000005 代入公式:
// 假設n1 var n1 = 10000000; var x = 1.00000005; var f = 7; console.log((n1 / Math.pow(10,f) - x)); // -4.9999999918171056e-8 // 假設n2 var n2 = 10000001; var x = 1.00000005; var f = 7; console.log((n2 / Math.pow(10,f) - x)); // 5.000000014021566e-8
由結果可知,當 n2=10000001 時,得到的結果取最接近 0 的值,故:
console.log(1.00000005.toFixed(7)); // 1.0000000 錯誤
哎...寫到這裡才發現給自己挖了一個大坑,幹嘛要搞那麼多零,自己都數暈圈了。。。
總的來說,上面例子就是教你如何通過規範定義的公式計算出結果而已,如果你看得懂規範,那麼直接去代入也是沒有問題的。
總結
到此這篇關於JS中toFixed()方法四捨五入精度問題的文章就介紹到這了,更多相關JS toFixed()四捨五入精度問題內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!