LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

[点晴永久免费OA]Math.sumPrecise:0.1 + 0.2 等于 0.3 了:JavaScript 的浮点精度问题,有救了

admin
2026年5月11日 10:4 本文热度 101

你一定记得JS里的这个问题,或许以前面试的时候经常被问到过

0.1 + 0.2
// 0.30000000000000004

这不是 bug,是 IEEE 754 浮点数标准的"特性"。JavaScript 用 64 位浮点数表示所有数字,而 0.1 和 0.2 在二进制里都是无限循环小数,存进去的时候就已经丢了精度。两个"不精确"的数加在一起,误差就暴露了。

这个问题存在了 25 年。每个 JavaScript 开发者都踩过,每个新手教程都会提一嘴,然后告诉你"用 toFixed 修一下"或者"乘以 100 转成整数再算"。

现在,ECMAScript 2026 终于给了一个正经的解决方案:Math.sumPrecise

不只是 0.1 + 0.2 的问题

你可能觉得 0.1 + 0.2 的问题不痛不痒,但如果场景变复杂了呢。

想象一个场景:你在做一个财务报表,需要把 1000 笔交易金额加起来。每笔金额都是小数,比如 12.50、3.75、89.99 这种。

你大概率会这么写:

const total = transactions.reduce((sum, t) => sum + t.amount, 0);

看起来没问题对吧?但这段代码的误差会随着交易数量累积。每做一次加法,中间结果就可能丢一点点精度。1000 笔下来,最终结果可能差了 0.01 甚至更多。

在财务场景里,一分钱的误差就够让你半夜被叫起来排查 bug 了。

更极端的例子:

const numbers = [1e20, 0.1, -1e20];
let
 sum = 0;
for
 (const n of numbers) {
  sum += n;
}
console
.log(sum); // 0

1e20 + 0.1 的中间结果是 1e20,因为 0.1 在 1e20 面前实在太小了,直接被吞掉了。然后 1e20 + (-1e20) = 0。0.1 就这么凭空消失了。

这不是理论上的 corner case。在数据分析、科学计算、游戏物理引擎里,这种"大数吃小数"的问题每天都在发生。

Math.sumPrecise:用更聪明的算法算加法

ES2026 的 Math.sumPrecise 就是来解决这个问题的。

用法很简单:

Math.sumPrecise([0.1, 0.2])
// 0.3


Math
.sumPrecise([1e20, 0.1, -1e20])
// 0.1

它接受一个可迭代对象(数组、Set、Generator 都行),返回精确的求和结果。

背后的算法叫 Shewchuk 算法,1996 年由 Jonathan Shewchuk 提出。核心思路是:不直接把数字加在一起,而是用一个数组记录所有的"误差项"。每次加法的时候,把真正的误差存起来,最后再把所有误差补偿回去。

打个比方:普通加法就像是你拿着一个有刻度误差的尺子量东西,量一次误差一点,量 100 次误差就大了。而 Shewchuk 算法是每次量完都记下"这次差了多少",最后把所有偏差加起来一次性修正。

所以它能做到"最大精度正确"——结果等价于你用无限精度算完再转回浮点数。

实际用法

基础求和

// 以前
const
 total = prices.reduce((a, b) => a + b, 0);

// 现在

const
 total = Math.sumPrecise(prices);

配合数组方法

// 筛选后再求和
const
 activeTotal = Math.sumPrecise(
  transactions
    .filter(t => t.status === 'active')
    .map(t => t.amount)
);

接受任何可迭代对象

// Generator
function
* range(start, end) {
  for
 (let i = start; i <= end; i++) yield i;
}

Math
.sumPrecise(range(1, 100)); // 5050

// Set

const
 scores = new Set([85, 92, 78, 95, 88]);
Math
.sumPrecise(scores); // 438

注意事项

1. 只接受数字

Math.sumPrecise([1, '2', 3])
// TypeError: value is not a number

跟 Math.max 不同,它不会悄悄把字符串转成数字。给了非数字就直接报错。这是好事,早期暴露类型问题比运行时出错强。

2. 空数组返回 -0

Math.sumPrecise([])
// -0

为什么是 -0 而不是 0?因为 -0 是浮点数加法的"单位元"。-0 + x === x 对任何 x 都成立,保证了 Math.sumPrecise([]) + Math.sumPrecise(arr) 和 Math.sumPrecise(arr) 结果一致。

这个设计细节很数学,但如果你的业务逻辑对 -0 和 0 有区分(比如 JSON 序列化),注意一下。

3. 性能

Shewchuk 算法比直接加法慢。它需要维护误差项数组,处理溢出边界情况。对于小数组(几十个元素),差距不大。对于大数组,差距会明显一些。

所以:日常小量数据用 reduce 没问题。涉及精度要求的场景(财务、科学计算、统计分析),用 Math.sumPrecise

浏览器支持

ES2026 的提案在 2025 年 7 月达到 Stage 4,现在各浏览器的支持情况:

  •  Chrome 137+ ✅
  •  Firefox 137+ ✅
  •  Safari 18.4+ ✅
  •  Node.js 23+ ✅
  •  Bun ✅

TypeScript 暂时还没有内置类型定义,需要自己加一行:

// global.d.ts
interface
 Math {
  sumPrecise
(items: Iterable<number>): number;
}

这个特性值得用吗?

值得,但要看场景。

如果你的代码只是做简单的加减,用户看到的数字都是整数或者小数点后两位,reduce 完全够用。浮点误差在这些场景里基本不可见。

但如果你在做:

  • 财务计算(金额累加、税费计算)
  • 数据分析(大量浮点数的统计汇总)
  • 科学计算(物理模拟、信号处理)
  • 游戏开发(物理引擎里的位置/速度累加)

Math.sumPrecise 是更安全的选择。它不是性能最好的,但它是精度最高的。在这些场景里,精度比性能重要得多。

而且用法太简单了,直接把 reduce((a, b) => a + b, 0) 替换成 Math.sumPrecise(arr) 就行。一行代码的改动,换来的是可验证的精度保证。

最后

JavaScript 的浮点精度问题从 1995 年就存在了。25 年来,开发者们用各种 workaround 应对:乘 100 转整数、用 toFixed、引入 decimal.js 这样的第三方库。

现在语言层面终于给了一个原生方案。不是修复浮点数本身(那会破坏整个生态),而是提供一个"精确求和"的工具。

这是一个小特性,但解决了真实存在了 25 年的痛点。

下次你要在代码里做浮点数累加的时候,试试 Math.sumPrecise


阅读原文:https://mp.weixin.qq.com/s/Zw3Z21FVq5iQXIJ0BdXJFA


该文章在 2026/5/11 10:04:26 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-2  粤公网安备44030602007207号