js 浮點數計算精度不許確問題

或許不少人都遇到過,js 對小數的加、減、乘、除時常常獲得一些奇怪的結果!javascript

好比 :0.1 + 0.2 = 0.3  ?java

這麼一個簡單的計算,當你用js 計算時會發現結果是:0.30000000000000004 。這麼奇葩,簡直沒法理解!編程

那,爲何會這樣呢?瀏覽器

對於浮點數的四則運算,幾乎全部的編程語言都會有相似精度偏差的問題,只是 C++ / C# / Java 這些語言中已經封裝好了方法來避免精度的問題,而javascript 是一門弱類型的語言,從設計思想上就沒有對浮點數有個嚴格的數據類型,因此精度偏差的問題就顯得格外突出。編程語言

那爲何 0.1 + 0.2 這種簡單的運算,計算機還搞不定呢?那是由於,計算機不管作什麼運算,其實都是轉成二進制來運算的,由於它只能讀懂二進制,而不是十進制,因此咱們先把0.1 和 0.2轉換成二進制看看:spa

0.1 => 0.0001 1001 1001 1001... (無限循環)設計

0.2 => 0.0011 0011 0011 0011... (無限循環)code

咱們發現0.1和0.2轉化爲二進制以後,變成了一個無限循環的數字,這在現實生活中,無限循環咱們能夠理解,但計算機是不容許無限循環的,對於無限循環的小數計算機會進行舍入處理。進行雙精度浮點數的小數部分最多支持52位,因此二者相加以後獲得這麼一串0.0100110011001100110011001100110011001100110011001100 因浮點數小數位的限制而截斷的二進制數字,這時候,咱們再把它轉換爲十進制,就成了 0.30000000000000004。blog

找到緣由了,那應該怎麼處理呢?ip

方法一:指定要保留的小數位數(0.1+0.2).toFixed(1) = 0.3;這個方法toFixed是進行四捨五入的也不是很精準,對於計算金額這種嚴謹的問題,不推薦使用,並且不通瀏覽器對toFixed的計算結果也存在差別。

方法二:把須要計算的數字升級(乘以10的n次冪)成計算機可以精確識別的整數,等計算完畢再降級(除以10的n次冪),這是大部分編程語言處理精度差別的通用方法。 

eg: (0.1*10 + 0.2*10) / 10 == 0.3

對於方法二能夠對四則運算分別做封裝:

/* ===== 浮點型數據的加、減、乘、除 ===== */
    add(arg1, arg2) { // 加法
        let 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 (arg1 * m + arg2 * m) / m
    },
    sub(arg1, arg2) { // 減法
        let r1, r2, m, n
        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))
        n = (r1 >= r2) ? r1 : r2
        return ((arg1 * m - arg2 * m) / m).toFixed(n)
    },
    mul(arg1, arg2) { // 乘法
        let m = 0
        let s1 = arg1.toString()
        let s2 = arg2.toString()
        try {
            m += s1.split('.')[1].length
        } catch (e) {
        }
        try {
            m += s2.split('.')[1].length
        } catch (e) {
        }
        return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m)
    },
    div(arg1, arg2) { // 除法
        let t1 = 0
        let t2 = 0
        let r1
        let r2
        try {
            t1 = arg1.toString().split('.')[1].length
        } catch (e) {
        }
        try {
            t2 = arg2.toString().split('.')[1].length
        } catch (e) {
        }
        r1 = Number(arg1.toString().replace('.', ''))
        r2 = Number(arg2.toString().replace('.', ''))
        return (r1 / r2) * Math.pow(10, t2 - t1)
    },
    /* ===== 浮點型數據的加、減、乘、除 ===== */
相關文章
相關標籤/搜索