为什么需要 big.js?
JavaScript 的原生 Number 类型基于 IEEE-754 双精度浮点数,它在处理小数运算时经常会产生精度误差,例如:
0.1 + 0.2 // 结果并不是 0.3,而是 0.30000000000000004
这种误差在涉及金钱、科学计算、统计分析等场景中可能会导致严重后果。big.js 是一个专注于十进制 任意精度 运算的轻量级库,它用字符串或十进制内部表示来避免浮点误差,提供高精度且稳定的算术操作。
big.js 的设计简洁、单一目标,不支持 NaN 或 Infinity,也没有过多的进阶功能,专注在十进制加、减、乘、除、幂与比较等核心功能上。
安装与引入方式
1. 使用 npm / yarn 安装
在 Node.js 或前端构建环境中,可以通过 npm 安装:
npm install big.js
或者使用 yarn:
yarn add big.js
然后在代码中引用:
import Big from 'big.js';
// 或者在 CommonJS 中
const Big = require('big.js');
2. 浏览器引入
若你在页面上直接使用,可以下载 big.min.js 并在 HTML 中通过 script 引入:
<script src="path/to/big.min.js"></script>
<script>
const x = new Big('1.23');
console.log(x.plus(0.77).toString()); // "2.00"
</script>
这样可以在全局访问 Big 构造器。
核心概念与全局设置
Big 构造器
Big(n) 或 new Big(n) 用于创建一个大数对象,其中 n 可以是数字、字符串或另一个 Big 对象。若将原生数字传入,则内部会转换为字符串再解析,以避免初始精度损失。
const a = new Big('0.1');
const b = Big(0.2);
const c = new Big(a); // 复制一个新的 Big
精度(DP)和舍入模式(RM)
- Big.DP:控制除法、开方、负指数幂运算结果的小数位数(默认通常是 20 位)
- Big.RM:舍入模式,可选值有 0(向零截断)、1(四舍五入)、2(银行家舍入 / 最近偶数)、3(向远舍入)
你可以按需设置:
Big.DP = 30; // 设置最多保留 30 位小数
Big.RM = Big.roundHalfUp; // 使用四舍五入模式
这些设置只在需要舍入的运算(像除法、开方或负幂)中生效。
基本算术操作:加减乘除与幂运算
big.js 的算术方法都是不可变的(immutable),也就是说每次方法调用都会返回一个新的 Big 实例。
1. 加法与减法
const x = Big('1.234');
const y = Big('2.345');
const sum = x.plus(y); // “3.579”
const diff = x.minus(y); // “-1.111”
2. 乘法
const prod = x.times(y);
3. 除法
const quotient = x.div(y);
如果除不尽,则结果会根据 Big.DP 与 Big.RM 设置自动舍入。
4. 幂运算(pow)
pow 用于求幂,支持整数幂和负指数幂:
const p1 = Big('0.9').pow(3); // 0.9 的三次方
const p2 = Big('2').pow(-2); // 2 的负二次方(即 1/4)
对于负指数幂计算,若结果为小数,则同样受 Big.DP 与 Big.RM 控制。
5. 额外方法:sqrt、abs、mod、neg
- sqrt():计算平方根
- abs():取绝对值
- mod(n):取模运算
- neg():取相反数
例如:
const z = Big('9').sqrt(); // “3”
const m = Big('10').mod('3'); // “1”
const neg = Big('-5').neg(); // “5”
比较与格式化输出
比较方法
Big 实例提供丰富的比较方法:
- eq(n):等于
- gt(n):大于
- gte(n):大于等于
- lt(n):小于
- lte(n):小于等于
- cmp(n):返回 -1 / 0 / 1
例如:
x.eq('1.234'); // true / false
x.gt(y); // boolean
x.cmp(y); // -1 / 0 / 1
输出与格式化
常用输出方法包括:
- toString():返回标准字符串形式
- toFixed(dp):保留 dp 位小数,四舍五入
- toExponential(dp):以指数形式输出
- toPrecision(sd):按有效位数输出
- toNumber():将 Big 转为普通 Number(若精度丢失则可能报错,尤其在 strict 模式下不可用)
例如:
const a = Big('2.5');
a.toFixed(5); // "2.50000"
a.toExponential(2); // "2.50e+0"
a.toPrecision(3); // "2.50"
a.toNumber(); // 2.5 (若精度可安全转换)
复杂表达式写法与链式操作
在复杂表达式中,可以采用链式调用或局部拆分写法。举个例子,比如你要计算:
res = a + (b / c) + (d + 1) / (e * f * g)
在 big.js 中可以写为:
const A = Big(a);
const B = Big(b);
const C = Big(c);
const D = Big(d);
const E = Big(e);
const F = Big(f);
const G = Big(g);
const result = A
.plus(B.div(C))
.plus(D.plus(1).div(E.times(F).times(G)));
const asNumber = result.toNumber(); // 如果你需要普通数字
你也可以拆解为多个中间变量,但链式写法更加紧凑。
注意事项与最佳实践
- 避免直接使用原生 Number 参与运算:务必将常量或变量转换为 Big 对象,以免初始就产生精度误差。
- 慎用 toNumber():它会将高精度 Big 转回普通数字,可能丢失精度;在严格模式下若损失明显可能抛错。
- 合理设置 DP 与 RM:不要设置过大或过小的小数位数,要根据具体业务场景决定精度上限与舍入方式。
- 链式调用注意顺序:运算顺序影响最终的舍入结果,建议从最内层先计算,逐步构造链式结构。
- 性能考量:big.js 并不适合极端大数或大规模运算场景,若你需要更多数学功能(如三角函数、对数、指数),你可能需要更全面的高精度库(如 decimal.js)。
- 不支持 NaN / Infinity:big.js 的设计理念是严格控制运算范围,不接受不合法数值。
- 子构造器功能:你可以用 const Big2 = Big() 创建一个新的构造器,该构造器可以有不同的 DP / RM 设置,以满足不同子模块的需求。
总结与应用建议
通过本文,你已经掌握了 big.js 的安装、基本用法、核心设置、比较与格式化、复杂表达式处理以及注意事项。借助 big.js,你可以在 JavaScript 中有效避免浮点误差,在涉及金钱计算、科学数据处理等领域获得更可靠的精度表现。