BITcoinimt_h5
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

258 lines
6.7 KiB

/**
* 数学模块,主要针对浮点型数据,在运算时的精度问题:
* 1| 两个小数相加 0.1 + 0.2 = 0.30000000000000004
* 2| 两个小数相乘 2.12 * 3.14 = 6.6568000000000005;
* 3| ...
* 4| ...
* 5| 数值长度大于13位时 显示科学计数法
* @author lautin
* @created 2019-11-19 17:15:04
*/
/**
* 检测小数点精度
* @param {number} num 传入的数值
*/
function countDecimals(num) {
let precision;
try {
precision = num.toString().split(".")[1].length;
} catch (e) {
precision = 0;
}
return precision;
}
/**
* js在以下情景会自动将数值转换为科学计数法:
* 小数点前的数字个数大于等于22位;
* 小数点前边是0,小数点后十分位(包含十分位)之后连续0的个数>=6时
* @param {number} 科学计数法显示的数值 1.2e-8
* @return {number} 返回实际的数值 1.00000002
*/
function scientific2No(val) { //-7.50375e-8
// 正则匹配科学计数法的数字
if (/\d+\.?\d*e[+-]*\d+/i.test(val)) {
let zero = '0',
parts = String(val).toLowerCase().split('e'), // ['-7.50375', '-8']
e = parts.pop(), // 存储指数 -8
l = Math.abs(e), // 0的个数 8
sign = e / l, // 判断正负 -
// 将系数按照小数点拆分
coeff_array = parts[0].split('.'); // [-7, 50375] 去除中间的.
// 如果恰好为8位 那么第二个数默认为undefined 需要重置为''
if (!coeff_array[1]) coeff_array[1] = '';
if (sign === -1) { // 小数
// debugger;
// 正数或者负数
if (coeff_array[0] < 0) {
val = '-' + zero + '.' + zero.repeat(l - 1) + coeff_array[0].slice(1) + coeff_array[1];
} else {
val = zero + '.' + zero.repeat(l - 1) + coeff_array.join(''); //拼接字符串,如果是小数,拼接0和小数点
}
} else {
let dec = coeff_array[1];
// 如果是整数,将整数除第一位之外的非零数字计入位数,相应的减少0的个数
if (dec) l = l - dec.length;
// 拼接字符串,如果是整数,不需要拼接小数点
val = coeff_array.join('') + new Array(l + 1).join(zero);
}
}
try {
return val.toString();
} catch (e) {
return '';
}
}
/**
* 截取小数点后n位
* @param {number} val 截取的数值
* @param {number} scale 保留的小数点位数
*/
function omitTo(val, scale) {
// 转化科学计数法
val = scientific2No(val);
let ret;
// 检测浮点数
if (val.toString().indexOf(".") > -1) {
// 提取实体集和精度值
let [entity, precisionVal] = val.toString().split(".");
if (precisionVal.length > scale) {
// trunc() 方法会将数字的小数部分去掉,只保留整数部分。
let tmp = scientific2No(Math.trunc(val * Math.pow(10, scale)));
// 处理零值
if (tmp == 0) ret = scientific2No('0.' + '0'.repeat(scale));
else {
// ret = tmp / ;
ret = division(tmp, Math.pow(10, scale));
try { // 小数
let [a, b] = ret.toString().split(".");
a = scientific2No(a),
b = scientific2No(b);
if (b.length < scale) {
ret = a + '.' + b.padEnd(scale, '0');
}
} catch (e) { // 整数
ret = ret + '.' + '0'.padEnd(scale, '0');
}
}
} else if (precisionVal.length == scale) { // 精度
ret = val;
} else {
// 补全小数点
ret = entity + '.' + precisionVal.padEnd(scale, '0');
}
// 检测整型值
} else ret = val + '.' + '0'.repeat(scale);
// 去除末尾可能产生的多余的.
if (ret.toString().endsWith('.')) ret = ret.slice(0, -1);
return ret;
}
/**
* 计算两个数的和
* @param {number} num1
* @param {number} num2
*/
function add(num1, num2, scale = null) {
num1 = scientific2No(num1);
num2 = scientific2No(num2);
let amplification, // 放大率
precision1 = countDecimals(num1), // 精度1
precision2 = countDecimals(num2); // 精度2
amplification = Math.pow(10, Math.max(precision1, precision2));
// 先放大再相加,然后除以放大率
let val = (num1 * amplification + num2 * amplification) / amplification;
// 转化科学计数法
let result = scientific2No(val);
// 控制显示长度
if (scale) result = omitTo(result, scale);
return result;
}
/**
* 计算两个数的差值
* @param {number} num1
* @param {number} num2
*/
function subtr(num1, num2, scale = null) {
num1 = scientific2No(num1);
num2 = scientific2No(num2);
let amplification, // 放大率
precision1 = countDecimals(num1), // 精度1
precision2 = countDecimals(num2); // 精度2
let precision = Math.max(precision1, precision2)
amplification = Math.pow(10, precision);
// 动态控制精度长度
let val = ((num1 * amplification - num2 * amplification) / amplification).toFixed(precision);
// 转化科学计数法
let result = scientific2No(val);
// 控制显示长度
if (scale) result = omitTo(result, scale);
return result;
}
/**
* 计算两个数的乘积
* @param {number} num1
* @param {number} num2
*/
function multiple(num1, num2, scale = null) {
num1 = scientific2No(num1);
num2 = scientific2No(num2);
let precision = 0;
precision += countDecimals(num1);
precision += countDecimals(num2);
let val = Number(num1.toString().replace(".", "")) * Number(num2.toString().replace(".", "")) / Math.pow(10, precision);
let result = scientific2No(val);
if (scale) result = omitTo(result, scale);
return result;
}
/**
* 两个数相除
* @param {number} num1
* @param {number} num2
* @param {optional} d
*/
function division(num1, num2, scale = null) {
num1 = scientific2No(num1);
num2 = scientific2No(num2);
let precision1 = countDecimals(num1),
precision2 = countDecimals(num2),
m = precision1 > precision2 ? precision1 : precision2;
// 两个整数相除 无需计算精度
if (m <= 1) m = 1;
let val = multiple(num1, m) / multiple(num2, m);
let result = scientific2No(val);
if (scale) result = omitTo(result, scale);
return result;
}
Object.assign(Math, {
countDecimals,
add,
subtr,
multiple,
division,
scientific2No,
omitTo
})
export default {
countDecimals,
add,
subtr,
multiple,
division,
scientific2No,
omitTo
}