toFixed()與銀行家舍入
一直在用
toFixed()方法做浮點數的舍入取值,如果只是客戶端展示資料是沒有多大問題的,但是如果涉及到和后端互交,資料的精度可能會導致介面對接失敗,當然了,涉及安全性的數值,比如金額之類的不應該放在前端計算,應該以后端為準,少數情況下如果需要的時候,則需要修復其精度
1.出現問題的場景
- 首先,我們發現在ie瀏覽器與其他的主流瀏覽器中,由于二進制下浮點數的存盤問題,
toFixed()的行為是不一樣的,也說明了各瀏覽器廠家的做法不一致,
在ie11中:
0.015.toFixed(2)
// 列印結果:"0.02"
在chrome中:
0.015.toFixed(2)
// 列印結果:"0.01"
對于此類問題,部分場景是不可接受的,測驗可能會打出一個兼容性的bug
- 其次,我們雖然知道,
toFixed()是一種銀行家舍入,但是他確是入五取單,而不是銀行家舍入所說的入五取雙,同樣是銀行家舍入,此函式計算精度與后臺java等語言的銀行家舍入精度不同,可能照成介面驗證失敗,
在前端瀏覽器中:
0.025.toFixed(2)
// 列印結果:"0.03"
0.035.toFixed(2)
// 列印結果:"0.03"
在后端服務器中:
0.025.setScale(2,RoundingMode.HALF_EVEN)
// 列印結果:"0.02"
0.035.setScale(2,RoundingMode.HALF_EVEN)
// 列印結果:"0.04"
事實上,正確的做法應該是:有關金額的運算應該完全交由后端計算,然后交給前端展示
2.修復方案
方案很簡單,我們只要重寫Number.prototype.toFixed方法就可以了,
- 如果我們需要普通的四舍五入:
/* eslint-disable no-extend-native */ // 規避eslint不可修改原型報錯
Number.prototype.originalToFixed = Number.prototype.toFixed // 保留原方法
Number.prototype.toFixed = function(length = 0) { // 默認保留0位小數
let result
const thisNum = this * Math.pow(10, length) // 將資料化為整數,方便處理
const thisNumList = thisNum.toString().split('.') // 分離整數與小數
if (thisNumList.length === 1) result = thisNum.toString() // 如果只有整數,則不需要處理
else {
if (Number(thisNumList[1][0]) >= 5) result = (Number(thisNumList[0]) + 1).toString() // 五入
else (Number(thisNumList[1][0]) < 5) result = thisNumList[0] // 四舍
}
if (length === 0) return result
else {
while (result.length < length + 1) { // 如果位數不夠,則用0補齊
result = '0' + result
}
return result.slice(0, (result.length - length)) + '.' + result.slice(result.length - length)
}
}
- 如果我們需要使用正常的銀行家舍入:
/* eslint-disable no-extend-native */ // 規避eslint不可修改原型報錯
Number.prototype.originalToFixed = Number.prototype.toFixed // 保留原方法
Number.prototype.toFixed = function(length = 0) { // 默認保留0位小數
let result
const thisNum = this * Math.pow(10, length) // 將資料化為整數,方便處理
const thisNumList = thisNum.toString().split('.') // 分離整數與小數
if (thisNumList.length === 1) result = thisNum.toString() // 如果只有整數,則不需要處理
else {
if (Number(thisNumList[1][0]) > 5) result = (Number(thisNumList[0]) + 1).toString() // 六入
else if (Number(thisNumList[1][0]) < 5) result = thisNumList[0] // 四舍
else { // 判斷5的情況
if (thisNumList[1].length > 1) result = (Number(thisNumList[0]) + 1).toString() // 如果5后還有位數則入
else {
if (Number(thisNumList[0][thisNumList[0].length - 1]) % 2 === 0) result = thisNumList[0] // 五前為偶應舍去
else result = (Number(thisNumList[0]) + 1).toString() // 五前為奇要進一
}
}
}
if (length === 0) return result
else {
while (result.length < length + 1) { // 如果位數不夠,則用0補齊
result = '0' + result
}
return result.slice(0, (result.length - length)) + '.' + result.slice(result.length - length)
}
}
這樣就統一了舍入的精度,可以根據后臺需要選擇,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/249767.html
標籤:其他
上一篇:用js實作列印九九乘法表
下一篇:js函式function(一)
