这篇文章发布于 2026年08月14日,星期五,19:20,归类于 CSS相关。 阅读 43 次, 今日 42 次 没有评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12345
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
一、数组求和新方法
之前大家数组求和,肯定多半都是使用的数组的reduce方法.
例如:
const sum = [1, 2, 3, 4, 5, 6].reduce((a, b) => a + b, 0); // 结果 sum: 21
如下截图所示:
![]()
在大多数时候,上面的求和计算是没问题的。
但是,如果计算的数值超大,超过了JS的整数范围;或者是小数,就可能出问题。
比方说:
const arr = [0.1, 0.2, 0.3]; // reduce求和 const sum = arr.reduce((prev, curr) => prev + curr, 0); console.log(sum); // 预期:0.6 // 实际输出:0.6000000000000001
const values = [1e20, 0.1, -1e20];
const sum = values.reduce((a, b) => a + b, 0);
console.log("sum", sum);
// 预期:sum 0.1
// 实际:sum 0
这怎么弄?
在过去,开发人员会把数组每一项都乘以100,1000这种,然后再计算,例如:
const sum = [0.1, 0.2, 0.3].reduce((prev, curr) => prev + curr * 100, 0) / 100; console.log(sum); // 预期:0.6 // 实际输出:0.6
或者使用Number.toPrecision()方法对输出值进行精度格式化处理。
例如:
const moneyList = [0.1, 0.2, 0.7, 0.3]; const total = moneyList.reduce((s, v) => s + v, 0); console.log(total); // 预期:1.3 // 实际:1.2999999999999998 console.log(Number(total.toPrecision(16))); // 预期:1.3 // 实际:1.3
然而,上面这些方法都不是完全之策,例如 toPrecision() 方法在下面这个案例中就是无效的:
[1e20, 0.1, -1e20].reduce((s, v) => s + v, 0).toPrecision(16) // 结果还是0.000000000000000 ❌
那怎么办呢?
有没有什么一了百了的方法呢?
有!
那就是Math对象新增的sumPrecise()方法。
一键求和,同时保证精度。
二、数组求和利器Math.sumPrecise()
从今往后,数组求和,再也无需使用reduce()方法了,直接Math.sumPrecise()方法一把梭。
使用非常简单,例如:
const sum = Math.sumPrecise([1, 2, 3, 4, 5, 6]); // 结果 sum: 21
同时,再也没有小数计算精度不准的问题了。
const sum = Math.sumPrecise([0.1, 0.2, 0.3]); // 结果 sum: 0.3
超大的整数计算也不会有问题了!
const values = [1e20, 0.1, -1e20];
const sum = Math.sumPrecise(values);
console.log("sum", sum);
// sumTwo 0.1

三、兼容性、Polyfill与结语
各大浏览器对Math.sumPrecise()方法的支持非常积极,目前领先的几个版本都已经支持。

对于不兼容的浏览器,可以引入Polyfill代码,参考示意:
if (typeof Math.sumPrecise !== 'function') {
Object.defineProperty(Math, 'sumPrecise', {
value: function sumPrecise(items) {
// 参数校验
if (items == null) {
throw new TypeError('Math.sumPrecise requires an iterable argument.');
}
if (typeof items[Symbol.iterator] !== 'function') {
throw new TypeError('Math.sumPrecise: argument is not iterable.');
}
var sum = 0;
var compensation = 0; // Neumaier 补偿值
var count = 0;
var hasNegZero = false;
var hasNonZero = false;
for (var value of items) {
var num = Number(value);
// 任意元素为 NaN,直接返回 NaN
if (Number.isNaN(num)) {
return NaN;
}
// 任意元素为 ±Infinity,处理无穷大相消问题
if (!isFinite(num)) {
if (sum === Infinity && num === -Infinity) return NaN;
if (sum === -Infinity && num === Infinity) return NaN;
sum = num;
hasNonZero = true;
count++;
continue;
}
// 记录 -0
if (num === 0) {
if (!isFinite(sum) || hasNonZero) {
// 有实际数值时 -0 不影响结果
} else if (Object.is(num, -0)) {
hasNegZero = true;
}
count++;
continue;
}
hasNonZero = true;
// Neumaier 补偿求和核心
var t = sum + num;
if (Math.abs(sum) >= Math.abs(num)) {
compensation += (sum - t) + num;
} else {
compensation += (num - t) + sum;
}
sum = t;
count++;
if (count > 2 ** 53) {
throw new RangeError('Math.sumPrecise: iterable is too large.');
}
}
// 全是零(含 -0)的情况
if (!hasNonZero) {
return hasNegZero ? -0 : 0;
}
// 有无穷大时,补偿值无意义,直接返回
if (!isFinite(sum)) {
return sum;
}
return sum + compensation;
},
writable: true,
configurable: true,
});
}
边界测试用例:
// 精度问题
Math.sumPrecise([0.1, 0.2, 0.3]) // 0.6
Math.sumPrecise([0.1, 0.1, 0.1, 0.1,
0.1, 0.1, 0.1, 0.1,
0.1, 0.1]) // 1
// 特殊值
Math.sumPrecise([]) // 0
Math.sumPrecise([-0]) // -0
Math.sumPrecise([-0, -0]) // -0
Math.sumPrecise([-0, 0]) // 0(正零优先)
Math.sumPrecise([Infinity, -Infinity]) // NaN
Math.sumPrecise([NaN, 1]) // NaN
Math.sumPrecise([Infinity, 1, 2]) // Infinity
// 非数组可迭代
Math.sumPrecise(new Set([1, 2, 3])) // 6
Math.sumPrecise((function*() {
yield 0.1; yield 0.2; yield 0.3;
})()) // 0.6
看起来还挺像那么回事。
好了,就说这么多吧。
大家周末愉快。

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12345
(本篇完)
- ES5中新增的Array方法详细说明 (0.440)
- 聊聊JS DOM变化的监听检测与应用 (0.231)
- Proxy是代理,Reflect是干嘛用的? (0.231)
- 不借助Echarts等图形框架原生JS快速实现折线图效果 (0.220)
- 深入JS getRandomValues和Math.random方法 (0.220)
- JS数组的copyWithin()语法我看了好几遍才懂 (0.220)
- 实用的JS对象分组静态方法Object.groupBy() (0.176)
- Object.is/===、数组at/直接索引、substring/slice的区别 (0.176)
- JS Set新支持了intersection, union, difference等方法 (0.176)
- 输入框value属性赋值触发js change事件的实现 (0.165)
- 深入 JS new Function 语法 (RANDOM - 0.066)