☰
scriptc数字格式化深度解析:JS精确f64语义与最短往返算法的实现
2026/10/1 8:08:32 网站建设 项目流程

scriptc数字格式化深度解析:JS精确f64语义与最短往返算法的实现

【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptc

scriptc 是一个 TypeScript/JavaScript 原生编译器,它能把 TS 和 JS 编译成类型化 IR、可读 C、LLVM 文本 IR、汇编、目标文件与原生可执行文件。其中容易被忽略、却决定输出正确性的细节是:数字格式化。编译后的原生运行时没有 JavaScript 引擎,却必须让String(0.1 + 0.2)和 Node 一样精确输出0.30000000000000004——这正是 scriptc 数字格式化模块要解决的"JS 精确 f64 语义"问题。

为什么数字转字符串这么难?

在 JavaScript 里,Number.prototype.toString(10)的规范(ECMA-262 §6.1.6.1.20)要求输出满足一个精妙定义:

找到最短的数字串 s(位数 k、标度 n),使得 s 能往返解析回同一个 double;在长度相同的所有候选中取最接近原值者,平局时取偶数。

然后还要按"放置规则"决定排版:

  • 指数 n 满足-6 < n <= 21时用定点表示(如123.45、0.000001)
  • 其余情况用科学计数法(如1e+21)

V8 用 Grisu、SpiderMonkey 用 Ryū 这类算法实现"最短往返数字"。scriptc 的选择是Ryū(纯整数运算),vendored 在运行时的 vendor/ryu/d2s.c,不依赖snprintf/strtod试探,也没有 locale 干扰。

核心实现:一行都不靠浮点运算

核心代码在 scr_number.c,分两层:

第一层:数字生成scr_f64_digits

int scr_f64_digits(double x, char digits[18], int *n_out) { // 1. 直接拆 IEEE-754 位模式(尾数 + 指数) // 2. 小整数快速路径 d2d_small_int():尾数末尾的十进制 0 折进指数 // 3. 其余走完整算法 d2d(),纯 64 位整数运算 // 4. 用 div10 逐位取出 k 个十进制数字(k ≤ 17) }

关键点:double 被拆成位模式后交给 Ryū 查表算法,整个"最短往返"判定没有任何浮点乘除,只有整数除 10 和查表。这意味着结果在不同平台、不同编译器、不同 locale 下都字节级一致。

第二层:ECMA 排版scr_f64_to_str

拿到数字串后按规范放置,四个分支正好对应 JS 的四种形态:

条件输出形态示例
k <= n <= 21整数补零123456789012345680
0 < n <= 21ddd.ddd3.141592653589793
-6 < n <= 00.000ddd0.000001
其他d.ddde±e1.7976931348623157e308

边界值也各有讲究:NaN、Infinity、-0单独短路处理,保证-0输出0而Object.is仍能区分符号。

字节级验证:拿 Node 当裁判 🧪

"精确"不是口号,scriptc 用oracle 测试把字节级一致性钉死:

  • 用例生成器 gen-number-cases.mjs 用 Node 作为 oracle,覆盖0.1 + 0.2、Number.MIN_VALUE(5e-324)、MAX_SAFE_INTEGER + 2、2 的幂及其邻域、科学计数法切换边界(1e±7附近)等,输出为"双精度位模式 + 期望字符串"的用例文件 number-cases.txt
  • 测试 number.test.ts 编译 C 侧 oracle 程序逐条比对,另有100 万随机 double 的 fuzz 门禁(fuzz子命令)持续防回归

集成层还有一个端到端语料测试 100-number-format.ts:从0.1 + 0.2、1/3到5e-324、-0,逐行断言编译产物与 Node 输出一致。

对使用者的意义

对新手而言,理解这套机制只需要记住三件事:

  1. 无需 Node 的精确输出——原生可执行文件直接链接 C 运行时,数字格式化不再依赖 V8 或任何 JS 引擎;
  2. 跨平台一致性——纯整数算法保证了 macOS / Linux / Windows / WASI 上console.log(x)输出逐字节相同;
  3. 性能友好——小整数走快速路径,普通路径是常数级查表 + 逐位除法,没有格式化缓冲区和strtod反查。

这套"Ryū 数字生成 + ECMA 排版 + Node oracle 验证"的组合,是 scriptc 把 JS 语义完整搬到原生运行时的一块典型拼图:规范条文变成 C 代码,Node 的行为变成不可漂移的测试基线。

延伸阅读:运行时 C 源码 packages/runtime/src/scr_number.c、Ryū 实现 packages/runtime/vendor/ryu/、测试用例生成 packages/runtime/test/gen-number-cases.mjs。

【免费下载链接】scriptcTypeScript-to-Native Compiler项目地址: https://gitcode.com/GitHub_Trending/sc/scriptc

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询