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 。這是為什么呢,下面我們就來剖析一下這種現象出現的本質原因 ??匆幌麓a:
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相加到exponentexponent += 1;reduction /= 2;}//當指數為 0 的時候 , 可以認為數值是一個整數//如果指數不為0,則通過校正系數來使其為 0reduction = exponent;while (reduction > 0) {coefficient /= 2;reduction -= 1;}while (reduction < 0) {coefficient *= 2;reduction += 1;}}return {sign,coefficient,exponent,number};}現在,我們看看傳入1的結果:

JS中數值類型的本質

文章插圖
根據數值公式計算:1 * 9007199254740992 * (2 ** -53) = 1 。
這顯然是沒有問題的 。
那么我們將0.1傳入函數呢,會產生怎樣的結果,接著來看 。
JS中數值類型的本質

文章插圖
根據數值公式計算:
1 * 7205759403792794 * 2 ** -56 = 0.1000000000000000055511151231257827021181583404541015625 。
結果出人意料!JS無法精確的處理小數 。這是由IEEE 754機制決定的 。
因此,0.1 + 0.2 != 0.3很好解釋了 。
那么,我們怎么在業務中,使得0.1 + 0.2 = 0.3呢?下面給出解決辦法:
JS中數值類型的本質

文章插圖
Number.toFixed()用于保留小數點具體位數進行四舍五入,參數默認為0,則不保留小數 。Number.toPrecision()用于保留并四舍五入到指定的數字位數,默認全部保留,參數為0,則保留1位有效數字 。注意,以上兩個函數均返回string類型的值 , 所以在這里需要將其顯示轉換為number類型 。
【JS中數值類型的本質】

    推薦閱讀