原因參考:
The Floating-Point Guide - Basic Answers
[浮點數] IEEE754 , C/C++ 浮點數誤差 @ Edison.X. Blog :: 痞客邦 PIXNET ::
尤其在經過運算後,可能目視便可看到誤差,也可能多出許多非預期的小數位數。
網路上有幾種處理方式,以下用兩個浮點數相加為例。
測試瀏覽器:Chrome 55.0.2883.87 m
例如:
0.1+0.7 (預計結果為0.8) =>0.7999999999999999
處理方式 1:
轉成整數運算後,再轉回小數。
(參考:http://www.w3schools.com/js/js_numbers.asp、http://www.cnblogs.com/konooo/archive/2010/01/23/1654617.html)
「先轉成整數運算後」-->「再轉回小數」
(0.1*10+0.7*10)/10 =>0.8
但發現一個特例
155.2+0.67 (預計結果為155.87) =>155.86999999999998
(155.2*100+0.67*100)/100 =>155.86999999999998 =>「先轉成整數運算後」-->「再轉回小數」,這方法無效
處理方式 2:
轉成整數運算後,緊接著使用 round,再轉回小數。
(參考:http://stackoverflow.com/a/10474055)
「先轉成整數運算後」-->「round處理確保為整數」-->「再轉回小數」
Math.round((155.2*100+0.67*100))/100 =>155.87
參考連結範例,是直接乘上 1e12 倍,看似也正常 Math.round((155.2*1e12+0.67*1e12))/1e12 =>155.87
但 Math.round 直接乘上 1e12 倍再還原,也出現例外
(參考:http://stackoverflow.com/a/13388202)
1234563995.721+12345691212.718+1234568421.5891+12345677093.49284 (預計結果為27160500723.52094) =>27160500723.520943 (IE11顯示 27160500723.520942) Math.round(1234563995.721*100000+12345691212.718*100000+1234568421.5891*100000+12345677093.49284*100000)/100000 =>27160500723.52094 (IE11顯示 27160500723.52094) Math.round(1234563995.721*1000000+12345691212.718*1000000+1234568421.5891*1000000+12345677093.49284*1000000)/1000000 =>27160500723.52094 (IE11顯示 27160500723.52094) Math.round(1234563995.721*1e12+12345691212.718*1e12+1234568421.5891*1e12+12345677093.49284*1e12)/1e12 =>27160500723.520943 (IE11顯示 27160500723.520942) => 小數原本只有5位,但chrome和IE11都變成6位 => 直接乘上 1e12 倍再還原,無法完美得出 27160500723.52094
處理方式 3:
前面 http://stackoverflow.com/a/13388202 參考連結,建議用 toFixed
(1234563995.721+12345691212.718+1234568421.5891+12345677093.49284).toFixed(5) =>"27160500723.52094" (但需注意 toFixed 回傳的結果是字串)
但 toFixed 在某些地方,也會出現不如預期的現象 (參考:http://stackoverflow.com/a/661757)
(1.695).toFixed(2) =>"1.70" (0.695).toFixed(2) (預期結果"0.70") =>"0.69" (但IE11正常顯示 "0.70") => Chrome無法正常四捨五入,但IE11可以正常四捨五入其他特例
(1.2).toFixed(16) =>"1.2000000000000000" (1.2).toFixed(17) =>"1.19999999999999996" (但IE11正常顯示 "1.20000000000000000")
結論:如果著重於相加後,小數位數不會變多
解決方式一:
「先乘上剛剛好能剛好將該小數最大位數變成整數的10倍數」-->「進行相加」-->「round處理確保為整數」-->「再轉回小數」
var floatAdd = function (arg1, arg2) { var r1, r2, m; try { r1 = arg1.toString().split(".")[1].length; } catch (e) { r1 = 0; } try { r2 = arg2.toString().split(".")[1].length; } catch (e) { r2 = 0; } m=Math.pow(10,Math.max(r1,r2)) return Math.round((arg1*m+arg2*m))/m; };
解決方式二:
「小數直接相加」-->「toFixed小數最大位數」
var floatAdd = function (arg1, arg2) { var r1, r2, m; try { r1 = arg1.toString().split(".")[1].length; } catch (e) { r1 = 0; } try { r2 = arg2.toString().split(".")[1].length; } catch (e) { r2 = 0; } m = Math.max(r1, r2); return parseFloat((arg1 + arg2).toFixed(m)); };
解決方式三:
使用The Floating-Point Guide提到的方式 「The Floating-Point Guide - Floating-point cheat sheet for JavaScript」
沒有留言:
張貼留言