相除精度 完美解決JS浮點型的運算精度問題

2021-10-14 16:50:21 字數 3488 閱讀 3006

console.log(0.1 + 0.2);    console.log(2.22 + 0.1);// 2.3200000000000003    console.log(1.0 - 0.9);    console.log(19.9 * 100);    console.log(6.6 / 0.2);
這真是乙個令人黯然蛋碎的bug,而且即使在es6時代依然沒能解決,怎麼辦呢?只能靠自己了。 為什麼出現這個問題呢? 我們生活中用到的十進位制計算,在計算機底層都會將其轉為二進位制運算。我們把0.1轉為二進位制,其實是: 0.1 => 0.0001 1001 1001 1001…(無限迴圈) 這就是浮點型數值計算不準確的原因。

既然浮點型直接計算會出問題,那麼我們就換種思路,可以將其先轉為整型計算,然後再轉回去。 比如0.1+0.2,我們完全可以先都擴大10倍,相加得3,然後除以10,不就得到0.3這個準確數值了麼? 根據這個思路,我們寫出如下**:

(function (number)  catch (e)         try  catch (e)         m = math.pow(10, math.max(r1, r2)) // 按小數字多的那個擴大倍數        var a = this * m         var b = arg * m        return (a + b) / m    }})(number)
**很簡單我們不做過多解釋,直接看結果吧:

console.log(0.1 + 0.2) // 0.30000000000000004console.log((0.1).add(0.2)) // 0.3
ok,貌似大功告成啊?別著急,咱們再看個例子:

console.log(2.22 + 0.1) // 2.3200000000000003console.log((2.22).add(0.1)) // 2.3200000000000003
草草?神馬情況這是?不科學啊~~ 其實我們忽略了乙個重要問題,那就是——浮點乘法也會有精度問題!!

console.log(2.22*100)  * 100);
愁人不?太愁人了!!咋辦呢? 別急,有辦法。 我們的目標,就是要保證擴大倍數後得到的整數一定要準確,我們是否可以通過parseint函式,對乘法結果直接取整呢? 如果是2.22x100,parseint取整得到222,這是準確的;然鵝,19.9x100,取整就是1989,正確的結果應該是1990,這就不對了。 怎麼就能讓它對呢? 咱們可以這樣: 將乘法得到的數值,再加個0.5,然後再取整,以保障整數的準確。 為啥要加0.5呢? 答案是:0.5會保證四捨五入。

console.log(2.22*100+0.5)     console.log(19.9 * 100+0.5);
這樣一來,parseint取整不就對了麼。 好的,我們將之前的**做一下修正:

number.prototype.add = function (arg)  catch (e)         try  catch (e)         m = math.pow(10, math.max(r1, r2))  // 取位數最大的那個        var a = parseint(this * m + 0.5)        var b = parseint(arg * m + 0.5)        return (a + b) / m    }
然後再看看結果:

console.log(0.1 + 0.2);    console.log(2.22 + 0.1);// 2.3200000000000003   console.log((0.1).add(0.2)) // 0.3   console.log((2.22).add(0.1)) // 2.32
好,下面我們繼續把減法、乘法和除法都補全。 減法其實很簡單,加乙個負數不就是減法了嘛~~

number.prototype.sub = function (arg)
然後是乘法。 乘法思路也很簡單,比如3.15x7.2,其實可以這麼計算: (315x72)/1000 對吧? 也就是說,我們可以先去掉他們的小數點,先轉為整數乘法,然後再將結果縮小n倍。 於是我們寫出如下**:

number.prototype.mul = function (arg)  catch (e)         try  catch (e)         return number(s1.replace('.', '')) * number(s2.replace('.', '')) / math.pow(10, m) // 直接去掉小數點,轉為整型計算,最後縮小n倍    }
最後是除法。 我們舉個例子,比如:1.5/0.21,還記得小學除法老師是怎麼教的來著? 是不是把分子分母同時都擴大n倍,然後相除呢?也就是:150/21 但我們前面說過,浮點型直接擴大整數倍,也會產生精度問題,這個問題要注意。 好,順著這個思路,我們可以輕鬆寫出**:

number.prototype.div = function (arg)  catch (e)         try  catch (e)         m = math.pow(10, math.max(r1, r2))        var a = parseint(this * m + 0.5)        var b = parseint(arg * m + 0.5)        return a/b    }
(function (number)  catch (e)         try  catch (e)         m = math.pow(10, math.max(r1, r2))        var a = parseint(this * m + 0.5)        var b = parseint(arg * m + 0.5)        return (a + b) / m    }//減法    number.prototype.sub = function (arg) //乘法    number.prototype.mul = function (arg)  catch (e)         try  catch (e)         return number(s1.replace('.', '')) * number(s2.replace('.', '')) / math.pow(10, m)    }//除法    number.prototype.div = function (arg)  catch (e)         try  catch (e)         m = math.pow(10, math.max(r1, r2))        var a = parseint(this * m + 0.5)        var b = parseint(arg * m + 0.5)        return a/b    }})(number)

浮點型失去精度的解釋

從原理上來講,任何一門語言對於浮點數的計算都是不精確的。因為現在的computer都是基於二進位制數來儲存計算的。例如計算8 3時,computer會轉換為二進位制的加法1000 11 1011,然後再轉換為十進位制數為11。這種演算法對於整數來說是不會產生誤差的 如果不超過計算範圍 而對於浮點數計...

浮點型資料的精度控制問題

time limit 1 sec memory limit 16 mb submit 2380 solved 392 submit status web board 兩點確定一條直線,判斷一條直線與乙個圓的位置關係。輸入中前兩行每行描述乙個點的座標 x,y x,y均為實數。第三行包含三個實數,即圓心...

js浮點數相乘精度丟失的問題

在用vue做專案時,需要以百分比的形式保留兩位小數,展示在頁面上,但遇到了乙個問題 var num data5 data4 data5 data6 tofixed 2 100 npm i decimal.js3.在專案中引入 import decimal from decimal.js 4.解決上述...