news 2026/9/12 12:23:33

ESLint no-loss-of-precision 规则全解析:拦截 JavaScript 数字字面量的运行时精度丢失

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESLint no-loss-of-precision 规则全解析:拦截 JavaScript 数字字面量的运行时精度丢失

ESLint no-loss-of-precision 规则全解析:拦截 JavaScript 数字字面量的运行时精度丢失

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

本篇指南以 ESLint 内置规则no-loss-of-precision(官方规则文档)为核心,讲解它如何识别并阻止那些在编译期"看着正确、运行期却悄悄变值"的数字字面量。读完本文,你将掌握 IEEE 754 双精度浮点数的精度边界、该规则覆盖的十进制/二进制/八进制/十六进制字面量判定逻辑,以及它在eslint:recommended推荐配置下的实际接入方式。

规则背景:数字字面量为何会"静默失真"

no-loss-of-precision是一条problem类型的规则,用于禁止使用会在运行时因 64 位浮点舍入而丢失精度的数字字面量(见 docs/src/rules/no-loss-of-precision.md 的引言)。它不检查任何运行时计算,只针对源码中直接写死的数字(number literal)做静态审计。

JavaScript 中所有Number都按 IEEE 754 标准的双精度(double-precision)浮点数存储,其尾数部分仅能精确表示约 15~16 位十进制有效数字。一旦程序员在字面量中写下超出该精度的额外数字,这些数字在转换为Number类型时就会被舍入丢弃,导致程序行为与源码意图不一致——而这种错误通常不会抛出任何异常,属于最隐蔽的 bug 类型之一。

在 ESLint 的规则元数据中(见 docs/src/_data/rules_meta.json),该规则被标记为:

  • type: "problem":属于"确定有问题"的代码错误,而非风格建议;
  • recommended: true:已被列入eslint:recommended推荐集,任何启用推荐配置的项目默认开启;
  • dialects: ["JavaScript", "TypeScript"]:同时适用于 JavaScript 与 TypeScript 语法。

规则详情:精度丢失的判定原理

十进制字面量的判定流程

从源码实现看(lib/rules/no-loss-of-precision.js),规则的入口在create(context)中监听Literal节点,并通过isNumber()先排除字符串、布尔值等非数值字面量:

create(context) { return { Literal(node) { if (isNumber(node) && losesPrecision(node)) { context.report({ messageId: "noLossOfPrecision", node, }); } }, }; }

核心判断函数losesPrecision按进制分流:十进制走baseTenLosesPrecision,其余走notBaseTenLosesPrecision

十进制路径的判定思路非常精妙(lib/rules/no-loss-of-precision.js):

  1. 规范化源文字面量:先调用getRaw()剔除数字分隔符_(如9_007_199_254_740_9939007199254740993),再用convertNumberToScientificNotation()把字面量转换为"科学计数法对象"ScientificNotation,其中coefficient保存去除前导零与尾随零后的有效数字字符串,magnitude保存数量级(十进制指数)。
  2. 特殊情况处理:若数值为 0,则直接校验规范化后的系数是否全为 0;若字面量的有效数字超过 100 位requestedPrecision > 100),无需转换即可判定为精度丢失。
  3. 回读比对:用node.value.toPrecision(requestedPrecision)把运行时已存储的数值按用户请求的精度重新格式化为字符串,再同样规范化;如果"用户想要的数"与"实际存储的数"在数量级或有效数字系数上不一致,即判定该字面量会丢失精度。

换句话说,规则不是凭位数一刀切(12300000000000000000000000有 26 位却合法),而是精确对比"源码想表达的数值"与"运行时真实能表示的数值",只有当两者真正不同才报告。

非十进制字面量的判定流程

对于二进制(0b/0B)、八进制(0o/0O以及旧式0前缀)、十六进制(0x/0X)字面量,notBaseTenLosesPrecision采用更直接的方法(lib/rules/no-loss-of-precision.js):将去除分隔符后的原始字符串按对应进制toString(base)node.value的实际值做回读比较,若源码写出的进制表示与运行时真实值的进制表示不一致,说明发生了精度丢失。

