首页
/ core-js 中 ECMAScript Math 模块全解析:19 个数学方法、入口点与精确浮点求和实现

core-js 中 ECMAScript Math 模块全解析:19 个数学方法、入口点与精确浮点求和实现

2026-09-11 20:07:52作者:裴麒琰

本文以 core-js 官方文档 docs/web/docs/features/ecmascript/math.md 为主体,系统梳理 core-js 对 ECMAScript Math 内置对象全部扩展模块的组成、类型签名、加载入口,并结合仓库源码剖析 Math.sumPreciseMath.hypotMath.f16round 等方法的底层实现原理。读者读完可掌握如何按需引入 Math 扩展、正确理解各方法的边界语义,以及核心算法在 core-js 中是如何落地的。

一、模块总览:core-js 覆盖的 19 个 Math 方法

core-js 将 ECMAScript 规范(tc39.es/ecma262 下。文档中列出的完整模块清单如下:

其中绝大多数属于 ES2015 起逐步纳入规范的标准方法,而 esnext.math.sum-preciseMath.sumPrecise)是正在推进中的新提案(TC39 proposal-math-sum),core-js 以 esnext 前缀模块先行提供。从入口目录 packages/core-js/es/math/ 可以看到每个方法均有对应的独立入口文件(acosh.jshypot.jssum-precise.js……以及汇总入口 index.js),方便按需加载。

二、内置方法签名(TypeScript 视角)

文档给出了上述方法在 Math 命名空间下的类型签名。这些签名直接决定了调用约定与返回值的语义边界:

namespace Math {
  acosh(number: number): number;
  asinh(number: number): number;
  atanh(number: number): number;
  cbrt(number: number): number;
  clz32(number: number): number;
  cosh(number: number): number;
  expm1(number: number): number;
  fround(number: number): number;
  f16round(number: any): number;
  hypot(...args: Array<number>): number;
  imul(number1: number, number2: number): number;
  log1p(number: number): number;
  log10(number: number): number;
  log2(number: number): number;
  sign(number: number): 1 | -1 | 0 | -0 | NaN;
  sinh(number: number): number;
  sumPrecise(items: Iterable<number>): Number;
  tanh(number: number): number;
  trunc(number: number): number;
}

几个值得注意的语义要点:

  • sign 的返回值类型是五值联合 1 | -1 | 0 | -0 | NaN,这是唯一一个在签名中明确暴露符号位细节的方法。从 internals/math-sign.js 的实现看,core-js 优先复用原生 Math.sign(源码注释标记为 safe),否则使用 polyfill:n === 0 || n !== n ? n : n < 0 ? -1 : 1——即保留 -0+0NaN 的原始语义,负数为 -1,正数为 1
  • sumPrecise 接收 Iterable<number> 而非可变参数,且返回 Number。这是与普通 Math 方法最大的调用差异:Math.sumPrecise([1, 2, 3]) 而不是 Math.sumPrecise(1, 2, 3)
  • f16round 的入参标注为 any,因为半精度舍入同样遵循 Number 到 Number 的转换规则,可接受任意可转数值的输入。
  • hypot 是唯一接受任意数量参数(...args)的方法,与 Math.maxMath.min 的调用方式一致。

三、入口点:按需加载的四种命名空间与三种取值

文档给出了 Math 系列功能的完整入口点模式。核心形态为:

core-js(-pure)/es|stable|actual|full/math
core-js(-pure)/es|stable|actual|full/math/acosh
core-js(-pure)/es|stable|actual|full/math/asinh
core-js(-pure)/es|stable|actual|full/math/atanh
core-js(-pure)/es|stable|actual|full/math/cbrt
core-js(-pure)/es|stable|actual|full/math/clz32
core-js(-pure)/es|stable|actual|full/math/cosh
core-js(-pure)/es|stable|actual|full/math/expm1
core-js(-pure)/es|stable|actual|full/math/fround
core-js(-pure)/es|stable|actual|full/math/f16round
core-js(-pure)/es|stable|actual|full/math/hypot
core-js(-pure)/es|stable|actual|full/math/imul
core-js(-pure)/es|stable|actual|full/math/log1p
core-js(-pure)/es|stable|actual|full/math/log10
core-js(-pure)/es|stable|actual|full/math/log2
core-js(-pure)/es|stable|actual|full/math/sign
core-js(-pure)/es|stable|actual|full/math/sinh
core-js(-pure)/es|stable|actual|full/math/sum-precise
core-js(-pure)/es|stable|actual|full/math/tanh
core-js(-pure)/es|stable|actual|full/math/trunc

3.1 命名空间的选择语义

es | stable | actual | full 四个目录(对应 packages/core-js/espackages/core-js/stablepackages/core-js/actualpackages/core-js/full)代表 core-js 的四个入口层级,按包含范围从小到大排序:

