亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript計算出現(xiàn)精度丟失問題的解決方法

 更新時間:2023年11月17日 10:22:16   作者:hikits  
Javascript作為一門大型編程語言,在日常開發(fā)中難免會涉及到大量的數(shù)學(xué)計算,然而,浮點數(shù)在計算過程中可能出現(xiàn)精度的問題,下面我們就來學(xué)習(xí)一下Javascript中高精度計算及其相關(guān)知識吧

前言

Javascript作為一門大型編程語言,在日常開發(fā)中難免會涉及到大量的數(shù)學(xué)計算。然而,浮點數(shù)在計算過程中可能出現(xiàn)精度的問題,因此Javascript提供了一個高精度計算庫來幫助處理復(fù)雜的數(shù)字計算。本文就來介紹一下Javascript高精度計算及其相關(guān)知識。

首先,我們來看一個簡單的例子:

0.1 + 0.2

結(jié)果不是 0.3,而是 0.30000000000000004

可以看到數(shù)字的精度已經(jīng)丟失,雖然結(jié)果相差無幾,但是作為技術(shù)人員,這絕對不可以忽略。 簡單一句話概括解釋為什么你會得到意想不到的結(jié)果:

因為在計算機內(nèi)部,使用的二進制浮點根本就不能準確地表示像 0.1, 0.2 或 0.3 這樣的數(shù)字。

當(dāng)編碼或解釋代碼時,你的 “0.1” 其實已經(jīng)舍入為和該數(shù)字的最接近的數(shù)字,即使在計算發(fā)生之前已經(jīng)會導(dǎo)致小的舍入誤差。

JavaScript 中的數(shù)字都是浮點數(shù),即使看起來像整數(shù)的數(shù)字也是。這是因為 JavaScript 使用 IEEE 754 標準來表示數(shù)字,這種表示方法對于大多數(shù)情況是足夠的,但在某些情況下可能導(dǎo)致精度丟失。

在涉及貨幣或其他需要精確計算的場景中,由于 JavaScript 浮點數(shù)的特性可能導(dǎo)致精度丟失,因此一種常見而有效的解決方案是將數(shù)字轉(zhuǎn)換為整數(shù)進行計算,然后再將結(jié)果轉(zhuǎn)換回浮點數(shù)。這種做法能夠在一定程度上規(guī)避浮點數(shù)運算中可能出現(xiàn)的舍入誤差,尤其在處理金融數(shù)據(jù)等對精確性要求極高的情況下顯得尤為重要。

let num1 = 0.1 * 10; // 轉(zhuǎn)換成整數(shù)進行計算 
let num2 = 0.2 * 10; 
let sum = (num1 + num2) / 10; 
// 轉(zhuǎn)換回浮點數(shù) 
console.log(sum); // 輸出:0.3

通過上面這種方式,我們可以在保留所需精度的同時,規(guī)避掉 JavaScript 浮點數(shù)運算可能引發(fā)的不精確性問題。

但是也會出現(xiàn)其他問題,增加小數(shù)點后面的位數(shù),會出現(xiàn)下面的情況:

20.24*100
// 2023.9999999999998

我們知道浮點型數(shù)據(jù)類型主要有:單精度float、雙精度double。

但是?。?!

JavaScript 存儲小數(shù)和其它語言如 Java 和 Python 都不同,JavaScript 中所有數(shù)字包括整數(shù)和小數(shù)都只有一種類型 即 Number類型 它的實現(xiàn)遵循 IEEE 754 標準,IEEE 754 標準的內(nèi)容都有什么,這個咱不用管,我們只需要記住以下一點:

javascript以64位雙精度浮點數(shù)存儲所有Number類型值,即計算機最多存儲64位二進制數(shù)。

對于double型數(shù)據(jù)(雙精度浮點數(shù)),其長度是8個字節(jié)(大小),右邊52位用來表示小數(shù)點后面的數(shù)字,中間11位表示e(exponent)小數(shù)點移動的位數(shù),左邊一位用來表示正負。如圖所示:

解決方法

Number(parseFloat(20.24*100).toPrecision(16))

存儲二進制時小數(shù)點的偏移量最大為52位,最多可表示的十進制為9007199254740992,對應(yīng)科學(xué)計數(shù)尾數(shù)是 9.007199254740992,這也是 JavaScript 最多能表示的精度。它的長度是 16,所以可以使用 toPrecision(16) 來做精度運算。

通過先轉(zhuǎn)為浮點型計算,然后做精度運算后再轉(zhuǎn)為Number類型即可。

但是不能保證還會不會有其他問題,并且這樣的計算太繁瑣,每次都需要對數(shù)字進行相應(yīng)的處理。

解決方案

我們將處理的計算問題進行統(tǒng)一封裝,可以專門處理精度問題。代碼如下:

export class Calc{
    /**
     * 加法運算
     * @param {number} num1
     * @param {number} num2
     * @returns {*}
     */
    add(num1: number, num2: number): number {
        num1 = Number(num1);
        num2 = Number(num2);
        let dec1: number, dec2: number, times: number;
        try { dec1 = this.countDecimals(num1)+1; } catch (e) { dec1 = 0; }
        try { dec2 = this.countDecimals(num2)+1; } catch (e) { dec2 = 0; }
        times = Math.pow(10, Math.max(dec1, dec2));
        const result = (this.mul(num1, times) + this.mul(num2, times)) / times;
        return this.getCorrectResult("add", num1, num2, result);
    }
    
