在做項目的時候疟呐,涉及到金額加減時脆栋,經(jīng)常會出現(xiàn)計算精度的問題打却,常見例子如下:
加法
0.1 + 0.2 = 0.30000000000000004
0.7 + 0.1 = 0.7999999999999999
0.2 + 0.4 = 0.6000000000000001
減法
1.5 - 1.2 = 0.30000000000000004
0.3 - 0.2 = 0.09999999999999998
乘法
0.8 * 3 = 2.4000000000000004
35.41 * 100 = 3540.9999999999995
除法
0.3 / 0.1 = 2.9999999999999996
0.69 / 10 = 0.06899999999999999
在遇到浮點數(shù)運算后出現(xiàn)的精度問題時,起初想等加完之后使用toFixed(2)來解決的叛甫,toFixed()方法可把Number四舍五入為指定小數(shù)位數(shù)的數(shù)字
測試發(fā)現(xiàn)還是存在一定誤差
為什么會產(chǎn)生
JavaScript中所有數(shù)字包括整數(shù)和小數(shù)都只有一種類型 — Number。它的實現(xiàn)遵循 IEEE 754 標(biāo)準杨伙,使用64位固定長度來表示其监,也就是標(biāo)準的 double 雙精度浮點數(shù),
這樣的存儲結(jié)構(gòu)優(yōu)點是可以歸一化處理整數(shù)和小數(shù)限匣,節(jié)省存儲空間抖苦。
64位比特又可分為三個部分:
符號位S:第 1 位是正負數(shù)符號位(sign),0代表正數(shù)米死,1代表負數(shù)
指數(shù)位E:中間的 11 位存儲指數(shù)(exponent)锌历,用來表示次方數(shù)
尾數(shù)位M:最后的 52 位是尾數(shù)(mantissa),超出的部分自動進一舍零
然后當(dāng)javaScript運算時峦筒,會先把十進制的0.1和0.2會被轉(zhuǎn)換成二進制的究西,但是由于浮點數(shù)用二進制表示時是無窮的:
0.1 -> 0.0001 1001 1001 1001...(1100循環(huán))
0.2 -> 0.0011 0011 0011 0011...(0011循環(huán))
IEEE 754 標(biāo)準的 64 位雙精度浮點數(shù)的小數(shù)部分最多支持53位二進制位,所以兩者相加之后得到二進制為:
0.0100110011001100110011001100110011001100110011001100
因浮點數(shù)小數(shù)位的限制而截斷的二進制數(shù)字物喷,再轉(zhuǎn)換為十進制卤材,就成了0.30000000000000004。所以在進行算術(shù)計算時會產(chǎn)生誤差峦失。
如何解決
大概思路就是把浮點數(shù)轉(zhuǎn)化成整數(shù)(乘以10的n次冪)去加減扇丛,然后在降級處理(除以10的n次冪)
例如: 0.1 + 0.2 == 0.3 //false
(0.1*10 + 0.2*10)/10 == 0.3 //true
本以為這樣就能完美解決了,尷尬的事情出行了
18.4 * 100 = 1839.9999999998
所以最終決定把數(shù)值先toString之后通過split方法切割小數(shù)點尉辑,把小數(shù)點位數(shù)補成兩位后帆精,通過字符串拼接的方式拼接起來然后轉(zhuǎn)成數(shù)值進行加減,最后在除以100即可隧魄。
代碼如下:
//補全位數(shù)
function operation(num) {
//如果是整數(shù)的話默認補兩位小數(shù)
if (num) {
num = num.slice(0,2);
return num.length === 1 ? num = num + '0' : num;
} else {
return '00'
}
}
//處理浮點數(shù)想加
var addNUm = function (num1,num2) {
var array1 = num1.toString().split("."),//轉(zhuǎn)化成字符串并已小數(shù)點切割
array2 = num2.toString().split(".");
//如果是整數(shù)的話默認補兩位小數(shù)
array1[1] = operation(array1[1]);
array2[1] = operation(array2[1]);
//把整數(shù)部分和小數(shù)部分拼接起來然后在轉(zhuǎn)成數(shù)字類型
var num1 = Number(array1[0] + array1[1]),
num2 = Number(array2[0] + array2[1]);
//整數(shù)想加之后除以100
return (num1 + num2) / 100;
}
addNUm(0.1,0.2);