入口 语义 典型场景
es 仅含 ECMAScript 规范内、且环境缺失的部分 只求标准兼容的最精简引入
stable 仅含已进入正式规范的内容 生产环境推荐,避开未定稿特性
actual 含已进入规范的 + 当前处于最新阶段(stage)的提案 想要最新特性但仍在规范轨道上
full 全部特性(含 esnext 级提案) 实验性质、追求完整

3.2 聚合入口与单方法入口

  • 聚合入口.../math 一次性加载全部 19 个方法(对应 packages/core-js/es/math/index.js),适合"整个 Math 扩展都要用"的场景。
  • 单方法入口.../math/acosh.../math/hypot 等按名加载,tree-shaking 友好,适合只需要个别方法、追求最小包体的场景。

3.3 pure 与全局版本

路径前缀中的 core-js(-pure) 表示同一入口同时适用于两个发布形态:

  • core-js(全局版):直接修补全局 Math 对象,packages/core-js 是默认发布包;
  • core-js-pure(纯净版):不污染全局对象,通过模块导出独立实现,用于库作者避免影响宿主环境,见 packages/core-js-pure

例如按需引入单个方法:

// 全局版:仅在缺失时修补 Math.log2
import 'core-js/es/math/log2';

// 纯净版:得到独立的实现
import log2 from 'core-js-pure/es/math/log2';

四、源码级深度解析:三个典型实现

4.1 Math.sumPrecise:基于 Shewchuk 算法的精确浮点求和

Math.sumPrecise 是当前文档示例中唯一展示实际运行效果的 esnext 级方法,其实现位于 packages/core-js/modules/es.math.sum-precise.js。文件头部注释明确说明:基于 Shewchuk 的精确浮点加法算法,改编自 TC39 proposal-math-sum 的 polyfill

从源码结构看,其核心设计包含三层:

  1. 异常状态机:先遍历可迭代对象,用 MINUS_ZERO / PLUS_INFINITY / MINUS_INFINITY / NOT_A_NUMBER / FINITE 五个状态跟踪 -0+Infinity-InfinityNaN 与普通有限数的传播规则。例如遇到 NaN 立即置为 NOT_A_NUMBER(最终返回 NaN),+Infinity-Infinity 同时出现则置为 NOT_A_NUMBER,符合 IEEE 754 语义。
  2. 参数校验if (++count > MAX_SAFE_INTEGER) throw new $RangeError(...) 防止超过 2^53 - 1 个元素;if (typeof n != 'number') throw new $TypeError(...) 强制要求元素均为 Number 类型。
  3. 两两求和的 partials 补偿算法twosum(x, y)x + y 拆成高位 hi 与误差项 lolo = y - (hi - x)),配合 overflow 计数处理超过 2^1023 的极端上溢场景,最终把"丢掉的精度"全部补偿回来。

单元测试 tests/unit-global/es.math.sum-precise.js 对该方法的验证覆盖了:自定义迭代器(createIterable([1, 2, 3]))、非可迭代入参抛 TypeError、非 Number 元素抛 TypeError[NaN][Infinity, -Infinity] 返回 NaN、空数组返回 -0,以及 [1e308, 1e308, 0.1, 0.1, 1e30, 0.1, -1e30, -1e308, -1e308] 这类灾难性抵消场景仍能精确得到 0.30000000000000004

4.2 Math.hypot:防上溢/下溢的缩放算法

packages/core-js/modules/es.math.hypot.js 的实现值得单独讲解,因为它并不是简单地对各参数平方求和再开方。源码中维护了当前最大绝对值 larg,对每个新参数:

  • larg < argdiv = larg / arg; sum = sum * div * div + 1; larg = arg;——先把旧比例折算进去,再更新基准;
  • 否则 div = arg / larg; sum += div * div;——按比例累加平方和。

最后返回 larg === Infinity ? Infinity : larg * sqrt(sum)。这种"先归一化、再缩放"的写法将中间结果始终控制在 [0, 1] 范围内,从而避免直接计算 1e200 * 1e200 时发生上溢为 Infinity,也不会因极小值平方而下溢为 0

该模块还通过 FORCED = $hypot(Infinity, NaN) !== Infinity 检测 Chrome 77 的已知 bug(V8 issue 9546,规范要求 Math.hypot(Infinity, NaN) 返回 Infinity),仅在宿主实现有缺陷时才强制覆盖,避免不必要的打补丁。

4.3 Math.f16round:半精度浮点舍入

Math.f16round 依赖通用浮点舍入工具 packages/core-js/internals/math-float-round.js。该工具接收三个浮点格式参数:FLOAT_EPSILONFLOAT_MAX_VALUEFLOAT_MIN_VALUE。模块 packages/core-js/modules/es.math.f16round.js 传入半精度常数:

