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.
262 lines
6.8 KiB
262 lines
6.8 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(".");
|
|
|
|
entity = entity.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
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 {
|
|
val = val.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
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
|
|
}
|