    /**
     * 減法運算
     * @param {number} num1
     * @param {number} num2
     * @returns {number}
     */
    sub(num1: number, num2: number): number {
        num1 = Number(num1);
        num2 = Number(num2);
        let dec1: number, dec2: number, times: number;
        try { dec1 = this.countDecimals(num1)+1; } catch (e) { dec1 = 0; }
        try { dec2 = this.countDecimals(num2)+1; } catch (e) { dec2 = 0; }
        times = Math.pow(10, Math.max(dec1, dec2));
        const result = Number((this.mul(num1, times) - this.mul(num2, times)) / times);
        return this.getCorrectResult("sub", num1, num2, result);
    }
    
    /**
     * 除法運算
     * @param {number} num1
     * @param {number} num2
     * @returns {number}
     */
    div(num1: number, num2: number): number {
        num1 = Number(num1);
        num2 = Number(num2);
        let t1 = 0, t2 = 0, dec1: number, dec2: number;
        try { t1 = this.countDecimals(num1); } catch (e) { }
        try { t2 = this.countDecimals(num2); } catch (e) { }
        dec1 = this.convertToInt(num1);
        dec2 = this.convertToInt(num2);
        const result = this.mul((dec1 / dec2), Math.pow(10, t2 - t1));
        return this.getCorrectResult("div", num1, num2, result);
    }
    /**
     * 乘法運算
     * @param {number} num1
     * @param {number} num2
     * @returns {number}
     */
    mul(num1: number, num2: number): number {
        num1 = Number(num1);
        num2 = Number(num2);
        let times = 0, s1 = num1.toString(), s2 = num2.toString();
        try { times += this.countDecimals(s1); } catch (e) { }
        try { times += this.countDecimals(s2); } catch (e) { }
        const result = this.convertToInt(s1) * this.convertToInt(s2) / Math.pow(10, times);
        return this.getCorrectResult("mul", num1, num2, result);
    }
    
    /**
     * 計算小數(shù)位的長度
     * @param {*} num
     * @returns {number}
     */
    private countDecimals(num: any): number {
        let len = 0;
        try {
            num = Number(num);
            let str = num.toString().toUpperCase();
            if (str.split('E').length === 2) { // 科學(xué)記數(shù)法
                let isDecimal = false;
                if (str.split('.').length === 2) {
                    str = str.split('.')[1];
                    if (parseInt(str.split('E')[0]) !== 0) {
                        isDecimal = true;
                    }
                }
                let x = str.split('E');
                if (isDecimal) {
                    len = x[0].length;
                }
                len -= parseInt(x[1]);
            } else if (str.split('.').length === 2) { // 十進制
                if (parseInt(str.split('.')[1]) !== 0) {
                    len = str.split('.')[1].length;
                }
            }
        } catch(e) {
            throw e;
        } finally {
            if (isNaN(len) || len < 0) {
                len = 0;
            }
            return len;
        }
    }
    
    /**
     * 將小數(shù)轉(zhuǎn)成整數(shù)
     * @param {*} num
     * @returns {*}
     */
    private convertToInt (num: any): number {
        num = Number(num);
        let newNum = num;
        let times = this.countDecimals(num);
        let temp_num = num.toString().toUpperCase();
        if (temp_num.split('E').length === 2) {
            newNum = Math.round(num * Math.pow(10, times));
        } else {
            newNum = Number(temp_num.replace(".", ""));
        }
        return newNum;
    }
    
    /**
     * 確認我們的計算結(jié)果無誤,以防萬一
     * @param {string} type
     * @param {number} num1
     * @param {number} num2
     * @param {number} result
     * @returns {number}
     */
    private getCorrectResult(type: 'add' | 'sub' | 'div' | 'mul', num1: number, num2: number, result: number): number {
        let temp_result = 0;
        switch (type) {
            case "add":
                temp_result = num1 + num2;
                break;
            case "sub":
                temp_result = num1 - num2;
                break;
            case "div":
                temp_result = num1 / num2;
                break;
            case "mul":
                temp_result = num1 * num2;
                break;
        }
        if (Math.abs(result - temp_result) > 1) {
            return temp_result;
        }
        return result;
    }
}

希望這個方法能夠幫助到遇到問題的小伙伴們。

總結(jié)

JavaScript 中的浮點數(shù)丟失精度問題是由底層表示方式引起的,因此在進行重要的精確計算時需要格外小心。選擇合適的方法,如整數(shù)計算、使用專門的庫或小數(shù)點后截斷,可以幫助我們在實際應(yīng)用中處理這些問題,確保得到精確的結(jié)果。在不同場景中選擇適當(dāng)?shù)姆椒?,是程序員需要謹慎考慮的問題,以避免潛在的錯誤。

到此這篇關(guān)于JavaScript計算出現(xiàn)精度丟失問題的解決方法的文章就介紹到這了,更多相關(guān)JavaScript精度丟失內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論