📖 概述
在我做的一个项目中,需要在前端进行大量的数学计算,但是单纯使用Number会导致小数点精度丢失的问题,为了解决这个问题,我在网上搜索相关的数学库。其中最具代表性的三个库:big.js 、bignumber.js 和 decimal.js 都出自同一位开发者之手,但它们各自有着不同的设计理念和适用场景。
本文将从多个维度深入对比这三个库,帮助大家根据具体需求做出最佳选择。
📊 基本信息对比
特性 | big.js | bignumber.js | decimal.js |
---|---|---|---|
体积 | ~8KB | ~20KB | ~32KB |
整数支持 | ✅ | ✅ | ✅ |
浮点数支持 | ✅ | ✅ | ✅ |
其他进制 | ❌ (抛出错误) | ✅ | ✅ |
科学计数法 | ✅ | ✅ | ✅ |
NaN/Infinity | ❌ | ✅ | ✅ |
三角函数 | ❌ | ❌ | ✅ |
指数/对数 | ❌ | ❌ | ✅ |
配置选项 | 基础 | 丰富 | 非常丰富 |
🎯 设计理念与定位
big.js - 极简主义的选择
javascript
// big.js 的设计哲学:简单、小巧、可靠
const Big = require('big.js');
// 简洁的API设计
const result = Big(0.1).plus(0.2);
console.log(result.toString()); // "0.3"
设计特点:
- 极简设计,仅提供核心功能
- 最小的体积负担(~8KB)
- 只支持十进制运算
- 不支持 NaN 和 Infinity
- 配置选项有限但够用
bignumber.js - 金融应用的首选
javascript
// bignumber.js 的设计哲学:功能全面、金融友好
const BigNumber = require('bignumber.js');
// 支持多种进制
const hex = new BigNumber('ff', 16);
console.log(hex.toString()); // "255"
console.log(hex.toString(16)); // "ff"
// 精确的金融计算
BigNumber.config({ DECIMAL_PLACES: 2 });
const price = new BigNumber('19.99');
const tax = price.multipliedBy(0.08);
console.log(tax.toFixed(2)); // "1.60"
设计特点:
- 面向金融应用优化
- 精度以小数位数为单位
- 仅在除法运算时应用精度限制
- 支持多进制和丰富的配置
- 支持 NaN 和 Infinity
decimal.js - 科学计算的利器
javascript
// decimal.js 的设计哲学:科学计算、高精度、功能完整
const Decimal = require('decimal.js');
// 科学计算示例
Decimal.set({ precision: 50 });
const pi = Decimal.acos(-1);
console.log(pi.toString());
// "3.1415926535897932384626433832795028841971693993751"
// 复杂数学运算
const result = Decimal.exp(1).plus(Decimal.ln(10));
console.log(result.toString()); // 高精度的 e + ln(10)
设计特点:
- 面向科学计算设计
- 精度以有效数字为单位
- 所有运算都应用精度限制
- 内置三角函数、指数、对数等
- 支持二进制指数表示法
⚙️ 功能特性详细对比
1. 数值创建和类型支持
javascript
// 字符串创建(推荐方式)
const bigNum1 = Big('123.456');
const bigNum2 = new BigNumber('123.456');
const decimal1 = new Decimal('123.456');
// 数字创建
const bigNum3 = Big(123.456); // ✅ 支持
const bigNum4 = new BigNumber(123.456); // ✅ 支持
const decimal2 = new Decimal(123.456); // ✅ 支持
// 特殊值处理
try {
const bigNaN = Big('NaN'); // ❌ 抛出错误
} catch (e) {
console.log('big.js 不支持 NaN');
}
const bignumberNaN = new BigNumber('NaN'); // ✅ 支持
const decimalNaN = new Decimal('NaN'); // ✅ 支持
2. 进制支持对比
javascript
// big.js - 仅支持十进制
try {
const bigHex = Big('0xff'); // ❌ 抛出错误
} catch (e) {
console.log('big.js 不支持十六进制');
}
// bignumber.js - 全面的进制支持
const bnHex = new BigNumber('ff', 16);
const bnOct = new BigNumber('77', 8);
const bnBin = new BigNumber('1111', 2);
console.log(bnHex.toString()); // "255"
console.log(bnHex.toString(16)); // "ff"
console.log(bnOct.toString()); // "63"
console.log(bnBin.toString()); // "15"
// decimal.js - 支持前缀表示法
const decHex = new Decimal('0xff');
const decOct = new Decimal('0o77');
const decBin = new Decimal('0b1111');
console.log(decHex.toString()); // "255"
console.log(decHex.toHexadecimal()); // "0xff"
console.log(decOct.toString()); // "63"
console.log(decBin.toString()); // "15"
3. 精度处理方式
javascript
// big.js - 精度仅应用于除法
Big.DP = 3; // 设置小数位数为3
const bigResult = Big('10').div(3);
console.log(bigResult.toString()); // "3.333"
const bigSum = Big('123.456789').plus(1);
console.log(bigSum.toString()); // "124.456789" (保持原精度)
// bignumber.js - 精度仅应用于除法
BigNumber.config({ DECIMAL_PLACES: 3 });
const bnResult = new BigNumber('10').div(3);
console.log(bnResult.toString()); // "3.333"
const bnSum = new BigNumber('123.456789').plus(1);
console.log(bnSum.toString()); // "124.456789" (保持原精度)
// decimal.js - 精度应用于所有运算
Decimal.set({ precision: 7 });
const decResult = new Decimal('10').div(3);
console.log(decResult.toString()); // "3.333333"
const decSum = new Decimal('123.456789').plus(1);
console.log(decSum.toString()); // "124.4568" (应用精度限制)
4. 数学函数支持
javascript
// big.js - 基础四则运算
const bigValue = Big('2');
console.log(bigValue.plus(3).toString()); // "5"
console.log(bigValue.minus(1).toString()); // "1"
console.log(bigValue.mul(4).toString()); // "8"
console.log(bigValue.div(2).toString()); // "1"
console.log(bigValue.pow(3).toString()); // "8"
// ❌ 不支持 sqrt, sin, cos, exp, ln 等
// bignumber.js - 扩展数学函数
const bnValue = new BigNumber('4');
console.log(bnValue.plus(2).toString()); // "6"
console.log(bnValue.sqrt().toString()); // "2"
console.log(bnValue.pow(0.5).toString()); // "2"
// ❌ 不支持三角函数和高级数学函数
// decimal.js - 完整数学函数库
const decValue = new Decimal('4');
console.log(decValue.plus(2).toString()); // "6"
console.log(decValue.sqrt().toString()); // "2"
console.log(decValue.pow(0.5).toString()); // "2"
// 高级数学函数
console.log(Decimal.sin('0.5').toString()); // "0.479..."
console.log(Decimal.cos('0').toString()); // "1"
console.log(Decimal.exp('1').toString()); // "2.718..." (e)
console.log(Decimal.ln('10').toString()); // "2.302..." (ln 10)
console.log(Decimal.log10('100').toString()); // "2"
🔧 API设计对比
方法命名风格
javascript
// 加法运算的不同命名
Big('5').plus(3); // big.js
BigNumber('5').plus(3); // bignumber.js
Decimal('5').plus(3); // decimal.js
// 乘法运算的不同命名
Big('5').mul(3); // big.js 使用 mul
BigNumber('5').multipliedBy(3); // bignumber.js 使用完整单词
Decimal('5').mul(3); // decimal.js 使用 mul
// 链式调用示例
const result1 = Big('10')
.plus(5)
.mul(2)
.div(3)
.toString(); // "10"
const result2 = new BigNumber('10')
.plus(5)
.multipliedBy(2)
.dividedBy(3)
.toString(); // "10"
const result3 = new Decimal('10')
.plus(5)
.mul(2)
.div(3)
.toString(); // "10"
构造函数差异
javascript
// big.js - new 关键字可选
const big1 = Big('123'); // ✅
const big2 = new Big('123'); // ✅
// bignumber.js - new 关键字可选
const bn1 = BigNumber('123'); // ✅
const bn2 = new BigNumber('123'); // ✅
// decimal.js - new 关键字可选
const dec1 = Decimal('123'); // ✅
const dec2 = new Decimal('123'); // ✅
// 参数类型支持
const fromNumber = Big(123); // ✅ 数字
const fromString = Big('123.456'); // ✅ 字符串
const fromAnother = Big(big1); // ✅ 同类型实例
📈 性能表现分析
基于实际基准测试数据,三个库的性能表现:
javascript
// 测试场景:计算 (100 + 200000) * 200000
// 执行次数:每秒操作数
// 性能排名(从快到慢):
// 1. big.js: 1,827,708 ops/sec
// 2. bignumber.js: 1,488,096 ops/sec
// 3. decimal.js: 1,272,333 ops/sec
// 性能测试示例代码
function performanceTest() {
const iterations = 100000;
// big.js 测试
console.time('big.js');
for (let i = 0; i < iterations; i++) {
Big('100').plus('200000').mul('200000').toString();
}
console.timeEnd('big.js');
// bignumber.js 测试
console.time('bignumber.js');
for (let i = 0; i < iterations; i++) {
BigNumber('100').plus('200000').multipliedBy('200000').toString();
}
console.timeEnd('bignumber.js');
// decimal.js 测试
console.time('decimal.js');
for (let i = 0; i < iterations; i++) {
Decimal('100').plus('200000').mul('200000').toString();
}
console.timeEnd('decimal.js');
}
性能分析总结:
- big.js 由于功能简单,性能最优
- bignumber.js 在功能和性能之间达到良好平衡
- decimal.js 功能最全面,但性能相对较慢
💡 实际应用场景示例
1. 金融计算场景
javascript
// 场景:计算商品价格、税费、折扣
// 推荐使用 bignumber.js
BigNumber.config({
DECIMAL_PLACES: 2,
ROUNDING_MODE: BigNumber.ROUND_HALF_UP
});
function calculateOrderTotal(items) {
let subtotal = new BigNumber(0);
items.forEach(item => {
const itemTotal = new BigNumber(item.price)
.multipliedBy(item.quantity);
subtotal = subtotal.plus(itemTotal);
});
const taxRate = new BigNumber('0.08'); // 8% 税率
const tax = subtotal.multipliedBy(taxRate);
const total = subtotal.plus(tax);
return {
subtotal: subtotal.toFixed(2),
tax: tax.toFixed(2),
total: total.toFixed(2)
};
}
// 使用示例
const order = calculateOrderTotal([
{ price: '19.99', quantity: 2 },
{ price: '5.50', quantity: 1 }
]);
console.log(order);
// { subtotal: "45.48", tax: "3.64", total: "49.12" }
2. 科学计算场景
javascript
// 场景:物理计算、统计分析
// 推荐使用 decimal.js
Decimal.set({ precision: 50, rounding: 4 });
function calculateCompoundInterest(principal, rate, time, compound) {
// A = P(1 + r/n)^(nt)
const P = new Decimal(principal);
const r = new Decimal(rate);
const t = new Decimal(time);
const n = new Decimal(compound);
const ratePerPeriod = r.div(n);
const exponent = n.mul(t);
const base = Decimal(1).plus(ratePerPeriod);
const amount = P.mul(base.pow(exponent));
return {
principal: P.toString(),
finalAmount: amount.toString(),
interest: amount.minus(P).toString()
};
}
// 计算复利
const investment = calculateCompoundInterest(10000, 0.05, 10, 12);
console.log(investment);
// 三角函数计算示例
function calculateWaveform(amplitude, frequency, time) {
const A = new Decimal(amplitude);
const f = new Decimal(frequency);
const t = new Decimal(time);
const omega = Decimal(2).mul(Decimal.acos(-1)).mul(f); // 2πf
const phase = omega.mul(t);
return A.mul(Decimal.sin(phase));
}
const waveValue = calculateWaveform(5, 50, 0.01);
console.log('波形值:', waveValue.toString());
3. 简单数值处理场景
javascript
// 场景:基础数学运算、简单的精度处理
// 推荐使用 big.js
Big.DP = 6; // 6位小数
Big.RM = 1; // 向下舍入
function simpleCalculator(operation, a, b) {
const numA = Big(a);
const numB = Big(b);
switch (operation) {
case 'add':
return numA.plus(numB).toString();
case 'subtract':
return numA.minus(numB).toString();
case 'multiply':
return numA.mul(numB).toString();
case 'divide':
return numA.div(numB).toString();
default:
throw new Error('不支持的运算');
}
}
// 使用示例
console.log(simpleCalculator('add', '0.1', '0.2')); // "0.3"
console.log(simpleCalculator('divide', '1', '3')); // "0.333333"
console.log(simpleCalculator('multiply', '0.123', '456')); // "56.088"
// 避免浮点数精度问题
function safeMath() {
// 原生JavaScript的问题
console.log(0.1 + 0.2); // 0.30000000000000004
// 使用big.js解决
console.log(Big(0.1).plus(0.2).toString()); // "0.3"
}
🤔 选择建议
选择 big.js 的情况
- ✅ 项目对包体积敏感
- ✅ 只需要基础的四则运算
- ✅ 追求最佳性能表现
- ✅ 仅处理十进制数值
- ✅ 团队偏好简单的API
javascript
// 适用场景示例
const price1 = Big('19.99');
const price2 = Big('5.01');
const total = price1.plus(price2);
console.log(total.toString()); // "25.00"
选择 bignumber.js 的情况
- ✅ 金融或商业应用
- ✅ 需要多进制支持
- ✅ 需要更多配置选项
- ✅ 要求高度的数值精确性
- ✅ 团队有丰富的JavaScript经验
javascript
// 适用场景示例
BigNumber.config({ DECIMAL_PLACES: 2 });
const investment = new BigNumber('1000.50');
const rate = new BigNumber('0.03');
const interest = investment.multipliedBy(rate);
console.log(interest.toFixed(2)); // "30.02"
选择 decimal.js 的情况
- ✅ 科学计算或工程应用
- ✅ 需要三角函数等高级数学函数
- ✅ 处理非常大或非常小的数值
- ✅ 需要科学计数法支持
- ✅ 对功能完整性要求高于性能
javascript
// 适用场景示例
const angle = Decimal('30').mul(Decimal.acos(-1)).div(180); // 30度转弧度
const sinValue = Decimal.sin(angle);
console.log(sinValue.toString()); // sin(30°) = 0.5
📋 迁移注意事项
从其他库迁移到这三个库
javascript
// 1. 构造函数统一使用字符串参数
// ❌ 避免
const bad1 = Big(0.1);
const bad2 = BigNumber(0.2);
// ✅ 推荐
const good1 = Big('0.1');
const good2 = BigNumber('0.2');
// 2. 方法名差异处理
function migrateOperation(value1, value2, library) {
const a = createNumber(value1, library);
const b = createNumber(value2, library);
switch (library) {
case 'big':
return a.mul(b);
case 'bignumber':
return a.multipliedBy(b);
case 'decimal':
return a.mul(b);
}
}
function createNumber(value, library) {
switch (library) {
case 'big': return Big(value);
case 'bignumber': return new BigNumber(value);
case 'decimal': return new Decimal(value);
}
}
// 3. 配置差异处理
function setupPrecision(library, precision) {
switch (library) {
case 'big':
Big.DP = precision;
break;
case 'bignumber':
BigNumber.config({ DECIMAL_PLACES: precision });
break;
case 'decimal':
Decimal.set({ precision: precision });
break;
}
}
📚 总结
这三个库各有优势,选择时应根据具体需求权衡:
维度 | big.js | bignumber.js | decimal.js |
---|---|---|---|
适用场景 | 简单数值处理 | 金融商业应用 | 科学工程计算 |
学习成本 | 低 | 中 | 高 |
功能丰富度 | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
性能表现 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
包体积 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
社区活跃度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
最终建议:
- 🚀 新手或简单项目 :选择
big.js
- 💰 金融或商业项目 :选择
bignumber.js
- 🔬 科学或工程项目 :选择
decimal.js
这三个库都能有效解决JavaScript原生数值精度问题,关键是找到最适合你项目需求的那一个!
文章最后,给大家介绍一下个人博客网站:叁木の小屋。欢迎各位捧场。笔芯❤。