For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/proposal-math-sum.md.
  • 简体中文
  • Math.sumPrecise S4

    中文标题:Math.sumPrecise:精确求和

    提案概览
    提案速览

    该提案向 JavaScript 添加 Math.sumPrecise 方法,用于对多个值求和,其动机是对列表求和的常见需求以及使用 Array.prototype.reduce 的局限。它不采用朴素累加,而是接受一个可迭代对象,并使用更精确的算法返回和。它会拒绝非数字值,且空列表的和为 -0。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    Math.sumPrecise

    一个向 JavaScript 添加用于对多个值求和的方法的提案。

    状态

    作者:Kevin Gibbons

    提案负责人:Kevin Gibbons

    本提案处于 TC39 流程的第 4 阶段:它已完成并包含在标准中。任何进一步的更改都需要通过常规流程在单独的提案中进行。此仓库已归档。

    动机

    对列表求和是一种非常常见的操作,也是 Array.prototype.reduce 少数剩余用例之一。更好的做法是让用户直接表达该操作。

    此外,使用更巧妙的算法,对一组浮点数求和可以比朴素的 .reduce((a, b) => a + b, 0) 方法更精确;很少有 JavaScript 程序员意识到这一点(即使在他们当中,大多数人也不会费心去做)。我们可以让人们更容易采用更好的选择。

    提案

    添加一个接受可迭代对象的 Math.sumPrecise 方法,该方法使用比朴素求和更精确的算法返回可迭代对象中各个值的和。

    let values = [1e20, 0.1, -1e20];
    
    values.reduce((a, b) => a + b, 0); // 0
    
    Math.sumPrecise(values); // 0.1

    问题

    采用哪种算法?

    本提案没有规定任何特定算法,而是要求得到最大程度正确的答案——也就是说,如果你进行任意精度算术,然后把结果转换回浮点数,就会得到的答案。这可以在实际上不需要任意精度算术的情况下完成。实现这一点的一种方式见 Shewchuk '96,我已将其用 JS 实现(另有一些处理中间溢出的细节)。其他策略也是可能的。

    Python 的 math.fsum 目前使用相同算法实现(不过不处理中间溢出)。

    Radford M. Neal 在 Fast exact summation using small and large superaccumulators 中给出了一种更近期的算法,其采用 MIT 许可的代码可在此处获取。

    接受可迭代对象还是可变参数?

    Math.max 的先例暗示应采用可变参数形式,但这实际上并不是你想要的方式——一旦你的列表超过几万个元素,你很可能会使栈溢出并得到 RangeError。

    因此本提案只包含接受可迭代对象的形式。

    命名

    Math.sum 是显而易见的名称,但人们并不清楚它将采用与朴素求和不同的(更慢的)算法。将其称为 Math.sumPrecise 是为了提请人们注意这一差异。

    这个方法是应该把值强制转换为数字,还是在给定非数字值时抛出错误?

    它将拒绝非数字值,这与 Math.max 的先例不同。

    空列表的和是 0 还是 -0?

    是 -0。这是浮点加法的单位元。这一选择确保 Math.sumPrecise([]) + Math.sumPrecise(foo) 总是与 Math.sumPrecise(foo) 给出相同的答案。

    它应该支持 BigInt 吗?

    不 —— Math.sumPrecise([]) 返回 Number -0(或 0)很重要,这意味着当 bigints 为空时,5n + Math.sumPrecise(bigints) 会抛出错误,这很糟糕。

    我们可以为 BigInt 求和设置单独的方法。根据上面命名讨论的结果,我会倾向于 BigInt.sum 或 BigInt.sumFrom。但这样的方法不会成为本提案的一部分。

    乘积呢?

    这种情况出现得少得多,因此我目前不提议它。