var FLOAT16_EPSILON = 0.0009765625;        // 2^-10
var FLOAT16_MAX_VALUE = 65504;             // 半精度最大值
var FLOAT16_MIN_VALUE = 6.103515625e-05;   // 半精度最小正规格化数 2^-14

其舍入策略清晰可读:

  1. 绝对值小于 FLOAT_MIN_VALUE 的次正规数区间,用 roundTiesToEven(银行家舍入,见 internals/math-round-ties-to-even.js,实现为 n + 2^53 - 2^53 的经典 trick)做"最近偶数"舍入;
  2. 正常区间用 a = (1 + EPSILON/Number.EPSILON) * abs 然后 a - (a - abs) 构造舍入边界;
  3. 结果超过 FLOAT16_MAX_VALUE 或为 NaN 时返回带符号的 Infinity

Math.fround 同理,只是参数换为 32 位浮点常数,说明 core-js 用同一套内部工具统一实现了 froundf16round

4.4 其他方法的防御性实现

  • Math.imules.math.imul.js):将两个数按 16 位高低拆分相乘后重组,规避 JS 数字无法直接表达 32 位无符号乘积的精度问题;同时通过 FORCED 检测 WebKit 对大数计算错误或 arity 错误(imul.length !== 2)的情况,仅在必要时 polyfill。
  • Math.clz32es.math.clz32.js):n = x >>> 0 先转为无符号 32 位整数,再用 floor(log(n + 0.5) * LOG2E) 求出位数差,n === 0 时返回 32。
  • Math.signes.math.sign.js):直接复用内部工具 internals/math-sign.js,并优先取原生实现保证性能。

所有模块都通过统一的 $({ target: 'Math', stat: true }, ...) 导出机制注册到 Math 上,其中 stat: true 表示静态方法、forced 字段用于声明是否强制覆盖宿主原生实现,这是 core-js 整个模块体系(packages/core-js/modules 内 566 个文件)一致遵循的约定。

五、运行示例:精确求和 vs 朴素求和

文档给出的示例恰好点明了 Math.sumPrecise 的存在价值——普通加法在"大数 + 小数"场景下的精度损失:

1e20 + 0.1 + -1e20; // => 0(1e20 的浮点表示精度不足以容纳 0.1,误差被吞掉)
Math.sumPrecise([1e20, 0.1, -1e20]); // => 0.1(补偿算法把 0.1 的精度保留下来)

这说明当聚合计算涉及数量级差异悬殊的浮点数(如财务累加、数值分析、统计求和)时,Math.sumPrecise 能以 O(n) 的代价获得接近精确的求和结果,而普通 + 运算符在灾难性抵消面前会静默丢精度。

其余方法的使用方式与原生一致,例如:

Math.hypot(3, 4);        // => 5
Math.imul(0xFFFFFFFF, 5); // => -5
Math.sign(-0);           // => -0
Math.f16round(1.1);      // => 1.099609375(半精度舍入结果)
Math.clz32(1);           // => 31

六、如何验证与测试

core-js 为每个 Math 模块都配备了独立的 QUnit 单元测试,位于 tests/unit-global/(全局版)与 tests/unit-pure/(纯净版)。以 sumPrecise 为例,tests/unit-global/es.math.sum-precise.js 不仅断言了基础求和结果,还逐条验证了 arity(参数个数为 1)、looksNative(尽量保持原生外观)、nonEnumerable(方法不可枚举)等元属性,并覆盖了 TypeError 抛出条件与 IEEE 754 特殊值行为——这些测试同时可以作为各方法语义的权威参考文档。

七、总结

core-js 对 ECMAScript Math 的覆盖可以概括为三层能力:

  1. 标准方法兜底acoshcbrthypotimullog2 等 ES2015+ 方法,仅在宿主缺失或实现有 bug(如 Chrome 77 的 hypot)时通过 forced 标志修补;
  2. 精度增强f16roundfround 提供 16/32 位浮点舍入,sumPrecise 提供精确求和,解决工程中真实的数值精度痛点;
  3. 灵活的加载粒度es/stable/actual/full 四档 + 聚合/单方法入口 + 全局/pure 双形态,让开发者可以精确控制引入范围与包体大小。

建议在生产环境中从 stable 入口按需引入 Math 扩展;若需要实验性质的 Math.sumPrecise 精确求和能力,则使用 fullesnext 命名空间(当前实现为 esnext.math.sum-precise 模块),并留意该提案在 TC39 的推进进度。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.21 K
2.81 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
945
1.86 K
docsdocs
暂无描述
Markdown
906
5.84 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
537
607
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
864
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
4.28 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.39 K
1.48 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
550
401
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.19 K
347