相关的参考文档
Math.js是一个为javascript 和 node扩展math功能的库;
目录
math.js与小数
math.js 处理小数是使用bignumber 这个数据类型,但是其实质是用了decimal.js来处理的,所以我们如果要查看math.js处理小数的运算,可以直接看decimal.js
math.js中bignumber进行小数据的加减乘除运算的实质就是,使用人的思维进行运算,而非 转换二进制-> 运算 -> 返回十进制 这种计算机的方式
BigNumber
前面我们写到,BigNumber 这个类型其实是decimal.js,我们看代码或者看文档可以看到,其实bignumber是Decimal.clone()这个闭包返回的构造方法; 实例化后的对象是这样的:
let m = math.bignumber(1.234)
let m = math.bignumber(1.234)
undefined674584140@qq.com
m
{
constructor: ƒ a(e), //Decimal.clone()返回的类,
d: (2) [1, 2340000], // m的数值信息
e: 0, // 参考IEEE 754中exponse偏移量,以个位为基准
s: 1, // 参考IEEE 754中的sign位 1 为正,-1为负(注意IEEE 754中sign 只有0 = 正数, 1 = 负数,因为Math.pow(-1,n),n=0时结果是1,n=1时结果是-1 );
}
decimal实例的字段说明
decimalInstance.constructor
可以理解为就是Decimal.close()返回的构造函数,方便后面的计算中随时实例他一个decimal实例出来;
decimalInstance.d
其中字段d是decimal的核心部分,理解了d就差不多可以理解decimal.js是如何处理小数问题的了。 d是一个最大四个元素的二维数组,里面的元素最多有7个元素,记录了数字从第一个非0数字开始的最多21位数字。 举个例子
math.bignumber(.1).d
[1000000]
math.bignumber(1.1).d
(2) [1, 1000000]
math.bignumber(1.12345678).d
(3) [1, 1234567, 8000000]
math.bignumber(987654321.12345678).d
(3) [98, 7654321, 1234568]
math.bignumber(87654321.12345678).d
(4) [8, 7654321, 1234567, 8000000]
decimalInstance.e
e是属于偏移量,是我们对于d的补充,因为我们的d是从第一位非0的数字开始的,所以在e则负责提供d处理的位数;
decimalInstance.s
s属于标志位,1代表正数,-1代表负数;(需要注意与IEEE 754sign位的差异,因为IEEE 754是二进制数,所以它需要用0和1代表正负,所以IEEE 754中0是正数,1是负数)
BigNumber 转换成 number
其实质是Decimal 对象的valueOf 和 toString方法,不过看了源码,不管哪个,返回的结果都是string而非numbr
number 转换成 BigNumber
其实质是实例化一个decimal.js 对象:
- Decimal 构造函数是通过decimal.js 的clone 这个闭包方法取得的;
- math.bignumber方法其实就是调用了new Decimal(val);
new Decimal(val)的核心过程:
/**
* 把数据进行处理,并返回结果Decimal 实例
* @param {Decimal} x 初始化了的实例,已经计算好s了
* @param {String} str 入参
* @return {Decimal} 结果实例
**/
function parseDecimal(x, str) {
var e, i, len;
// 找出小数点的位置,如果有,则清除小数点
if ((e = str.indexOf('.')) > -1) str = str.replace('.', '');
// 如果没有小数点,则理解为小数点在数字最后一位
if (e < 0) {
// Integer.
e = str.length;
}
// Determine leading zeros.
// 计算str第一个非0数字前面的0的个数,累计入i中;
for (i = 0; str.charCodeAt(i) === 48; i++);
// 清除str后面的0,记录str.length
for (len = str.length; str.charCodeAt(len - 1) === 48; --len);
str = str.slice(i, len);
// 开始了...
// 这里 i 第一个非0数字前面的0的个数,
// e 是 小数点的位置,此两参数是用作计算decimal.e位移量的
// len
if (str) {
// 是为了计算出真实有效的数字的长度
len -= i;
// 计算出decimal.e 偏移量
x.e = e = e - i - 1;
// 重置decimal.d 数组
x.d = [];
// 开始转换
// e is the base 10 exponent.
// i is where to slice str to get the first word of the digits array.
i = (e + 1) % LOG_BASE;
// 如果是0.0X的情况,
if (e < 0) i += LOG_BASE;
// 如果有多个分组的情况,即从第一个非0数字起,总共有超过7位数字的情况
if (i < len) {
// 先取第一组
if (i) x.d.push(+str.slice(0, i));
// 遍历中间部分
for (len -= LOG_BASE; i < len;) x.d.push(+str.slice(i, i += LOG_BASE));
// 为最后一组准备
str = str.slice(i);
i = LOG_BASE - str.length;
} else {
// else 里面的是只有一组数据的情况
i -= len;
}
// 遍历最后一组
for (; i--;) str += '0';
x.d.push(+str);
if (external) {
// Overflow?
if (x.e > x.constructor.maxE) {
// Infinity.
x.d = null;
x.e = NaN;
// Underflow?
} else if (x.e < x.constructor.minE) {
// Zero.
x.e = 0;
x.d = [0];
// x.constructor.underflow = true;
} // else x.constructor.underflow = false;
}
} else {
// Zero.
x.e = 0;
x.d = [0];
}
return x;
}