JavaScript数学库深度对比

📖 概述

在我做的一个项目中,需要在前端进行大量的数学计算,但是单纯使用Number会导致小数点精度丢失的问题,为了解决这个问题,我在网上搜索相关的数学库。其中最具代表性的三个库:big.jsbignumber.jsdecimal.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原生数值精度问题,关键是找到最适合你项目需求的那一个!

文章最后,给大家介绍一下个人博客网站:叁木の小屋。欢迎各位捧场。笔芯❤。

相关推荐
roamingcode1 小时前
Claude Code NPM 包发布命令
前端·npm·node.js·claude·自定义指令·claude code
码哥DFS1 小时前
NPM模块化总结
前端·javascript
灵感__idea1 小时前
JavaScript高级程序设计(第5版):代码整洁之道
前端·javascript·程序员
唐璜Taro2 小时前
electron进程间通信-IPC通信注册机制
前端·javascript·electron
陪我一起学编程3 小时前
创建Vue项目的不同方式及项目规范化配置
前端·javascript·vue.js·git·elementui·axios·企业规范
LinXunFeng4 小时前
Flutter - 详情页初始锚点与优化
前端·flutter·开源
GISer_Jing4 小时前
Vue Teleport 原理解析与React Portal、 Fragment 组件
前端·vue.js·react.js
Summer不秃4 小时前
uniapp 手写签名组件开发全攻略
前端·javascript·vue.js·微信小程序·小程序·html
coderklaus4 小时前
Base64编码详解
前端·javascript
NobodyDJ4 小时前
Vue3 响应式大对比:ref vs reactive,到底该怎么选?
前端·vue.js·面试