比如计算百分比,乘除法,js计算有小数点,精度会丢失,通过toFixed可以截取小数后几位,但是会四舍五入,而toPrecision则需要指定数字长度。现在要保留小数点后2位,请问怎么处理
let n = 123.123;
console.log(n.toPrecision(4)) // 123.1
let n = 123.126;
console.log(n.toFixed(2)) // 123.13
期望结果:
let n = 123.1266646635525563;
// 123.12
比如计算百分比,乘除法,js计算有小数点,精度会丢失,通过toFixed可以截取小数后几位,但是会四舍五入,而toPrecision则需要指定数字长度。现在要保留小数点后2位,请问怎么处理
let n = 123.123;
console.log(n.toPrecision(4)) // 123.1
let n = 123.126;
console.log(n.toFixed(2)) // 123.13
期望结果:
let n = 123.1266646635525563;
// 123.12
类似的方法,函数其实很多,比如说可以用Math.floor函数,将结果向下取整,最后再除以100。这样可以保留小数点后2位并且不进行四舍五入。
可以这样:
let n = 123.1266646635525563;
let result = Math.floor(n * 100) / 100;
console.log(result); // 123.12
或者用字符串的substring函数截取小数点后的位数也是可以的,相对刚刚纯数字的方法多了一个转换步骤,相对复杂一点点,但有时候用得上,将len n转换为s字符串,然后在里面找到小数点的位置,最后再输出。
let n = 123.1266646635525563;
let result = n.toString();
let decimalIndex = result.indexOf('.') + 1;
result = result.substring(0, decimalIndex + 2);
console.log(parseFloat(result)); // 123.12
自定义计算函数:
function multiply(a, b) {
let m = 0;
const s1 = a.toString();
const s2 = b.toString();
try { m += s1.split(".")[1].length } catch (e) {}
try { m += s2.split(".")[1].length } catch (e) {}
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
function divide(a, b) {
let t1 = 0;
let t2 = 0;
try { t1 = a.toString().split(".")[1].length } catch (e) {}
try { t2 = b.toString().split(".")[1].length } catch (e) {}
const r1 = Number(a.toString().replace(".", ""));
const r2 = Number(b.toString().replace(".", ""));
return multiply((r1 / r2), Math.pow(10, t2 - t1));
}
function add(a, b) {
let c = 0;
let d = 0;
try { c = a.toString().split(".")[1].length } catch (e) {}
try { d = b.toString().split(".")[1].length } catch (e) {}
const m = Math.pow(10, Math.max(c, d));
return (multiply(a, m) + multiply(b, m)) / m;
}
function subtract(a, b) {
let c = 0;
let d = 0;
try { c = a.toString().split(".")[1].length } catch (e) {}
try { d = b.toString().split(".")[1].length } catch (e) {}
const m = Math.pow(10, Math.max(c, d));
const n = (c >= d) ? c : d;
return ((multiply(a, m) - multiply(b, m)) / m).toFixed(n);
}
let n = 123.1266646635525563;
let result = multiply(n, 100);
result = Math.floor(result);
result = divide(result, 100);
console.log(result); // 123.12
// 示例:使用 Bigint 进行计算
const num1 = BigInt(0.1 * 10);
const num2 = BigInt(0.2 * 10);
const result = (num1 + num2) / BigInt(10);
console.log(result.toString()); // 输出: 0.3
我的经验是解决计算精度 直接用现成的库去处理 比如上面说的BigNumber decimal等等, 我个人更喜欢jsbi-calculator, 加减乘除写起来简单 也不用像其他几个库那样要设置 引入的体积也更小
而保留小数主要看是不是要四舍五入或者银行家舍入 比如自己写一个舍入的方法 或者用lodash的round等 看个人选择
个人用的比较多的方法,加减除
function preciseAdd(num1, num2) {
let m = 0, s1 = num1.toString(), s2 = num2.toString();
try { m += s1.split(".")[1].length } catch (e) { }
try { m += s2.split(".")[1].length } catch (e) { }
return Number(s1.replace(".", "")) + Number(s2.replace(".", "")) / Math.pow(10, m);
}
function preciseMultiply(num1, num2) {
let m = 0, s1 = num1.toString(), s2 = num2.toString();
try { m += s1.split(".")[1].length } catch (e) { }
try { m += s2.split(".")[1].length } catch (e) { }
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
function preciseDivide(num1, num2) {
let t1 = 0, t2 = 0, s1 = num1.toString(), s2 = num2.toString();
try { t1 = s1.split(".")[1].length } catch (e) { }
try { t2 = s2.split(".")[1].length } catch (e) { }
let r1 = Number(s1.replace(".", ""));
let r2 = Number(s2.replace(".", ""));
return (r1 / r2) * Math.pow(10, t2 - t1);
}
/**
* 数字运算(主要用于小数点精度问题)
* [see](https://juejin.im/post/6844904066418491406#heading-12)
* @param {number} a 前面的值
* @param {"+"|"-"|"*"|"/"} type 计算方式
* @param {number} b 后面的值
* @example
* 可链式调用
* const res = computeNumber(1.3, "-", 1.2).next("+", 1.5).next("*", 2.3).next("/", 0.2).result;
*/
export function computeNumber(a, type, b) {
/**
* 获取数字小数点的长度
* @param {number} n 数字
*/
function getDecimalLength(n) {
const decimal = n.toString().split(".")[1];
return decimal ? decimal.length : 0;
}
/**
* 修正小数点
* @description 防止出现 `33.33333*100000 = 3333332.9999999995` && `33.33*10 = 333.29999999999995` 这类情况做的处理
* @param {number} n
*/
const amend = (n, precision = 15) => parseFloat(Number(n).toPrecision(precision));
const power = Math.pow(10, Math.max(getDecimalLength(a), getDecimalLength(b)));
let result = 0;
a = amend(a * power);
b = amend(b * power);
switch (type) {
case "+":
result = (a + b) / power;
break;
case "-":
result = (a - b) / power;
break;
case "*":
result = (a * b) / (power * power);
break;
case "/":
result = a / b;
break;
}
result = amend(result);
return {
/** 计算结果 */
result,
/**
* 继续计算
* @param {"+"|"-"|"*"|"/"} nextType 继续计算方式
* @param {number} nextValue 继续计算的值
*/
next(nextType, nextValue) {
return computeNumber(result, nextType, nextValue);
},
};
}
let m = 123.1923
let a = n.toString()
console.log(a.indexOf('.')===-1?(+a).toFixed(2):a.slice(0,a.indexOf('.')+3))
不四舍五入只能借助字符串 比如截取
消除js计算误差add、reduce是传入的加减数字,reduce是被加数(被减数),s是加减符号
formatNum(add, reduce, s, num) {
let m = Math.pow(10, num); //num是10的次幂
let res = s == '+' ? (add * m + reduce * m) / m : (add * m - reduce * m) / m;
return Math.round(res * m) / m;
},
let n= 123.123
console.log(this.formatNum(n, 0, '-', 2)) // 123.12
8 回答5.8k 阅读✓ 已解决
9 回答9.2k 阅读
6 回答4.8k 阅读✓ 已解决
5 回答3.5k 阅读✓ 已解决
3 回答10.3k 阅读✓ 已解决
4 回答7.9k 阅读✓ 已解决
7 回答9.8k 阅读
所谓“没有完美的算法,只有合适的场景”,浮点数字(小数)就是个很好的例子。
我们知道,按照目前的规范,JS 无法直接完美的处理小数。但是实际上,我们往往也不需要它“完美”处理小数。比如,以记账为例,我们通常只关心小数点后两位,也就是“分”,再细的,我们关注它意义不大,投入产出比太低。那么我们就可以把所有数字 * 100,取整后保存,带来的误差非常低。
所以,讨论小数精度问题,要看具体的需求。只要满足需求,怎么保存都可以。