JavaScript因为存在计算的精度问题,所以直接计算就可能会导致各种各样的bug,为了解决这个问题,就要使用BigNumber.js这个库。
至于为什么JavaScript会有精度问题呢,可以看 https://github.com/camsong/blog/issues/9。简单来说就是因为:JavaScript中所有的数字(包括整数和小数)都只有一种类型–Number。它的实现遵循IEEE 754标准,使用64位固定长度来表示,也就是标准的double双精度浮点数。它的优点是可以归一化处理整数和小数,节省储存空间。而实际计算的时候会转换成二进制计算再转成十进制。进制转换之后会很长,舍去一部分,计算再转回来,就有了精度误差。
BigNumber.js是一个用于任意精度计算的js库。可以在 官方文档 的console中测试使用。也可以通过 npm install bignumber.js --save 来安装。然后 import BigNumber from ‘bignumber.js’ 来引入使用。他的大概原理是将所有数字当做字符串,重新实现了计算逻辑。缺点是性能比原生的差很多。
具体用法可以参考以下资料:
●https://mikemcl.github.io/bignumber.js/
consyructor
/* * n {number|string|BigNumber} A numeric value. * [b] {number} The base of n. Integer, 2 to ALPHABET.length inclusive. */ function BigNumber(n, b) { }
静态方法
clone()
生成一个独立的BigNumber构造函数
var BN = BigNumber.clone() BN(1).div(3).toNumber() //0.3333333333333333
config()
为这个独立的BigNumber构造函数设置参数
主要包括以下几个参数:
1.DECIMAL_PLACES(默认值:20) 用于设置小数位数。在除法、开方、指数计算时会用到。
var BN = BigNumber.clone() BN.config({DECIMAL_PLACES:4}) BN(1).div(3).toNumber() //0.3333,注意跟上面计算结果的区别
2.ROUNDING_MODE(默认值4) 舍入模式,取值的意义可参照 文档
//取值范围: BigNumber.ROUND_UP = 0; //远离0方向舍入 BigNumber.ROUND_DOWN = 1; //向0方向舍入 BigNumber.ROUND_CEIL = 2; //向正无限大舍入 BigNumber.ROUND_FLOOR = 3; //向负无限大舍入 BigNumber.ROUND_HALF_UP = 4; //四舍五入:向最接近的数字方向舍入,如果与两个相邻数字的距离相等,则向上舍入。 BigNumber.ROUND_HALF_DOWN = 5; //向最接近的数字方向舍入,如果与两个相邻数字的距离相等,则向下舍入。 BigNumber.ROUND_HALF_EVEN = 6; //向最接近数字方向舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入 BigNumber.ROUND_HALF_CEIL = 7; BigNumber.ROUND_HALF_FLOOR = 8;
3.EXPONENTIAL_AT(默认值[-7,20]) 指数计数法
4.RANGE(默认值[-1e+9,1e+9])
5.CRYPTO(默认值 false) 用于设置BigNumber.random()的随机生成算法。如果无法设置为true,则使用Math.random()生成随机值。
6.MODULO_MODE(默认值:ROUND_DOWN) 取模运算的模式
7.POW_PRECISION(默认值:0) pow运算结果的精度
8.FORMATE(格式化对应的设置)
默认值: BigNumber.config().FORMAT ============================== { decimalSeparator: "." fractionGroupSeparator: " " fractionGroupSize: 0 groupSeparator: "," groupSize: 3 secondaryGroupSize: 0 }
获取数组中的最大值/最小值
maximum([]),minimum([])
返回一个伪随机值,参数可以指定小数点位数
random([precision])
实例方法
加法:.plus(n [, base]) ⇒ BigNumber
0.1 + 0.2 // 0.30000000000000004 x = new BigNumber(0.1) y = x.plus(0.2) // '0.3' BigNumber(0.7).plus(x).plus(y) // '1' x.plus('0.1', 8) // '0.225'
减法:.minus(n [, base]) ⇒ BigNumber
0.3 - 0.1 // 0.19999999999999998 x = new BigNumber(0.3) x.minus(0.1) // '0.2' x.minus(0.6, 20) // '0'
乘法:.times(n [, base]) ⇒ BigNumber; m.ultipliedBy(n [, base]) ⇒ BigNumber;
0.6 * 3 // 1.7999999999999998 x = new BigNumber(0.6) y = x.multipliedBy(3) // '1.8' BigNumber('7e+500').times(y) // '1.26e+501' x.multipliedBy('-a', 16) // '-6
普通除法运算: .div(n [, base]) ⇒ BigNumber; .dividedBy(n [, base]) ⇒ BigNumber
x = new BigNumber(355) y = new BigNumber(113) x.dividedBy(y) // '3.14159292035398230088' x.div(5) // '71' x.div(47, 16) // '5'
注意: 除法计算结果会根据DECIMAL_PLACES和ROUNDING_MODE两个属性设置进行舍入。
除法,返回整数: .idiv(n [, base]) ⇒ BigNumber;.dividedToIntegerByv(n [, base]) ⇒ BigNumber
x = new BigNumber(355) y = new BigNumber(113) x.dividedBy(y) // '3.14159292035398230088' x.div(5) // '71' x.div(47, 16) // '5'
取模/取余: .mod(n [, base]) ⇒ BigNumber;modulo.(n [, base]) ⇒ BigNumber
1 % 0.9 // 0.09999999999999998 x = new BigNumber(1) x.modulo(0.9) // '0.1' y = new BigNumber(33) y.mod('a', 33) // '3'
注意: 取模/取余运算受MODULO_MODE设置影响
指数运算: .pow(n [, m]) ⇒ BigNumber;.exponentiatedBy(n [, m]) ⇒ BigNumber
Math.pow(0.7, 2) // 0.48999999999999994 x = new BigNumber(0.7) x.exponentiatedBy(2) // '0.49' BigNumber(3).pow(-2) // '0.11111111111111111111'
比较大小: .comparedTo(n [, base]) ⇒ number
比较结果,参考如下表:
1 操作数>n -1 操作数<n 0 操作数==n null 操作数或者n不是数字
举例:
x = new BigNumber(Infinity) y = new BigNumber(5) x.comparedTo(y) // 1 x.comparedTo(x.minus(1)) // 0 y.comparedTo(NaN) // null y.comparedTo('110', 2) // -1