core-js Float16 方法完全指南:DataView.getFloat16 / setFloat16 与 Math.f16round 的源码级剖析 core-js Float16 方法完全指南DataView.getFloat16 / setFloat16 与 Math.f16round 的源码级剖析【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-jscore-js 在proposals/float16入口中提供了 TC39 float16array 提案的核心内置方法DataView.prototype.getFloat16、DataView.prototype.setFloat16与Math.f16round。本文以 docs/web/docs/features/proposals/float16-methods.md 为骨架逐行剖析 core-js 中这三个方法的实际实现并结合单元测试与兼容性数据帮助你理解半精度浮点数的编解码原理、边界行为以及如何在老环境里安全地启用并验证这些 API。一、Float16 提案与半精度浮点数背景Float16半精度浮点数是一种 16 位二进制浮点格式遵循 IEEE 754 的 binary16 布局1 位符号位sign5 位指数位exponent偏置 1510 位尾数位significand/fraction相比 JS 默认的 64 位 doublebinary64与 32 位 floatbinary32Float16 在内存占用上只有 float32 的一半常用于 AI 推理、WebGPU 纹理、图像处理与游戏引擎的顶点数据等对精度要求不高但对带宽敏感的领域。TC39 的 float16array 提案packages/core-js/proposals/float16.js源码注释指向该提案不仅提出了Float16Array等类型化数组还定义了与之配套的DataView读写方法与Math舍入函数——这正是 core-js 在本入口中实现的部分。需要先说明一个容易踩坑的细节原始文档的签名表中写的是Math.fround但该提案实际要求的方法是Math.f16roundMath.fround是 ES2015 中早已存在的 float32 舍入方法。core-js 源码实现的是Math.f16round下文全部以实际实现为准。二、内置方法签名与入口点2.1 内置方法签名以下是 core-js 实际提供的三个方法与文档签名一致其中littleEndian默认值为false即默认按大端序读写class DataView { getFloat16(offset: any, littleEndian?: boolean false): number setFloat16(offset: any, value: any, littleEndian?: boolean false): void; } namespace Math { f16round(number: any): number; }关于littleEndian默认值的细节可以从实现中验证在 es.data-view.get-float16.js 中getUint16(this, byteOffset, arguments.length 1 ? arguments[1] : false)明确以false作为缺省参数与签名声明完全一致。2.2 入口点文档给出的入口为core-js/proposals/float16实际入口文件 packages/core-js/proposals/float16.js 会依次引入三个底层模块require(../modules/esnext.data-view.get-float16); require(../modules/esnext.data-view.set-float16); require(../modules/esnext.math.f16round);而这三个esnext模块目前只是es稳定模块的转发源码注释标注TODO: Remove from core-js4例如 esnext.math.f16round.js 实际就是require(../modules/es.math.f16round)。因此你也可以按需精确引入对应的稳定入口import core-js/proposals/float16; // 一次性引入全部三个方法 // 或者按需引入稳定模块 import core-js/stable/data-view/get-float16; import core-js/stable/data-view/set-float16; import core-js/stable/math/f16round;三、DataView.prototype.getFloat16读回半精度字节getFloat16(byteOffset, littleEndian)的作用是从DataView的指定字节偏移处读取一个 16 位无符号整数再按 Float16 布局解包为 JSNumber。3.1 解包实现unpackFloat16es.data-view.get-float16.js 的核心是unpackFloat16函数var EXP_MASK16 31; // 2 ** 5 - 1指数位全 1 掩码 var SIGNIFICAND_MASK16 1023; // 2 ** 10 - 1尾数位全 1 掩码 var MIN_SUBNORMAL16 pow(2, -24); // 最小正次规格化数 2 ** -10 * 2 ** -14 var SIGNIFICAND_DENOM16 0.0009765625; // 2 ** -10 var unpackFloat16 function (bytes) { var sign bytes 15; var exponent bytes 10 EXP_MASK16; var significand bytes SIGNIFICAND_MASK16; if (exponent EXP_MASK16) return significand 0 ? sign 0 ? Infinity : -Infinity : NaN; if (exponent 0) return significand * (sign 0 ? MIN_SUBNORMAL16 : -MIN_SUBNORMAL16); return pow(2, exponent - 15) * (sign 0 ? 1 significand * SIGNIFICAND_DENOM16 : -1 - significand * SIGNIFICAND_DENOM16); };这个函数完整覆盖了 Float16 的四种数值形态情形判定条件返回值无穷指数位全 1 且尾数为 0±InfinityNaN指数位全 1 且尾数非 0NaN次规格化数指数位全 0significand * 2^-24带符号规格化数其余情况2^(exponent-15) * (1 significand * 2^-10)其中bytes 15取出符号位bytes 10 31取出 5 位指数bytes 1023取出 10 位尾数。DataView.prototype.getUint16通过uncurryThis解包后直接复用保证字节序大端/小端语义与原生getUint16完全一致。四、DataView.prototype.setFloat16把 Number 打包为半精度字节setFloat16(byteOffset, value, littleEndian)先将 JSNumber转换为 Float16 位模式再写入DataView。4.1 打包实现packFloat16es.data-view.set-float16.js 实现了packFloat16并处理了舍入进位等细节var MIN_INFINITY16 65520; // (2 - 2 ** -11) * 2 ** 15超出即视为无穷 var MIN_NORMAL16 0.000061005353927612305; // (1 - 2 ** -11) * 2 ** -14最小规格化数附近边界 var REC_MIN_SUBNORMAL16 16777216; // 2 ** 10 * 2 ** 14 var REC_SIGNIFICAND_DENOM16 1024; // 2 ** 10 var packFloat16 function (value) { if (value ! value) return 0x7E00; // NaN → 默认 NaN 位模式 if (value 0) return (1 / value -Infinity) 15; // 保留 ±0 符号 var neg value 0; if (neg) value -value; if (value MIN_INFINITY16) return neg 15 | 0x7C00; // 溢出 → ±Infinity if (value MIN_NORMAL16) return neg 15 | roundTiesToEven(value * REC_MIN_SUBNORMAL16); // 次规格化 // 规格化路径计算指数与尾数处理 2^-15 边界与尾数进位 var exponent floor(log2(value)); if (exponent -15) return neg 15 | REC_SIGNIFICAND_DENOM16; var significand roundTiesToEven((value * pow(2, -exponent) - 1) * REC_SIGNIFICAND_DENOM16); if (significand REC_SIGNIFICAND_DENOM16) return neg 15 | exponent 16 10; return neg 15 | exponent 15 10 | significand; };几个值得注意的工程细节NaN 规范化所有 NaN 统一编码为0x7E00与规范要求一致±0 符号保留通过1 / value -Infinity区分-0舍入策略使用roundTiesToEven银行家舍入处理次规格化数与规格化数的尾数舍入进位处理当尾数舍入后达到1024即需要进位到下一指数级时指数1并清空尾数避免双重进位错误参数校验写入前使用aDataView(this)校验接收者必须是DataView实例使用toIndex(byteOffset)把偏移量规范化为数组索引。4.2 边界数值速查表结合 es.math.f16round.js 中定义的常量Float16 的关键边界值如下常量数值说明FLOAT16_MAX_VALUE65504最大有限值(2 - 2^-11) * 2^15FLOAT16_MIN_VALUE6.103515625e-05最小正规格化数2^-14FLOAT16_EPSILON0.0009765625机器精度2^-10—5.960464477539063e-08最小正次规格化数2^-24—0x7C00 / 0xFC00±Infinity位模式—0x7E00默认 NaN 位模式五、Math.f16round一步到位的半精度舍入Math.f16round(x)把输入值舍入到最接近的 Float16 可表示值等价于转成半精度再读回。5.1 实现与内部工具函数es.math.f16round.js 本身极薄逻辑复用自通用浮点舍入工具 internals/math-float-round.jsmodule.exports function (x, FLOAT_EPSILON, FLOAT_MAX_VALUE, FLOAT_MIN_VALUE) { var n x; var absolute abs(n); var s sign(n); if (absolute FLOAT_MIN_VALUE) return s * roundTiesToEven(absolute / FLOAT_MIN_VALUE / FLOAT_EPSILON) * FLOAT_MIN_VALUE * FLOAT_EPSILON; var a (1 FLOAT_EPSILON / EPSILON) * absolute; var result a - (a - absolute); if (result FLOAT_MAX_VALUE || result ! result) return s * Infinity; return s * result; };算法要点次规格化路径当绝对值小于最小规格化数2^-14时先按2^-24的粒度做 ties-to-even 舍入落在次规格化区间规格化路径通过a - (a - absolute)的技巧在 double 运算中模拟单精度级舍入EPSILON即Number.EPSILON溢出处理舍入结果超过65504或产生 NaN 时返回±Infinity零与负零sign与符号处理保留了-0的语义。正因为同一套floatRound工具函数被 float32/float16 等不同精度复用Math.f16round与规范要求的语义可以保持高度一致这也是 core-js 把舍入逻辑下沉到 internals 的原因。六、实战示例读写 Float16 数据以下示例可以直接在支持 ES Modules 的环境中使用先引入入口再完成编码、写入、读回与舍入验证import core-js/proposals/float16; const buffer new ArrayBuffer(4); const view new DataView(buffer); // 大端序写入littleEndian 缺省为 false view.setFloat16(0, 1.337); console.log(view.getUint16(0).toString(16)); // 0x3d591.337 的 Float16 位模式 console.log(view.getFloat16(0)); // 1.3369140625半精度舍入结果 // 小端序写入 view.setFloat16(2, -0.5, true); console.log(view.getFloat16(2, true)); // -0.5 // Math.f16round 与 set/get 结果一致 console.log(Math.f16round(1.337)); // 1.3369140625 console.log(Math.f16round(-0)); // -0 console.log(Math.f16round(65504)); // 65504最大有限值 console.log(Math.f16round(65505)); // Infinity溢出 console.log(Math.f16round(5.96e-8 / 2)); // 0小于最小次规格化数一半单元测试 tests/unit-global/es.math.f16round.js 对上述边界行为做了系统断言f16round()与f16round(undefined)返回NaN、f16round(null)返回0、f16round(-0)返回-0、f16round(Number.MAX_VALUE)返回Infinity、f16round(1.337)精确等于1.3369140625、f16round(7.9999999)进位为8等。这些断言同样适用于setFloat16/getFloat16组合的读写一致性验证。七、兼容性判断与 Polyfill 时机在决定是否引入 polyfill 前应判断运行环境是否已原生支持这些方法。core-js-compat 的浏览器兼容数据维护在 packages/core-js-compat/src/data.mjs 中es.data-view.get-float16/es.data-view.set-float16Chrome 135、Firefox 129、Safari 18.2、Deno 1.43、Bun 1.1.23 起原生支持es.math.f16roundChrome 135、Firefox 129、Safari 18.2、Deno 1.43、Bun 1.1.23、Rhino 1.9.0 起原生支持详见该文件第 540、547、974 行附近。也就是说如果目标环境低于上述版本就需要通过core-js/proposals/float16入口补齐这三个方法。core-js-compat 包index.js支持按目标浏览器列表browserslist自动计算需要引入的模块清单可与core-js-builder配合生成最小化的按需构建产物。八、相关文件导航想要深入钻研或复现本文结论可以重点阅读以下仓库文件入口与转发proposals/float16.js、esnext.data-view.get-float16.js、esnext.math.f16round.js核心实现es.data-view.get-float16.js、es.data-view.set-float16.js、es.math.f16round.js通用舍入工具math-float-round.js边界校验辅助to-index.js 与 a-data-view.js位于packages/core-js/internals/目录测试用例tests/unit-global/es.math.f16round.js兼容性数据packages/core-js-compat/src/data.mjs稳定入口清单packages/core-js/stable/data-view/与packages/core-js/stable/math/目录下的get-float16.js、set-float16.js、f16round.js九、小结DataView.prototype.getFloat16/setFloat16与Math.f16round是 TC39 float16array 提案中可直接独立使用的一组能力前者完成半精度字节与 JSNumber之间的双向转换默认大端序支持小端序参数后者提供等价的纯数值舍入。core-js 通过 proposals/float16.js 一个入口把它们全部引入实现上分别以unpackFloat16、packFloat16与共享的floatRound工具函数承载核心逻辑并通过roundTiesToEven保证舍入符合 IEEE 754 语义。配合 compat 数据 与 单元测试你可以准确地判断目标环境是否需要 polyfill并验证接入后的行为是否符合规范。【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考