isBaseTen()(lib/rules/no-loss-of-precision.js)负责识别进制:检查原始文本是否以0x/0X/0b/0B/0o/0O前缀开头,或是否符合旧式八进制形态^0[0-7]+$

不正确的代码示例

以下代码会被no-loss-of-precision"error"级别报告(规则消息为noLossOfPrecision,文案是"This number literal will lose precision at runtime.",见 lib/rules/no-loss-of-precision.js):

/*eslint no-loss-of-precision: "error"*/ const a = 9007199254740993 const b = 5123000000000000000000000000001 const c = 1230000000000000000000000.0 const d = .1230000000000000000000000 const e = 0X20000000000001 const f = 0X2_000000000_0001;

逐个解读这些用例(均有对应测试佐证,见 tests/lib/rules/no-loss-of-precision.js):

  • a = 9007199254740993:这是经典的Number.MAX_SAFE_INTEGER9007199254740991)+ 2,超过安全整数范围后相邻整数已无法区分;
  • b = 5123000000000000000000000000001:31 位有效数字远超双精度容量,且末尾的1会被舍入吞掉;
  • c = 1230000000000000000000000.0d = .1230000000000000000000000:整数与小数部分均因有效位数过长而失真;
  • e = 0X20000000000001f = 0X2_000000000_0001:十六进制字面量同样会丢失精度,且支持带数字分隔符的写法(_需要 ES2021 及以上的语法环境)。

测试中还覆盖了更多形态的非法用例(tests/lib/rules/no-loss-of-precision.js),例如带分隔符的90_0719925_4740.9_93e3、指数形式9.007199254740993e159007199254740.993e32e999、下溢到 0 的1e-3501e-324,以及二进制0b100000000000000000000000000000000000000000000000000001、八进制0o400000000000000001等非十进制用例。

正确的代码示例

以下写法均不会触发该规则:

/*eslint no-loss-of-precision: "error"*/ const a = 12345 const b = 123.456 const c = 123e34 const d = 12300000000000000000000000 const e = 0x1FFFFFFFFFFFFF const f = 9007199254740991 const g = 9007_1992547409_91

要点说明:

  • a = 12345b = 123.456:有效数字有限,双精度可精确表示;
  • c = 123e34:虽然数值极大,但科学计数法只包含 3 位有效数字,仍可精确存储;
  • d = 12300000000000000000000000:26 位全是"后缀零",尾数只需保存123与指数,不损失任何有效信息(这与前文b = 5123...0001结尾非零形成鲜明对比);
  • e = 0x1FFFFFFFFFFFFF:十六进制 53 位全 1,恰好是双精度尾数能容纳的极限(对应十进制9007199254740991);
  • f = 9007199254740991Number.MAX_SAFE_INTEGER本身;
  • g = 9007_1992547409_91:数字分隔符只是书写形式,不影响规则判定(getRaw()会先剔除_)。

