Decimal.js:JavaScript 中的高精度十進制運算庫

Decimal.js:JavaScript 中的高精度十進制運算庫
作者: Calpa Liu
字數:1515
出版:April 7, 2025

Decimal.js 是一個為 JavaScript 和 TypeScript 設計的任意精度十進制運算庫,專為解決 JavaScript 原生數字型別在處理浮點數時所面臨的精度問題。對於需要高精度計算的金融和科學應用來說,這個庫提供了一個強大且可靠的解決方案。

浮點數運算的問題與 Decimal.js 的解決方案

在 JavaScript 中,浮點數使用 IEEE 754 雙精度格式表示,這導致了一些令人困惑的計算結果。例如,一個經典的案例是:

0.1 + 0.2; // 輸出:0.30000000000000004,而非預期的 0.3

這不僅僅是 JavaScript 特有的問題,而是幾乎所有計算機表示數字時的共同缺陷。當數字從十進制轉換為二進制時,由於位數有限,某些數字無法被精確表示,從而導致舍入誤差。

Decimal.js 通過直接以十進制方式表示數字來解決這個問題,而不需要進行二進制轉換:

const Decimal = require("decimal.js");
const a = new Decimal("0.1");
const b = new Decimal("0.2");
const result = a.plus(b);
console.log(result.toString()); // 輸出:'0.3'

這種方法確保了計算結果的準確性,特別是在金融和科學應用中,這些領域通常不能容忍任何計算誤差。

Decimal.js 的核心特性

任意精度十進制運算

Decimal.js 最顯著的特點是它能夠進行任意精度的十進制運算。這意味著它可以處理包含任意數量有效數字的數值,無論是非常大還是非常小的數字。

// 處理超大數字
const largeNumber = new Decimal("123456789e+250");
const result = largeNumber.times("10e+150");
console.log(result.toString()); // 輸出:'1.23456789e+409'

完整的數學功能支持

Decimal.js 提供了豐富的 API,複製了 JavaScript 中Number.prototypeMath對象的許多方法,同時還增加了如三角函數等其他功能。

可配置的精度與舍入模式

所有計算都會根據 Decimal 構造函數的precisionrounding屬性指定的有效數字數量和舍入模式進行舍入。

// 設置默認 Decimal 構造函數的精度和舍入方式
Decimal.set({ precision: 5, rounding: 4 });

// 創建另一個 Decimal 構造函數,可選地傳入配置對象
const Dec = Decimal.clone({ precision: 9, rounding: 1 });

const x = new Decimal(5);
const y = new Dec(5);

console.log(x.div(3)); // 輸出:'1.6667'
console.log(y.div(3)); // 輸出:'1.66666666'

使用 Decimal.js 的優勢

精確的十進制運算

使用 Decimal.js 可以避免 JavaScript 浮點數計算中常見的精度誤差。這對於需要精確計算的金融和貨幣處理至關重要。

擴展的數值範圍

Decimal.js 可以表示任意大小的數字,遠超過 JavaScript 原生 Number 類型的限制。這在需要處理極大或極小數值的科學計算中特別有用。

// JavaScript 中數值超出範圍會導致精度損失
new Decimal(2e308); // 'Infinity' 在原生 JavaScript 中
new Decimal("2e+308"); // 可以正確表示在 Decimal.js 中

可定制的精度控制

Decimal.js 允許開發者根據應用需求設置所需的精度級別,確保計算結果按照預期進行舍入。

簡單一致的 API

Decimal.js 提供了簡潔明了的 API,使其易於學習和使用。它的方法名稱直觀,功能齊全。

廣泛的平台兼容性

Decimal.js 僅使用 JavaScript 1.5(ECMAScript 3)特性,因此可以在各種平台上運行,包括較舊的瀏覽器環境。

TypeScript 支持

Decimal.js 包含 TypeScript 聲明文件,提供了完整的類型支持,有助於提高代碼質量和開發效率。

與其他類似庫的比較

市場上存在幾個類似的高精度數字庫,各有優缺點:

big.js

  • 高性能,專為速度優化
  • 簡單直觀的 API
  • 相比 Decimal.js 功能較少
  • 運算速度:1,164,624.9 Ops/sec

bignumber.js

  • 全面的數學運算功能
  • 用戶群體大,維護活躍
  • 運算速度:611,546.2 Ops/sec

decimal.js-light

  • Decimal.js 的輕量版本,不包含三角函數
  • 保持高精度但體積更小
  • 性能更好,適合對大小有嚴格限制的項目

在性能方面,這些庫都比原生 JavaScript 數字慢得多(原生操作:177,217,520.0 Ops/sec),但對於要求精確計算的應用來說,這種性能損失通常是可以接受的。

使用示例

基本算術運算

const Decimal = require("decimal.js");

const a = new Decimal("9.99");
const b = new Decimal("8.03");

// 加法
const addition = a.add(b);
console.log(addition.toString()); // '18.02'

// 減法
const subtraction = a.sub(b);
console.log(subtraction.toString()); // '1.96'

// 乘法
const multiplication = a.mul(b);
console.log(multiplication.toString()); // '80.2197'

// 除法
const division = a.div(b);
console.log(division.toString()); // '1.244084682440847'

處理金融計算

在金融應用中,Decimal.js 可以確保計算的準確性,避免因舍入誤差導致的資金損失。

// 計算貸款利息
const principal = new Decimal("10000");
const rate = new Decimal("0.05").div(12); // 月利率
const term = 60; // 60 個月

// 計算每月付款額
const monthlyPayment = principal
  .times(rate)
  .div(new Decimal(1).minus(new Decimal(1).plus(rate).pow(-term)));

console.log(monthlyPayment.toFixed(2)); // '188.71'

性能考量

雖然 Decimal.js 提供了高精度計算,但這是以性能為代價的。與原生 JavaScript 數字相比,Decimal.js 的運算速度明顯較慢(約 762,818.1 Ops/sec vs 原生的 177,217,520.0 Ops/sec)。

因此,在選擇使用 Decimal.js 時,應該考慮應用的需求:

  • 如果精確度至關重要(如金融計算),Decimal.js 是理想選擇
  • 對於性能敏感但不需要極高精度的應用,可以考慮使用原生數字或其他優化方法

結論

Decimal.js 是一個強大的 JavaScript 庫,為需要進行精確十進制計算的應用提供了可靠的解決方案。它解決了 JavaScript 原生浮點數運算中固有的精度問題,提供了豐富的數學功能和靈活的配置選項。

對於金融應用、科學計算或任何對數值精度有嚴格要求的領域,Decimal.js 提供了不可或缺的工具。雖然它可能不如原生數字運算那麼快,但在需要精確結果的場景中,這種性能損失通常是值得的。

作為一個 Web3 開發者,尤其是在處理加密貨幣相關計算時,使用像 Decimal.js 這樣的高精度庫可以確保交易金額的準確性,避免因精度問題導致的資金損失。結合 TypeScript 的類型檢查,能夠進一步提高代碼的可靠性和可維護性。

感謝您閱讀我的文章。歡迎隨時分享你的想法。
關於 Calpa

Calpa 擅長使用 TypeScriptReact.jsVue.js 建立 Responsive Website。

他積極參與開源社區,曾在 2019 年的香港開源大會上擔任講者,提供工作經驗和見解。此外,他也在 GitHub 上公開分享個人博客程式碼,已獲得超過 300 顆星星和 60 個分支的支持。

他熱愛學習新技術,並樂意分享經驗。他相信,唯有不斷學習才能跟上快速演變的技術環境。