比較常見的計(jì)算問題: 0.1 + 0.2 ~= 0.3
天真的我瓮床,以為(a100+b100)/100 就解決問題了
參考計(jì)算方式
function sum(num1,num2){
num1= num1==null?0:num1
num2= num2==null?0:num2
var str1 = num1.toString(),str2=num2.toString(),result,str1Length,str2Length2
try{
str1Length = str1.split('.')[1].length
}catch{
str1Length = 0
}
try{
str2Length = str2.split('.')[1].length
}catch{
str2Length = 0
}
var temp = Math.pow(10,Math.max(str1Length,str2Length))
return (num1*temp+num2*temp)/temp
}
比如解決sim(0.1, 0.2) 是沒問題的, 確實(shí)= 0.3
但是如果是下面這兩個(gè)值相加發(fā)現(xiàn)還是不對:
sum(289.46, 38.48) = 327.93999999999994
發(fā)現(xiàn)其實(shí)够庙,在*100(小數(shù)點(diǎn)的位數(shù))的過程,這個(gè)值也會有精度問題,乘完的結(jié)果因?yàn)椴⒉粫玫秸麛?shù)阿宅,所以相加過后橡类,同樣會丟失精度..
這里就發(fā)現(xiàn)簡單的乘法求整處理可能無法徹底解決這個(gè)問題的,鑒于我這里的背景是金額計(jì)算摆碉,我索性重新寫了一個(gè)sum方法塘匣,主要支持兩位小數(shù)點(diǎn)以內(nèi)的計(jì)算,運(yùn)用tofixed和parseInt的方法來強(qiáng)轉(zhuǎn)整數(shù)巷帝,然后再做加減運(yùn)算忌卤。
修改后的計(jì)算方式
// 提升兩位數(shù)精度的加法, 因?yàn)槭墙痤~相加,所以:參數(shù)只支持最多兩位小數(shù)點(diǎn)
// 目前涉及金額不算非常大數(shù)據(jù)的情況楞泼,如果后期數(shù)據(jù)量大驰徊,可能會考慮引入第三方庫如:decimal.js
// Math.pow(2, 53) 一旦超過此值,js的計(jì)算就會出現(xiàn)誤差
function sumAmount(...args) {
let result = 0;
const toFixTransNumX100 = (n) => {
const strFloat = n.toFixed(2);
const intStr = strFloat.split(".")[0] + strFloat.split(".")[1];
return parseInt(intStr);
};
for (let i = 0; i < args.length; i++) {
// 如果args的小數(shù)點(diǎn)超過兩位堕阔,報(bào)錯(cuò)
if (Number(args[i].toFixed(2)) != args[i]) {
throw new Error("sumAmount: args[i] should be less than 2 decimal");
}
result += toFixTransNumX100(args[i]);
}
if (result > Math.pow(2, 53)) {
throw new Error("sumAmount: result is too big");
}
return result / 100;
}
總結(jié):第二個(gè)方法目前測試過雖然是可行的棍厂,主要是項(xiàng)目上不想引用太多第三方庫。正經(jīng)項(xiàng)目還是推薦用一些開源的庫來解決這類計(jì)算問題超陆,如
https://www.npmjs.com/package/decimal.js牺弹, decimal.js 的核心思想是使用字符串來表示數(shù)字,從而避免 JavaScript 浮點(diǎn)數(shù)的精度問題时呀,并提供高精度的數(shù)學(xué)運(yùn)算功能张漂。它通過字符串操作來實(shí)現(xiàn)各種數(shù)學(xué)運(yùn)算和函數(shù),以確保計(jì)算結(jié)果的精度谨娜,感興趣的可以研究其源碼航攒。
額外的思考,其實(shí)在存這些金額值的時(shí)候趴梢,數(shù)據(jù)庫設(shè)計(jì)金額的時(shí)候漠畜,應(yīng)該設(shè)計(jì)成 “分” 為單位,這樣也可以避免這種問題垢油。