JS中數值類型的本質

一、JS中的數值類型

眾所JS愛好友周知,JS中只有一個總的數值類型——number,它包含了整型、浮點型等數值類型。其中,浮點數的實現思想有點複雜,它把一個數拆成兩部分來存儲。第一部分是有效位數,也可以稱作係數、分數或者尾數;第二部分被稱為指數,表示小數點位應該插在係數的哪個位置。在JS中,浮點數由IEEE 754標準非完全實現。一個number包含1個符號位,11位指數為和53位有效位數。IEEE 754基於二進位運作,分為兩個部分組成。第一部分包含兩個子部分:符號位和有效位數。符號位在最高位中,該位為1表示該數是負數,為0則表示正數;有效位數在64位的最低幾位中,通常表示一個範圍內的小數。

0.5 <= 有效位數 < 1.0

按照這種表示法,有效位數的最高位理論上始終為1。因此,該位實際上並不需要被存放於number 當中,於是就多出了能用的1位,稱作彩蛋位。

第二部分指數存在於符號位和有效位數之間那些位中。存放號後,可以用如下公式表示一個數值:

數值 = 符號位 * 係數 * (2 ** 指數)

下面來分析一下數值類型的本質。

二、JS中數值類型的本質

在了解JS中數值類型的本質之前,我們先來看一個問題:0.1 + 0.2 = ?

我相信很多人都見過JS中這個經典的計算。是的,0.1 + 0.2 != 0.3,而是等於0.30000000000000004。這是為什麼呢,下面我們就來剖析一下這種現象出現的本質原因。看一下程式碼:

function deconstruct(number) {
    let sign = 1;
    let coefficient = number;
    let exponent = 0;

    //將符號位從係數中提取出來
    if (coefficient < 0) {
        coefficient = -coefficient;
        sign = -1;
    }

    if (Number.isFinite(number) && number !== 0) {
        //-1128 就是 Number.MIN_VALUE的指數減去有效位數再減去彩蛋位的結果
        exponent = -1128;
        let reduction = coefficient;
        //將係數不斷除以 2,直到趨近於 0 為止
        while (reduction !== 0) {
            //將除的次數與-1128相加到exponent
            exponent += 1;
            reduction /= 2;
        }
        //當指數為 0 的時候,可以認為數值是一個整數
        //如果指數不為0,則通過校正係數來使其為 0
        reduction = exponent;
        while (reduction > 0) {
            coefficient /= 2;
            reduction -= 1;
        }

        while (reduction < 0) {
            coefficient *= 2;
            reduction += 1;
        }
    }
    return {
        sign,
        coefficient,
        exponent,
        number
    };
}

現在,我們看看傳入1的結果:

image

根據數值公式計算:1 * 9007199254740992 * (2 ** -53) = 1

這顯然是沒有問題的。

那麼我們將0.1傳入函數呢,會產生怎樣的結果,接著來看。

image

根據數值公式計算:

1 * 7205759403792794 * 2 ** -56 = 0.1000000000000000055511151231257827021181583404541015625

結果出人意料!JS無法精確的處理小數。這是由IEEE 754機制決定的。

因此,0.1 + 0.2 != 0.3很好解釋了。

那麼,我們怎麼在業務中,使得0.1 + 0.2 = 0.3呢?下面給出解決辦法:

image

Number.toFixed()用於保留小數點具體位數進行四捨五入,參數默認為0,則不保留小數。Number.toPrecision()用於保留並四捨五入到指定的數字位數,默認全部保留,參數為0,則保留1位有效數字。注意,以上兩個函數均返回string類型的值,所以在這裡需要將其顯示轉換為number類型。

Tags: