﻿# 前端工程研究：關於 JavaScript 中 Number 型別的常見地雷與建議作法

在 JavaScript 的型別系統中，數值型別([Number](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Number))應該是數一數二的地雷型別。今天這篇文章，我想來深度探討 Number 型別的各種魔鬼般的細節，也談談 Number 的常見地雷與建議作法。

### 數值的表示法

要透過 JavaScript 表示一個數值，比較常見的語法有：

-   16 進制
    
    ```javascript
    let num = 0xFFF;
    ```
    
-   10 進制
    
    ```javascript
    let num = 100;
    ```
    
-   8 進制
    
    ```javascript
    let num = 0700;
    ```
    
    這裡有點需要注意，從 ES2015 開始，在[嚴格模式](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)下的 JS 就不允許使用這種 8 進制數值表示法 (Octal literals)。
    
    ```javascript
    (function() {
      'use strict';
      let num = 0700;
      console.log(num);
    })();
    ```
    
    ![2020-02-18\_18-32-04](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/2d766040393c9da1d831-74728006-0070bf00-527d-11ea-94f3-537f33930c85.png)
    
    如果要的話，必須改用 ES2015 全新的 8 進制表示法，必須以 `0o` 或 `0O` 為前綴：
    
    ```javascript
    (function() {
      'use strict';
      let num = 0o700;
      console.log(num);
    })();
    ```
    
-   2 進制
    
    使用 2 進制必須以 `0b` 或 `0B` 為前綴，此語法是 ES2015 才新增的表示法：
    
    ```javascript
    let num = 0b1111;
    ```
    
-   科學記號表示法 ([Scientific notation](https://en.wikipedia.org/wiki/Scientific_notation))
    
    以下這段數值等於 10 乘以 10 的 5 次方：
    
    ```javascript
    let num = 10e5;
    ```
    
    以下這段數值等於 3.1415 乘以 10 的 -3 次方：
    
    ```javascript
    let num = 3.1415e-3;
    ```
    
-   無窮大 (Infinity)
    
    正無窮大有兩種表示法，一種是瀏覽器內建的 `Infinity` 全域變數。另一種則是 ECMAScript 定義的 `Number.POSITIVE_INFINITY` 也可以直接使用：
    
    ```javascript
    let num = Infinity;
    let inf = Number.POSITIVE_INFINITY;
    let sho = 1/0; // 更短的正無窮大寫法
    ```
    
    負無窮大也有兩種表示法：
    
    ```javascript
    let num = -Infinity;
    let inf = Number.NEGATIVE_INFINITY;
    let sho = -1/0; // 更短的負無窮大寫法
    ```
    
    在瀏覽器中，這裡的 `Infinity` 其實是 `window` 根物件下的一個屬性，可以被視為全域變數，所以你也可以寫成：
    
    ```javascript
    let num = window.Infinity;
    ```
    
    在 ES2015 版本裡，你可以透過一個 Number 型別內建的 `Number.isFinite()` 函式來判斷該數值物件是否為**有限的數值**！
    
    ```javascript
    Number.isFinite(46872394293) // true
    ```
    
-   NaN (Not a Number)
    
    這是一個相當特別的「數值」物件，從名稱上來看 `NaN` 就是 `Not a Number` 的縮寫，但是透過 `typeof(NaN)` 你又會得到 `"number"` 的結果，代表這是一個 `number` 型別，但本身不是一個數值，你無法對 `NaN` 物件做任何四則運算！
    
    ```javascript
    let num = NaN;
    ```
    
    你要判斷一個變數是否為 `NaN`，絕對不能用以下判斷式：
    
    ```javascript
    let num = NaN;
    num === NaN // false
    ```
    
    要判斷某數是否為 `NaN` 只能透過以下方法：
    
    ```javascript
    let num = NaN;
    window.isNaN(num); // true
    
    // 由於 isNaN 是 window 下的一個屬性，因此可以視為全域的存在，因此可以簡化成以下寫法
    isNaN(num); // true
    ```
    
    在 ES2015 版本裡，你可以透過一個 Number 型別內建的 `Number.isNaN()` 函式來判斷該數值物件是否為**NaN**！
    
    ```javascript
    Number.isNaN(num); // true
    ```
    

> 目前 TC39 有個 [Numeric Separators](https://github.com/tc39/proposal-numeric-separator) 提案已經抵達 Stage 3 階段 ([瀏覽器相容性](https://caniuse.com/#feat=mdn-javascript_grammar_numeric_separators))。這份提案可以讓你在撰寫 **數字** (numeric literals) 的時候，可以加上分隔符號 `_` (底線)，大幅提升可讀性！👍

```javascript
1_000_000_000           // Ah, so a billion
101_475_938.38          // And this is hundreds of millions

let fee = 123_00;       // $123 (12300 cents, apparently)
let fee = 12_300;       // $12,300 (woah, that fee!)
let amount = 12345_00;  // 12,345 (1234500 cents, apparently)
let amount = 123_4500;  // 123.45 (4-fixed financial)
let amount = 1_234_500; // 1,234,500
```

### 數值型別轉換

你可以將任意物件「嘗試轉型」為「數值型別」，但要注意並非每種物件都可以順利轉成 `number` 型別，只要轉不成功就會變成 `NaN` (非數值的數值)！

-   字串轉數字
    
    你可以透過 `Number` 函式來轉換**字串**為**數值**
    
    ```javascript
    Number('3.14')   // 3.14
    Number('100')    // 100
    Number(' ')      // 0
    Number('')       // 0
    Number('a123')   // NaN
    Number('1,000')  // NaN
    ```
    
    也可以透過 `+` (加號) 來轉換數值，這算是一個相當常見的數值轉換技巧：
    
    ```javascript
    +'3.14'   // 3.14
    +'100'    // 100
    +' '      // 0
    +''       // 0
    +'a123'   // NaN
    +'1,000'  // NaN
    ```
    
    另一種常見**字串**轉**數值**的方式，則是使用 [parseInt](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/parseInt) 或 [parseFloat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat) 函式來解析字串。
    
    這裡你必須特別注意這兩個函式的定義：
    
    1.  `parseInt(str [, radix])`
        
        第 2 個參數 `radix` (基數) 代表第 1 個參數的內容代表哪種進制，預設值為 `10` 進制。你可以設定 2 ~ 36 的數字都可以，最大值不能超過 `36` 進制。
        
        > 所謂 36 進制，代表你可以用 `0-9` 與 `A-Z` 等 36 個字元當成數值表示。
        
    2.  `parseFloat(str)`
        
        這個函式只能固定轉換**十進制**的實數。
        
    
    這兩個函式在傳入第 1 個參數時，都要務必確認此參數只能是 **字串** (`string`) 型態，千萬不要傳入 **非字串** 的物件進來，否則你就會得到以下詭異的結果：
    
    ```javascript
    parseInt(0.1) // 0
    parseInt(0.01) // 0
    parseInt(0.001) // 0
    parseInt(0.0001) // 0
    parseInt(0.00001) // 0
    parseInt(0.000001) // 0
    parseInt(0.0000001) // 1
    ```
    
    為什麼數值小到一定程度，就會導致 `parseInt` 的結果發生異常呢？那是因為從 `0.0000001` 開始，該數值會被轉成科學記號表示法，實際上會變成 `1e-7`，而 `parseInt` 需要將**第一個參數**轉為**字串**，因此你傳入的 **字串** 其實是 `"1e-7"`，而對 `parseInt` 來說，他會從字串最左邊開始解析每個認得的數字符號，遇到無法解析的字元就會自動忽略所有剩餘的內容。以這個例子來說，`parseInt` 指任何 `1e-7` 的 `1` 而已，因為預設 `radix` 為 `10` 進制，因此 `e` 這個字元是看不懂的，所以最終結果為 `1`，非常意外吧！ 😅
    
    > 如果要解析複雜的數值字串，可以考慮採用 [Numeral.js](http://numeraljs.com/) 函式庫。
    
-   布林轉數值
    
    使用 `Number(false)` 會將布林值 `false` 轉為數字為 `0`
    
    使用 `Number(true)` 會將布林值 `true` 轉為數字為 `1`
    
    因此，以下運算式就不足為奇，不是嗎？ 😅
    
    ```javascript
    1 < 2 < 3 // true
    ```
    
    ```javascript
    3 > 2 > 1 // false
    ```
    
-   日期轉數值
    
    在 JavaScript 中的 [Date](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Date) 型別用來指向某一個時間點，但事實上在骨子裡儲存的其實是 `number` 型態，你可以透過 `.valueOf()` 方法取得內部的數值：
    
    ```javascript
    new Date(2020, 1, 20, 0, 0, 0).valueOf() // 1582128000000
    ```
    
    也可以透過 `+` (加號) 來轉型：
    
    ```javascript
    +new Date(2020, 1, 20, 0, 0, 0) // 1582128000000
    ```
    
    或是透過 `Number()` 來轉型：
    
    ```javascript
    Number(new Date(2020, 1, 20, 0, 0, 0)) // 1582128000000
    ```
    
-   數字轉字串 (預設為 10 進制)
    
    ```javascript
    let num = 566;
    num.toString(); // "566"
    ```
    
-   數字轉字串 (指定基數)
    
    將數值轉為 16 進制的字串
    
    ```javascript
    let num = 65535;
    num.toString(16); // "ffff"
    ```
    
    將數值轉為 36 進制的字串
    
    ```javascript
    let num = 65535;
    num.toString(36); // "1ekf"
    ```
    

### 整數運算的地雷

JavaScript 採用 [IEEE-754 Floating Point](https://en.wikipedia.org/wiki/IEEE_754) 規定的[浮點數](https://zh.wikipedia.org/wiki/%E6%B5%AE%E7%82%B9%E6%95%B0)計算所有數值，但是有計算上的精準度問題，任何整數數值超過 `2^53-1` (`9007199254740991`) 就會開始產生誤差，這是一個非常地雷的數值邊界，必須特別注意。

由於這個數值非常特別，又稱為**最大安全整數**，因此在 JavaScript 中，有個 `Number.MAX_SAFE_INTEGER` 靜態屬性來代表這個數值：

```javascript
Number.MAX_SAFE_INTEGER == 2**53-1 // true
```

我們可以來看一下超過這個數值的誤差情形，絕對會讓你大開眼界：

```javascript
Number.MAX_SAFE_INTEGER+0 // 9007199254740991
Number.MAX_SAFE_INTEGER+1 // 9007199254740992
Number.MAX_SAFE_INTEGER+2 // 9007199254740992
Number.MAX_SAFE_INTEGER+3 // 9007199254740994
Number.MAX_SAFE_INTEGER+4 // 9007199254740996
Number.MAX_SAFE_INTEGER+5 // 9007199254740996
Number.MAX_SAFE_INTEGER+6 // 9007199254740996
Number.MAX_SAFE_INTEGER+7 // 9007199254740998
Number.MAX_SAFE_INTEGER+8 // 9007199254741000
Number.MAX_SAFE_INTEGER+9 // 9007199254741000
```

> 換句話說，若將 9,007,199,254,740,991 **辛巴威幣**(Zimbabwean dollar)換算成**美金**大約是 24,888,641,212,326.99 美元 (24 trillion USD)，你如果用 JavaScript 來計算「整數金額」的話，這輩子大概沒機會算錯！XDD

所以只要現有的 `number` 數值超過 `Number.MAX_SAFE_INTEGER` 的話，建議就不要再算下去了。而且比對 `=` (等於) `>` (大於) `<` (小於) 也都會出現問題。如果我們再將整數加大一點，此時 JavaScript 更會將**整數數值**轉為**科學記號表示法**，如此一來數值就更不精準，四則運算就只能取「概略」的數值，不能做精密的計算。你可以參考以下範例：

```javascript
999999999999999         // 999999999999999
9999999999999999        // 10000000000000000
10000000000000000 + 1.1 // 10000000000000002

9999999999999999999999 + 100 === 9999999999999999999999 // true
```

在 ES2015 版本裡，你可以透過一個 Number 型別內建的 `Number.isSafeInteger()` 函式來判斷該數值物件是否為**安全整數**！

```javascript
Number.isSafeInteger(46872394293) // true
Number.isSafeInteger(-4362794324) // true
```

在 ES2015 版本裡，你可以透過一個 Number 型別內建的 `Number.isInteger()` 函式來判斷該數值物件是否為**整數**！

```javascript
Number.isInteger(4687239); // true
Number.isInteger(4687239.4293); // false
```

### 超大整數 (BigInt)

由於 JavaScript 擁有**最大安全整數**的限制，各家瀏覽器也陸續推出解決方案，例如 Google Chrome 瀏覽器從 `Chrome 67` 版本開始，內建了 [BigInt](https://tc39.es/proposal-bigint/) 型別。這是一個目前還在 **TC39 Stage 4** 的提案階段，但大部分現代主流瀏覽器都已經內建 `bigint` 型別，只有 IE11 與 Safari 不支援！(業內經常戲稱 Safari 是下一代 IE 並不是沒有道理) (如果想看瀏覽器相容性報告可以[點擊這裡](https://caniuse.com/#feat=bigint))

要透過 JavaScript 表示一個 `bigint` 數值，只要在整數數值後面加上一個 `n` 即可，如下範例：

```javascript
let num = 39837212195743250943287503298475432n
typeof(num); // bigint
```

如果要對 `bigint` 進行四則運算，必須確保最終計算結果也必須為「整數」才行，而且不能與傳統 `number` 型別混用。例如：

```javascript
50000n/2n === 25000n
```

如果你想要嘗試 `bigint` 超大整數的**上限**，可以試試 2 的 50000 次方，即便這麼大的整數，依然可以順利計算出結果，而且完全沒有誤差，其結果將有 `15052` 個位數，相當過癮！

```javascript
2n**50000n
```

如果要將一個超大整數的字串轉換為 BigInt 型別，可以參考以下寫法：

```javascript
BigInt('39837212195743250943287503298475432')
```

我們可以比對一下傳統 `number` 與 `bigint` 執行時的差異：

```javascript
39837212195743250943287503298475432  // 3.983721219574325e+34
39837212195743250943287503298475432n // 39837212195743250943287503298475432n
```

![2020-02-20\_01-32-51](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/bfcf40924e4ee875ce7f-74858839-f20bcb80-5380-11ea-9381-8eba1bed2ff8.png)

以下則是一些錯誤的 `bigint` 用法：

```javascript
BigInt(999999999999999999999999) // 999999999999999983222784n
BigInt(1.5)                      // RangeError: 不能有小數
BigInt('1.5')                    // SyntaxError: 語法錯誤
1 + 1n                           // TypeError: 不能混用型別
new BigInt(123)                  // TypeError: 不能用 new 建立物件
```

### 浮點數運算的地雷

江湖中有一句話是這樣說的：

> 算錢用浮點，遲早被人扁！

沒錯，由於 JavaScript 使用[浮點數](https://zh.wikipedia.org/wiki/%E6%B5%AE%E7%82%B9%E6%95%B0)計算所有數值，主要原因還是在於計算機最底層都採用 2 進制描述所有數值，而**二進制**大於 `0` 的最小整數為 `1`，如果想表示 `0.5` 怎麼辦？我們可以利用 Number 型別的 `toString(radix)` 方法，將**十進制**的數值表示法轉換為**二進制**，如下範例：

```javascript
(1).toString(2)        // 1
(0.5).toString(2)      // 0.1
(0.25).toString(2)     // 0.01
(0.125).toString(2)    // 0.001
(0.0625).toString(2)   // 0.0001
(0.03125).toString(2)  // 0.00001
```

你可以想像一下，如果想用**二進制**表示**十進制**的 `0.1` 要怎樣寫？

```javascript
(0.1).toString(2) // "0.0001100110011001100110011001100110011001100110011001101"
```

那用**二進制**表示**十進制**的 `0.2` 呢？

```javascript
(0.2).toString(2) // "0.001100110011001100110011001100110011001100110011001101"
```

是的，**十進制**的 `0.1` 與 `0.2` 換算成**二進制**之後，都是**無法精準表達**的數值。

若你想透過 JavaScript 的浮點數運算特性，將 `0.1` + `0.2` 的話，就會得到一個相當詭異的結果：

```javascript
0.1 + 0.2 != 0.3
0.2 + 0.4 != 0.6
0.3 + 0.6 != 0.9
0.4 + 0.8 != 1.2
```

實際上計算的結果是：

```javascript
0.1 + 0.2 // 0.30000000000000004
0.2 + 0.4 // 0.6000000000000001
0.3 + 0.6 // 0.8999999999999999
0.4 + 0.8 // 1.2000000000000002
```

所以說啊，這不叫地雷，什麼才叫地雷？ 😂

其實這種問題，還可以衍生出更多小數計算的問題，例如：

```javascript
Math.ceil(0.1*0.2*100)   // 3
Math.ceil(0.1*0.2*1000)  // 21
Math.ceil(0.1*0.2*10000) // 201
```

看到這裡，你應該可以知道 `算錢用浮點，遲早被人扁！` 真正的涵義了吧？！ 😅

> 目前 TC39 也有個 [BigDecimal](https://github.com/tc39/proposal-decimal) 的提案正在 Stage 1 階段，可以讓你用**十進制**精準無誤差的計算小數。他跟 BigInt 的用法很像，不過這算是非常早期的提案，也還沒有瀏覽器支援，但值得期待！ 👍

> 如果不想等瀏覽器內建 `BigDecimal` 型別，可以考慮使用 [decimal.js](http://mikemcl.github.io/decimal.js/) 函式庫。

### 數值型別的最大值與最小值

我們稍早有看到一個 Number 型別有 **最大整數** 的限制，也有 **正無窮大** 與 **負無窮大** 的數值存在。但在 Number 型別中，還有兩個特別的**靜態屬性**叫做 `Number.MAX_VALUE` 與 `Number.MIN_VALUE`，其值如下：

```javascript
Number.MAX_VALUE == 1.7976931348623157e+308
Number.MIN_VALUE == 5e-324
```

一般人不特別看文件的話，一定會將 `Number.MIN_VALUE` 想像成是一個最小的數值，大部分的人都會覺得是一個 **非常小的負數**，這是一個非常普遍的認知誤差。

事實上，`Number.MIN_VALUE` 其實是一個**超級超級小**的**正數**，而 `5e-324` 其實是 5 乘以 10 的 -324 次方的結果，所以你會得到以下結果：

```javascript
Number.MIN_VALUE > 0 // true
```

而在 Math 物件中，有兩個**取最大值**與**取最小值**的函式，其**正常的使用方式**如下：

```javascript
Math.max(2, 4, 6, 8); // 8
Math.min(1, 3, 5, 7); // 1
```

但有人發現底下這種不帶參數的寫法，非常讓人匪夷所思，這怎麼可能呢？

```javascript
Math.max() > Math.min(); // -> false
Math.min() > Math.max(); // -> true
```

從可讀性的角度上來看，這種程式碼不但沒有意義，還很容易誤導大家理解。

其實 `Math.max()` 的意思非常明顯，就是取得參數中的「最大值」，但是在你沒有傳入任何數值時，最大的數值就是 **負無窮大**。相反的，`Math.min()` 是取得參數中的「最小值」，但是在你沒有傳入任何數值時，最小的數值就是 **正無窮大**。這真的是跌破大家眼鏡！👓

這部分語言特性在 [ECMAScript 5.1](http://www.ecma-international.org/ecma-262/5.1/) 規格書的 [15.8.2.11 max(\[value1\[,value2\[,…\]\]\])](http://www.ecma-international.org/ecma-262/5.1/#sec-15.8.2.11) 章節中，有非常明確的說明。

### 其他技巧

-   自動取整數
    
    ```javascript
    parseInt(2.99)
    ```
    
    > 請注意：由於 `parseInt` 的第一個參數必須傳入**字串型別**的物件，若傳入的是一個 `number` 的話，預設會自動先轉成字串，才會開始進行解析。
    
    ```javascript
    ~~2.99
    ```
    
    > 因為 `~` 是一種**位元運算子**，所有**位元運算子**的**運算元**皆會被轉換成**二補數系統**下的**帶號32位元整數**。所以，如果你使用 `~~` 技巧來取整數，須注意數值不可**大於等於** 2 的 31 次方 (`2147483648`)，否則會發生溢位，而導致結果為負數！(詳見 [MDN](https://developer.mozilla.org/zh-TW/) 的 [位元運算子](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators) 說明)
    
-   自動取小數幾位
    
    ```javascript
    parseInt(2.99 * 10) / 10
    ```
    
    ```javascript
    +(2.999).toFixed(3)
    ```
    
-   四捨五入 (負數超過 0.5 才會進位)
    
    ```javascript
    Math.round(2.5) == 3
    Math.round(-2.5) == -2
    Math.round(-2.51) == -3
    ```
    
-   無條件捨去 (小於自己的最大整數)
    
    ```javascript
    Math.floor(2.99) == 2
    ```
    
-   無條件進位 (大於自己的最小整數)
    
    ```javascript
    Math.ceil(2.11) == 3
    ```
    

### 結論

在 JavaScript 中使用 `number` 型別，無論是「整數」或「小數」都有不少地雷，任何一位 JavaScript 開發人員都應該特別深入理解 `number` 型別的特性與限制，否則未來真的遇到問題時，只會覺得丈二金剛摸不著頭緒。

如果真的不想面對這些地雷，建議多多利用網路上現成的函式庫來處理數值，透過成熟穩定的數值函式庫，可以幫助你寫出不容易出問題的代碼，無論四則運算或解析字串格式的數值，都比較不容易出錯。

我們寫程式的時候，最重要的就是維持程式碼的可讀性，如果過多的開發技巧會干擾程式碼閱讀，那麼就非常建議透過**自定函式庫**或引用**外部函式庫**來處理數值計算，減少維護程式碼的難度，也提升程式碼維護性。以下是相關函式庫推薦：

-   [Numeral.js](http://numeraljs.com/) ( [GitHub](https://github.com/adamwdraper/Numeral-js) )
    
    ```javascript
    numeral('10,000.12').value()  // 10000.12
    numeral('$10,000.00').value() // 10000
    
    numeral(974)          // 974
    numeral(0.12345)      // 0.12345
    numeral('10,000.12')  // 10000.12
    numeral('23rd')       // 23
    numeral('$10,000.00') // 10000
    numeral('100B')       // 100
    numeral('3.467TB')    // 3467000000000
    numeral('-76%')       // -0.76
    numeral('2:23:57')    // NaN
    ```
    
-   [decimal.js](http://mikemcl.github.io/decimal.js/) ( [GitHub](https://github.com/MikeMcl/decimal.js/) )
    
    ```javascript
    +Decimal(0.1).add(Decimal(0.2))          // 0.3
    
    x = new Decimal(9)                       // '9'
    y = new Decimal(x)                       // '9'
    
    new Decimal('5032485723458348569331745.33434346346912144534543')
    new Decimal('4.321e+4')                  // '43210'
    new Decimal('-735.0918e-430')            // '-7.350918e-428'
    new Decimal('5.6700000')                 // '5.67'
    new Decimal(Infinity)                    // 'Infinity'
    new Decimal(NaN)                         // 'NaN'
    new Decimal('.5')                        // '0.5'
    new Decimal('-0b10110100.1')             // '-180.5'
    new Decimal('0xff.8')                    // '255.5'
    
    new Decimal(0.046875)                    // '0.046875'
    new Decimal('0.046875000000')            // '0.046875'
    
    new Decimal(4.6875e-2)                   // '0.046875'
    new Decimal('468.75e-4')                 // '0.046875'
    
    new Decimal('0b0.000011')                // '0.046875'
    new Decimal('0o0.03')                    // '0.046875'
    new Decimal('0x0.0c')                    // '0.046875'
    
    new Decimal('0b1.1p-5')                  // '0.046875'
    new Decimal('0o1.4p-5')                  // '0.046875'
    new Decimal('0x1.8p-5')                  // '0.046875'
    ```
    
-   [bignumber.js](http://mikemcl.github.io/bignumber.js/) ( [GitHub](https://github.com/MikeMcl/bignumber.js) )
    
    ```javascript
    new BigNumber(43210)                       // '43210'
    new BigNumber('4.321e+4')                  // '43210'
    new BigNumber('-735.0918e-430')            // '-7.350918e-428'
    new BigNumber('123412421.234324', 5)       // '607236.557696'
    new BigNumber('-Infinity')                 // '-Infinity'
    
    new BigNumber(NaN)                         // 'NaN'
    new BigNumber(-0)                          // '0'
    new BigNumber('.5')                        // '0.5'
    new BigNumber('+2')                        // '2'
    
    new BigNumber(-10110100.1, 2)              // '-180.5'
    new BigNumber('-0b10110100.1')             // '-180.5'
    new BigNumber('ff.8', 16)                  // '255.5'
    new BigNumber('0xff.8')                    // '255.5'
    
    BigNumber.config({ DECIMAL_PLACES: 5 })
    new BigNumber(1.23456789)                  // '1.23456789'
    new BigNumber(1.23456789, 10)              // '1.23457'
    
    new BigNumber('5032485723458348569331745.33434346346912144534543')
    new BigNumber('4.321e10000000')
    
    new BigNumber('.1*')                       // 'NaN'
    new BigNumber('blurgh')                    // 'NaN'
    new BigNumber(9, 2)                        // 'NaN'
    ```
    
-   [big.js](http://mikemcl.github.io/big.js/) ( [GitHub](https://github.com/MikeMcl/big.js/) )
    
    ```javascript
    x = new Big(9)                       // '9'
    y = new Big(x)                       // '9'
    Big(435.345)                         // 'new' is optional
    new Big('5032485723458348569331745.33434346346912144534543')
    new Big('4.321e+4')                  // '43210'
    new Big('-735.0918e-430')            // '-7.350918e-428'
    ```
    
-   [What is the difference between big.js, bignumber.js and decimal.js?](https://github.com/MikeMcl/big.js/wiki)
    

### 相關連結

-   MDN
    -   [Number - JavaScript](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Number)
    -   [Math - JavaScript](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Math)
-   Spec
    -   [ECMAScript® 2019 Language Specification - 6.1.6 The Number Type](https://www.ecma-international.org/ecma-262/#sec-ecmascript-language-types-number-type)
    -   [IEEE 754 - Wikipedia](https://en.wikipedia.org/wiki/IEEE_754)
-   Others
    -   [ES6 入门教程 - ECMAScript 6入门](https://es6.ruanyifeng.com/)
    -   [https://0.30000000000000004.com](https://0.30000000000000004.com/)
    -   [Why is Math.max() less than Math.min()? -- Charlie Harvey](https://charlieharvey.org.uk/page/why_math_max_is_less_than_math_min)
    -   [denysdovhan/wtfjs: A list of funny and tricky JavaScript examples](https://github.com/denysdovhan/wtfjs)
