在Node.js應用程序中處理大數的操作指南
如何在 Node.js 應用程序中處理大數
計算機很難在不損失精度的情況下準確地表示具有很多個有效數字的數字。當你將超過 JavaScript 中最大安全整數限制的整數存儲為普通整數時,它們會失去精度。
在 JavaScript 生態(tài)系統(tǒng)中,你可以使用 BigInt 來處理大整數。但是,你也可以使用具有類似于 BigInt 功能的第三方庫。
本文將是使用 BigInt 和提供類似功能的流行庫管理大數的完整指南。我們還將比較第三方庫的用例、優(yōu)勢和劣勢。
JavaScript 如何編碼數字?
表示大數時精度損失的挑戰(zhàn)并不是 JavaScript 獨有的。在內部,JavaScript 使用雙精度二進制浮點格式來表示數字。
雙精度二進制浮點格式
雙精度二進制浮點格式由 IEEE 標準 754 定義。它使用 64 位來表示帶符號的浮點數。用雙精度二進制浮點數表示的數由符號、尾數和指數三部分組成,如下圖所示:

雙精度二進制浮點格式將 64 位分配給這三部分。它使用 1 位編碼符號,11 位編碼偏置指數,52 位編碼尾數或尾數。
下面的示例顯示了十進制數 -1.7976931348623157e+308 的內部雙精度二進制浮點數表示。我使用了 • 字符來分隔這三個部分的編碼。
第一位編碼符號。因為我們正在編碼一個負數,所以它的值為一。如果我們編碼一個正數,它的值將為零。隨后的 11 位編碼偏置指數,最后 52 位編碼尾數:
1•11111111110•1111111111111111111111111111111111111111111111111111
計算機只能理解二進制。因此,在存儲或執(zhí)行數學運算之前,JavaScript 在內部將每個數字轉換為雙精度二進制浮點格式,如上例所示。
然而,某些數字無法準確地用二進制表示。因此,當你將某些數字從十進制轉換為二進制再轉換回十進制時,它們會失去精度。
同樣,JavaScript 使用固定位數對雙精度二進制浮點數的不同部分進行編碼。因此,在處理大整數時,你需要使用第三方庫或內置的 bigint 類型。
JavaScript 中的最小和最大安全整數
由于雙精度格式將表示尾數的位數限制為 53,因此你可以使用的 JavaScript 整數的精度和準確性存在限制。
你可以在不丟失精度的情況下使用的最大安全整數是 2 ** 53 - 1。它也是使用 Number.MAX_SAFE_INTEGER 訪問 Number 構造函數的靜態(tài)數據屬性:
console.log(2 ** 53 - 1 === Number.MAX_SAFE_INTEGER) // true
還有一個對應的最小安全整數,其值為-(2**53-1)。你可以使用 Number.MIN_SAFE_INTEGER 靜態(tài)屬性訪問它的值:
console.log(-(2 ** 53 - 1) === Number.MIN_SAFE_INTEGER) // true
你執(zhí)行的任何涉及大于最大安全整數或小于最小安全整數的整數的數學運算都將導致意外的近似結果:
const maxSafeInteger = Number.MAX_SAFE_INTEGER; const minSafeInteger = Number.MIN_SAFE_INTEGER; console.log(maxSafeInteger + 1); // 9007199254740992 console.log(maxSafeInteger + 2); // 9007199254740992 console.log(maxSafeInteger + 1 === maxSafeInteger + 2); // true console.log(minSafeInteger - 1); // -9007199254740992 console.log(minSafeInteger - 2); // -9007199254740992 console.log(minSafeInteger - 1 === minSafeInteger - 2); // true
JavaScript 中的正無窮大和負無窮大
就像上面的最小和最大安全整數一樣,JavaScript 有一個它可以在內部表示的最大數值。該值為 2 ** 2014 - 1。你可以使用 Number.MAX_VALUE 數據屬性訪問它。
JavaScript 使用 Infinity 表示任何超過 Number.MAX_VALUE 的數值,并使用 -Infinity 表示相應的負值,如下例所示:
console.log(Number.MAX_VALUE * 2); // Infinity console.log(Number.MAX_VALUE * 3); // Infinity console.log(-Number.MAX_VALUE * 3); // -Infinity
盡管 Infinity 在 Node 中是全局的,但你可以使用 Number.POSITIVE_INFINITY 數據屬性訪問它,并使用 Number.NEGATIVE_INFINITY 數據屬性訪問 -Infinity 。
如何使用 BigInt 在 JavaScript 中管理大整數
正如前面部分所介紹的那樣,JavaScript 在內部使用雙精度格式來表示數字。因為它使用 53 位來編碼尾數,所以你可以在 JavaScript 中使用的最大安全整數是 2**53 - 1。
要安全地處理大于最大安全整數的整數,你需要 bigint 類型。它是在不損失精度的情況下操作大整數的內置功能。
你可以通過將 n 附加到整數或使用 BigInt 函數來創(chuàng)建 bigint 類型。因為 BigInt 不是構造函數,所以不使用 new 關鍵字調用它,如下面的示例所示:
const number = 1n; console.log(1n + 2n); // 3n const maxSafeInt = BigInt(Number.MAX_SAFE_INTEGER); console.log(maxSafeInt + 1n); // 9007199254740992n console.log(maxSafeInt + 2n); // 9007199254740993n console.log(maxSafeInt * maxSafeInt); // 81129638414606663681390495662081n
與普通的數字類型不同,你不能將內置的 Math 方法與 BigInt 值一起使用。但是,你可以使用 bigint 類型執(zhí)行基本的數學運算,例如加法、減法和求冪:
console.log(2n + 3n) // 5n console.log(2n - 3n) // -1n console.log(2n ** 3n) // 8n console.log(4n % 3n) // 1n console.log(BigInt(3) - 4n) // -1n
由于你只能使用 bigint 類型執(zhí)行基本的數學運算,因此在 JavaScript 中處理大量數字時,你可能需要為某些用例使用第三方庫。
在 JavaScript 中管理大數的庫
除了內置的 bigint 類型之外,還有幾個第三方庫可以在 JavaScript 中處理大數字。其中一些庫附帶了 BigInt 可能不提供的解決方案。
但是,與任何第三方庫一樣,使用它們也有缺點。它們伴隨著額外的打包大小、維護、安全和許可問題。
使用 Math.js 管理大數
Math.js 是一個免費、開源且功能豐富的數學庫。你既可以在瀏覽器中使用它,也可以在 Node 運行時環(huán)境中使用它。
雖然它是一個功能豐富的庫,但在本文中,我們將使用 Math.js 在 Node 運行時環(huán)境中管理大數。用你的包管理器 npm 安裝它,如下所示:
# npm npm i mathjs # yarn yarn add mathjs #pnpm pnpm add mathjs
安裝 Math.js 后,你可以使用默認配置加載和使用它,如下例所示:
const { add, subtract, evaluate }= require('mathjs');
const sum = add(2, 3);
const difference = subtract(2, 3);
const anotherSum = evaluate('2 + 3');
console.log(sum); // 5
console.log(difference}); // -1
console.log(anotherSum}); // 5你可以使用自定義配置創(chuàng)建一個 Math.js 實例,而不是使用默認配置的 Math.js 內置函數:
const { create, all } = require("mathjs");
const config = {};
const math = create(all, config);
console.log(math.add(2, 3)); // 5
console.log(math.pow(2, 3)); // 8
console.log(math.divide(4, 2)); // 2
console.log(math.multiply(2, 3)); // 6Math.js 具有專門用于處理大數的 BigNumber 數據類型。
在上面的一個部分中,我們強調了在使用內置數字類型時,JavaScript 使用 Infinity 表示超過最大可表示數值的數字。
使用 Math.js,你可以表示超過最大可表示數的數字并對它們執(zhí)行數學運算。但是,請注意,對 BigNumber 類型執(zhí)行數學運算比對普通數字類型執(zhí)行數學運算要慢:
const { create, all } = require("mathjs");
const config = {};
const math = create(all, config);
const maxValue = math.bignumber(Number.MAX_VALUE);
console.log(math.add(maxValue, maxValue)); // 3.5953862697246314e+308
const maxSafeInt = math.bignumber(Number.MAX_SAFE_INTEGER);
console.log(math.square(maxSafeInt)); // 8.1129638414606663681390495662081e+31
console.log(math.add(maxSafeInt, maxSafeInt)); // 18014398509481982
console.log(math.subtract(maxSafeInt, maxSafeInt)); // 0
console.log(math.multiply(maxSafeInt, math.bignumber(2))); // 18014398509481982
console.log(math.divide(maxSafeInt, math.bignumber(2))); // 4503599627370495.5
console.log(math.log10(maxSafeInt)); // 15.95458977019100329811178809273377220616031325194798178472905735
console.log(math.pow(maxSafeInt, math.bignumber(2))); // 8.1129638414606663681390495662081e+31使用 bignumber.js 管理大數
bignumber.js 是另一個用于管理任意精度十進制和非十進制數的 JavaScript 庫。它是一個免費、開源、MIT 許可的庫,用于處理大數據。
它可在瀏覽器、Node 和 Deno 中運行。要開始使用 bignumber.js,請用 npm 安裝它:
# npm npm i bignumber.js # yarn yarn add bignumber.js #pnpm pnpm add bignumber.js
安裝后,導入并創(chuàng)建一個 BigNumber 構造函數的實例,它以數字、字符串或 BigNumber 類型作為參數并返回一個對象。
在下面的示例中,我使用 commonjs 語法導入 bignumber.js。它還支持 ES 語法。如果你打算在沒有 JavaScript 打包器的情況下在瀏覽器環(huán)境中使用 bignumber.js,你也可以通過 CDN 訪問它:
const BigNumber = require("bignumber.js");
const distanceOfTheSun = new BigNumber('1.49597870700e11'); //
console.log(distanceOfTheSun) // BigNumber { s: 1, e: 11, c: [ 149597870700 ] }
console.log(distanceOfTheSun.valueOf()) // 149597870700當使用內置數字類型時,JavaScript 會將任何大于 Number.MAX_VALUE 的數值表示為 Infinity。但是,對于 bignumber.js,你可以使用任何大于 Number.MAX_VALUE 的值。
在下面的示例中,我通過將 Number.MAX_VALUE 作為字符串傳遞并計算其平方來創(chuàng)建 BigNumber 的實例。如果你使用內置的 JavaScript 數字類型做同樣的事情,你會得到 Infinity:
const BigNumber = require("bignumber.js");
console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
console.log(Number.MAX_VALUE ** 2) // Infinity
const maxValue = new BigNumber(Number.MAX_VALUE.toString());
const square = maxValue.exponentiatedBy("2");
console.log(square.valueOf()); // 3.23170060713109998320439596646649e+616
const squareRoot = square.squareRoot();
console.log(squareRoot.valueOf()); // 1.7976931348623157e+308
console.log(squareRoot.isEqualTo(maxValue)); // true但是,當處理無法在 JavaScript 中表示的如此大的數字時,請使用 toString 或 valueOf 方法以字符串形式訪問計算結果。
toNumber 方法會將你的計算結果強制轉換為 JavaScript 數字類型。你仍然會遇到上面突出顯示的相同 JavaScript 大數問題。你的答案將失去精度,或者 JavaScript 會將其表示為 Infinity。
雖然我們在本文中的目標是使用 bignumber.js 包來處理大數,但 bignumber.js 也可以處理相應的小數。它有幾個我沒有在這里突出展示的內置方法。你可以通過查看文檔以了解其他內置函數。
使用 JS Big Decimal 管理大數
JS Big Decimal 是另一個可用于處理大數的 JavaScript 庫。與上面的同類庫不同,JS Big Decimal 的包體積小,并且功能有限。你可以使用它來管理大、小十進制數。
根據你的包管理器,使用以下命令之一從 npm 包倉庫安裝 JS Big Decimal:
# npm npm i js-big-decimal # yarn yarn add js-big-decimal #pnpm pnpm add js-big-decimal
與其他兩個包一樣,導入 BigDecimal 構造函數并創(chuàng)建一個實例,如下例所示。 BigDecimal 構造函數將數字或字符串作為參數并返回 BigDecimal 對象。
然后,你可以使用 getValue 方法以字符串形式訪問數字的值?;蛘撸绻袷交敵?,請使用 getPrettyValue:
const BigDecimal = require("js-big-decimal");
const value = new BigDecimal('23');
console.log(value.add(new BigDecimal(2)).getValue())JS Big Decimal 具有執(zhí)行基本數學運算的函數,例如加法、減法、乘法和除法。下面的代碼演示了如何使用它們來處理大數據:
const BigDecimal = require("js-big-decimal");
const maxSafeInt = new BigDecimal(Number.MAX_SAFE_INTEGER.toString());
const divisor = new BigDecimal("2");
console.log(maxSafeInt.getPrettyValue()); // 9,007,199,254,740,991
const sum = maxSafeInt.add(maxSafeInt);
const quotient = maxSafeInt.divide(divisor);
const diff = maxSafeInt.subtract(quotient);
const product = quotient.multiply(divisor);
console.log(sum.getValue()); // 18014398509481982
console.log(quotient.getPrettyValue()); // 4,503,599,627,370,495.50000000
console.log(diff.getPrettyValue()); // 4,503,599,627,370,495.50000000
console.log(product.getPrettyValue()); // 9,007,199,254,740,991比較在 JavaScript 中管理大數的庫
并非所有庫都是完美的。每個第三方庫都有用例、優(yōu)點和缺點。讓我們通過對比它們的優(yōu)缺點并探索 GitHub 星級和問題、包大小和 npm 下載等指標來比較上述第三方包。
GitHub stars 等指標與社交媒體點贊類似。你可以將其用作第三方庫受歡迎程度的指標。然而,它并沒有告訴你太多關于質量的信息。
同樣,npm 下載統(tǒng)計數據也遠非精確。根據 npm,下載計數是 tarball 文件的 HTTP 200 響應服務的數量。因此,下載計數包括構建服務器、鏡像和機器人的自動下載。雖然 npm 下載計數不是庫活躍用戶的準確度量,但你可以使用它來對包進行比較。
| Math.js | bignumber.js | JS Big Decimal | |
|---|---|---|---|
| 壓縮后的包體積 | 187.93KB | 8.09KB | 3.88KB |
| 依賴數量 | 9 | 0 | 0 |
| GitHub stars | 13.1k | 6k | 96 |
| 積極維護 | Yes | Yes | Yes |
| 文檔 | Good | Good | Good |
| 許可證 | Apache-2.0 | MIT | MIT |
| npm 周下載量 | 502,656 | 7,114,325 | 25,204 |
上述所有第三方庫都是免費的開源庫,具有許可。在這三者中,Math.js 是一個功能豐富的通用數學庫,而另外兩個是為管理大數字而創(chuàng)建的。
因此,Math.js 具有最大的 Gzipped 包大小。但是,如果你使用像 webpack 這樣的打包器,它是可通過 tree-shake 優(yōu)化的。 Math.js 和 bignumber.js 都帶有幾個用于管理大數和對它們執(zhí)行數學運算的特性。
另一方面,JS Big Decimal 具有最小的包大小。但是,它的功能也最少。它只能執(zhí)行基本的數學運算。
小結
JavaScript 在內部使用 64 位雙精度二進制浮點格式來表示數字。它分配一位表示符號,11 位表示指數,53 位表示尾數。
JavaScript 分配固定位來表示雙精度浮點數的不同部分。因此,它近似于安全整數范圍之外的整數。同樣,它使用 Infinity 表示大于 Number.MAX_VALUE 的數值,使用 -Infinity 表示它們對應的負值。
盡管內置的 BigInt 對于處理大于最大安全整數或小于最小安全整數的整數很有用,但它是有一定局限的,因為你只能執(zhí)行基本的數學運算,例如加法、減法、乘法和求冪。你不能將它與內置 Math 對象的方法一起使用;這樣做會引發(fā)錯誤。
要在 JavaScript 中處理大數字而不會遇到上述限制,你需要第三方庫,例如 Math.js、bignumber.js 和 JS Big Decimal。盡管大多數第三方庫都有局限性,正如上面強調的那樣,但它們具有輕而易舉處理大數的功能。
以上就是在Node.js應用程序中處理大數的操作指南的詳細內容,更多關于Node.js 處理大數的資料請關注腳本之家其它相關文章!
相關文章
在Debian(Raspberry Pi)樹莓派上安裝NodeJS的教程詳解
在樹莓派上運行NodeJS并不需要特別的配置,你只需要確??梢杂胦penssh遠程連接到你的樹莓派就ok了,關于在Debian(Raspberry Pi)樹莓派上安裝NodeJS的方法,大家可以通過本文了解下2017-09-09
詳解nodejs中exports和module.exports的區(qū)別
本文主要介紹了exports 和 module.exports 的區(qū)別。具有很好的參考價值,下面跟著小編一起來看下吧2017-02-02
node.js中stream流中可讀流和可寫流的實現與使用方法實例分析
這篇文章主要介紹了node.js中stream流中可讀流和可寫流的實現與使用方法,結合實例形式分析了node.js stream流可讀流和可寫流基本分類、原理、定義、使用方法及相關注意事項,需要的朋友可以參考下2020-02-02
一會帶你學會用Webpack搭建開發(fā)環(huán)境并打包代碼
這篇文章主要給大家介紹了關于如何用Webpack搭建開發(fā)環(huán)境并打包的相關資料,webpack是一個現代JavaScript應用程序的靜態(tài)模塊打包器(module bundler),需要的朋友可以參考下2023-08-08
Node.js 使用 Express-Jwt和JsonWebToken 進行Token身份
這篇文章主要介紹了Node.js 使用 Express-Jwt和JsonWebToken 進行Token身份驗證的操作方法,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-08-08
Node 使用express-http-proxy 做api網關的實現
這篇文章主要介紹了Node 使用express-http-proxy 做api網關的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10