测试用例还确认了以下边界情况均为合法(tests/lib/rules/no-loss-of-precision.js):

  • 各种指数形态:123.0e34123e-34-12.3e-349.00e29.0000000000e10(回归自 eslint 议题 #19957 的修复);
  • 极小值5e-324(双精度最小正非零次正规数)、0.00000000000000000000000123
  • 零的各种写法00.00.-00e5,以及带大量尾随零的123.0000000000000000000000
  • 旧式八进制形态019.501950019500080377777777777777777
  • 非数值字面量true'abc'nullundefined、对象与数组字面量、'9007199254740993'(字符串不会参与数值精度判定);
  • 带分隔符的合法写法12_34_560b111_111_...0x1FFF_FFFF_FFF_FFF等。

配置方式:Options 与推荐集接入

该规则没有配置项

no-loss-of-precisionmeta.schema为空数组(见 lib/rules/no-loss-of-precision.js),即不接收任何选项,无法通过参数放宽或收紧判定阈值。规则行为由实现内部固定:有效数字超过 100 位一律报错,其余情况严格比对用户意图与实际存储值。

单独启用该规则,可在扁平配置(flat config)中直接写入:

// eslint.config.js export default [ { rules: { "no-loss-of-precision": "error", }, }, ];

或使用传统的eslintrc风格:

{ "rules": { "no-loss-of-precision": "error" } }

已纳入 eslint:recommended

由于recommended: true(见 docs/src/_data/rules_meta.json),只要你的配置扩展了eslint:recommended,该规则就默认以"error"级别生效,无需显式声明。这意味着在绝大多数标准 ESLint 项目中,这类精度丢失字面量会直接在npx eslint或编辑器集成中亮起错误提示。

源码视角:规则如何做到"既精准又不误报"

深入实现可以发现几个值得称道的设计(lib/rules/no-loss-of-precision.js):

  1. 科学计数法抽象ScientificNotation类(lib/rules/no-loss-of-precision.js)用"系数 + 数量级"二元组统一表达整数与浮点,normalizeInteger/normalizeFloat(lib/rules/no-loss-of-precision.js)负责剥离前导零、尾随零并计算数量级,从而把12300000000000000000000000123e25归一到同一表示。
  2. 不依赖经验阈值:除 100 位有效数字的兜底外,判定完全基于toPrecision()回读比对,因此不会误伤"位数多但恰好可精确表示"的字面量。
  3. 进制全覆盖isBaseTennotBaseTenLosesPrecision配合parseInttoString(base)覆盖了二进制、八进制(新旧两式)、十六进制全部字面量形态。
  4. 与解析器解耦losesPrecision(node)只消费 ASTLiteral节点的valueraw字段,因此天然兼容 TypeScript(测试中通过@typescript-eslint/parser单独验证了 TS 场景,见 tests/lib/rules/no-loss-of-precision.js)。

规则的注册入口位于 lib/rules/index.js,采用懒加载方式() => require("./no-loss-of-precision")引入,避免在仅启用少量规则时拖慢启动。文档站点的规则元数据与版本信息(7.1.0起收录)可在 docs/src/_data/rule_versions.json 与 docs/src/_data/rules_meta.json 中查阅。

实践建议

  • 大整数改用BigInt:当业务确实需要超过Number.MAX_SAFE_INTEGER9007199254740991)的整数时,应使用9007199254740993n这类BigInt字面量,而不是依赖会失真的普通数字字面量。
  • 关注有效数字而非位数:判定标准是"有效数字",末尾补零通常安全,末尾带非零有效位则危险——1.0000000000000000000000123这类"夹心零"同样会触发报告。
  • 保持默认推荐配置:既然该规则无需选项且已进eslint:recommended,直接沿用推荐集即可获得静态防护,无需额外维护。
  • 警惕科学计数法形态9.007199254740993e152e999(溢出为Infinity边界)、1e-3241e-350(下溢为 0)等指数写法也会被捕获,说明规则不仅处理"看着就很长"的数字。

【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 12:21:25

如何将 Graphiti MCP 服务器以 stdio 方式接入 Claude Desktop?

如何将 Graphiti MCP 服务器以 stdio 方式接入 Claude Desktop? 【免费下载链接】graphiti Build Real-Time Knowledge Graphs for AI Agents 项目地址: https://gitcode.com/GitHub_Trending/grap/graphiti Claude Desktop 只支持 stdio 传输,而…

作者头像 李华
网站建设 2026/9/12 12:15:03

如何用 Wand-Enhancer 免费解锁 Wand 游戏修改器的高级功能

如何用 Wand-Enhancer 免费解锁 Wand 游戏修改器的高级功能 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer Wand-Enhancer 是开源的本地补丁工具&a…

作者头像 李华
网站建设 2026/9/12 12:14:55

结构化提示技术在代码语义推理中的应用与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华