欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

首先,我們來(lái)看一個(gè)簡(jiǎn)單的例子:

0.1 + 0.2

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

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

因?yàn)樵谟?jì)算機(jī)內(nèi)部,使用的二進(jìn)制浮點(diǎn)根本就不能準(zhǔn)確地表示像 0.1, 0.2 或 0.3 這樣的數(shù)字。

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

JavaScript 中的數(shù)字都是浮點(diǎn)數(shù),即使看起來(lái)像整數(shù)的數(shù)字也是。這是因?yàn)?JavaScript 使用 IEEE 754 標(biāo)準(zhǔn)來(lái)表示數(shù)字,這種表示方法對(duì)于大多數(shù)情況是足夠的,但在某些情況下可能導(dǎo)致精度丟失。

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

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

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

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

20.24*100
// 2023.9999999999998

我們知道浮點(diǎn)型數(shù)據(jù)類型主要有:?jiǎn)尉萬(wàn)loat、雙精度double。

但是?。?!

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

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

對(duì)于double型數(shù)據(jù)(雙精度浮點(diǎn)數(shù)),其長(zhǎng)度是8個(gè)字節(jié)(大小),右邊52位用來(lái)表示小數(shù)點(diǎn)后面的數(shù)字,中間11位表示e(exponent)小數(shù)點(diǎn)移動(dòng)的位數(shù),左邊一位用來(lái)表示正負(fù)。如圖所示:

解決方法

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

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

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

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

解決方案

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

export class Calc{
    /**
     * 加法運(yùn)算
     * @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);
    }
    
    /**
     * 減法運(yùn)算
     * @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);
    }
    
    /**
     * 除法運(yùn)算
     * @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);
    }
    /**
     * 乘法運(yùn)算
     * @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);
    }
    
    /**
     * 計(jì)算小數(shù)位的長(zhǎng)度
     * @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) { // 十進(jìn)制
                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;
    }
    
    /**
     * 確認(rèn)我們的計(jì)算結(jié)果無(wú)誤,以防萬(wàn)一
     * @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;
    }
}

希望這個(gè)方法能夠幫助到遇到問(wèn)題的小伙伴們。

總結(jié)

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

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

相關(guān)文章

最新評(píng)論