tinygrad 超越函数降解:SIN 阈值 30 与双归约路径

作者:

在

结论:transcendental.py 中的 SIN、EXP2、LOG2、SQRT 近似改写,只在该算子未被 renderer 声明为原生支持、或 force_transcendental=True 时才注册;TRANSCENDENTAL 默认值为 1,需 >= 2 才会在 dtype 降解之后并入后期降解,所以「源码里有近似函数」并不等于车端总走软件路径。xsin() 默认在 abs(d) >= 30 就从 Cody–Waite 切到 Payne–Hanek,但函数注释自述其适用范围从 39800 起,中间区间并无全域精度证明。以下为对固定源码归档的静态分析,未运行目标后端、未加载模型实体、未做实车验证。

编译入口与注册条件

get_transcendental_patterns() 只在 SIN、EXP2、LOG2、SQRT 未列入 renderer 支持算子,或 force_transcendental=True 时注册相应改写。float16/32/64 直接走本文件的公式,其他浮点 dtype 先转 float32 执行、再转回原 dtype。因此「后端是否声明原生超越指令」是决定走原生还是走降级的第一道开关。

full_rewrite_to_sink() 在 dtype 降级之后把该模式集并入后期降级,并传入 TRANSCENDENTAL >= 2。TRANSCENDENTAL 默认值为 1,仅凭源码存在近似函数不能推断车端总走软件路径,需要结合该环境变量与实际后端来判定。

另需区分入口:普通 POW 由 symbolic.py 的规则降为 xpow(),不受「后端是否支持 SIN/LOG2/EXP2」这同一入口控制。也就是说,即使后端声明了这些超越算子,幂运算仍可能落到本文件的 xpow() 上。

位操作与结构辅助

整个文件依赖目标后端在 BITCAST、移位和浮点格式上与公式假设完全一致。下表按函数族列出实现与限制,其中多处只对有限输入成立,不应泛化为标准库语义。

函数族 实现与限制
_lazy_map_numbers() 用条件选择分别处理 +Inf、-Inf、NaN 与普通数;NaN 判断用 x != x;最终特殊值由各顶层函数按自身语义决定
mantissa_bits() / exponent_bias() / exponent_mask() 从 dtype 浮点格式取尾数与指数结构;依赖后端 BITCAST 与浮点格式精确一致
pow2if() / ilogb2k() 通过整数位型构造 2^q、从位型提取指数
rintk() 先按符号加减 0.5 再转整数,实现「中点远离零」取整;转换溢出与非有限输入不由该函数统一处理
ldexp3k() / ldexp2k() 前者直接调浮点指数位,后者把整数指数拆两段构造二次幂再相乘;后者注释要求正、非次正规数
frexp() 拆位得尾数与指数,假定输入非零;float64 掩码还会清符号;此处主要用于 SIN 对绝对值的范围归约

这些函数的共同前提是「目标后端的整数宽度、移位与浮点转整数语义符合假设」。若设备缺 uint64 或其乘移语义不同,下面的 Payne–Hanek 路径需要重点复核。

SIN 的两条归约路径

SIN 先把幅值归约到小角,再用按 dtype 选择的多项式逼近,最后按象限恢复移相与符号。cody_waite_reduction() 用 1/pi 估算象限并做分段常数减法;float64 分拆多段 pi,float16 的减法临时升到 float32。payne_hanek_reduction() 则从 190 位 2/pi 常量提取片段,以 uint64 乘法求大角余量和象限。

xsin() 默认同时构造 Cody–Waite 与 Payne–Hanek 两条路径,以绝对值 30 为选择阈值,并恢复原符号;fast=True 时只使用 Cody–Waite,调用者需自行约束幅值。Inf/NaN 最后统一映射为 NaN。

路径 / 参数 机制 阈值与边界
Cody–Waite 1/pi 估象限 + 分段常数减法 小角与中等角;float64 多段 pi,float16 临时升 float32
Payne–Hanek 190 位 2/pi 片段 + uint64 乘法 注释声称适用范围从 39800 起;对整数宽度、移位/溢出与浮点转整数语义敏感
xsin() 选择 默认在 abs(d) >= 30 选择 Payne–Hanek 有限样本在 31、100、1000、39800 及 1e6 未显示明显误差,但不构成整个 30–39800 区间或全部浮点值的精度证明
fast=True 只用 Cody–Waite 调用者应自行约束幅值,否则大角误差无保证

需要注意的一处不一致:Payne–Hanek 函数注释写的是 39800 <= d,而 xsin() 默认在 abs(d) >= 30 就选择其结果。文中不把函数注释里的「1 ULP」当作已验证保证。

EXP2、LOG2、POW 与插件验收

xexp2() 用 rintk 把输入分解为整数指数与小数部分,小数用多项式逼近、整数用 ldexp2k 恢复,随后按 dtype 上下界置 +Inf/0 并单独处理 NaN。xlog2() 对小幅值输入先乘二次幂以避开次正规区,再抽指数、正规化尾数并用多项式计算,末尾依次处理 +Inf、零、负数和 NaN,负零另用倒数检测。xpow() 从 exp2(exponent * log2(abs(base))) 构造幂;负底数时用转 int32 后回转识别非整数及奇偶,指数为零时强制返回 1。

函数 关键分界 需另行验证的边界
xexp2() 按 dtype 上下界置 +Inf/0,NaN 单独处理 上溢/下溢边界的实际取值
xlog2() 小幅值先乘二次幂避开次正规区;负零用倒数检测 负值、零、次正规数
xpow() 负底数转 int32 判奇偶;指数为 0 强制返回 1 超出整数转换可靠范围的指数、非有限值组合
验收样本 正常数、次正规数、正负零、Inf/NaN SIN 切换边界与大幅值、EXP2 上下溢、LOG2 负值、POW 负底数/非整数指数

面向自制计算后端,应明确选择「原生超越指令」还是「编译器降级」,并把该选择与模型工件、dtype、编译器版本及浮点模式绑定。验收要记录真实后端与参考实现的绝对/相对/ULP 误差和最坏时延。模型输出的变化必须在离线回放与受控台架中评估,计算插件不得获得 CAN 发送权。

验证方面,在 PYTHONPATH=. DEV=PYTHON 下上游 test_transcendental_helpers.py 五项通过;TRANSCENDENTAL=2 小样例中,float32 的 SIN/EXP2/LOG2 对给定有限样本与 Python math 的最大绝对差分别约 2.8e-8、2.5e-8、0,POW 的负底数整数/非整数、0^0 及 LOG2 特殊值样例符合预期。更广的图重写边界测试因缺 hypothesis 未导入;大角样本只做有限抽查,未运行全域误差证明、目标 QCOM 后端、LFS 模型或实车验证,归档源码未编辑。

常见问题

后端不支持 SIN,就一定会走软件近似吗?

不一定等同于此结论的细节是:只有在 renderer 未声明支持这些算子、或 force_transcendental=True 时才注册改写,且还需 TRANSCENDENTAL >= 2 才会并入后期降级;TRANSCENDENTAL 默认值为 1。

这里的 SQRT 是独立的牛顿迭代平方根吗?

不是。在无原生路径时,SQRT 被改写成 xpow(x, 0.5),因此它复用幂函数的构造,而不是另一套牛顿迭代实现。

xsin 在 30 到 39800 之间可靠吗?

不能这样断言。Payne–Hanek 的函数注释自述适用范围从 39800 起,而 xsin() 默认在 abs(d) >= 30 就选择它;有限样本在若干点上未显示明显误差,但这不构成该区间的全域精度证明,注释中的「1 ULP」亦未经验证。

自制计算后端应重点验收哪些点?

至少覆盖 float16/32/64 的正常数、次正规数、正负零、Inf/NaN,SIN 切换边界与大幅值,EXP2 上下溢,LOG2 负值,POW 负底数与非整数指数;若设备缺 uint64 或乘移语义不同,还需重点验证 Payne–Hanek 路径与 dtype 模拟。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/TINYGRAD_TRANSCENDENTAL_REDUCTION_NUMERICS_PLUGIN_CONTRACT_DEEP_DIVE.md(逐函数实现、SIN 两条归约路径与 TRANSCENDENTAL 入口条件)
  • 项目 02 —— openpilot-research/TINYGRAD_OP_DECOMPOSITION_DIVMOD_THREEFRY_PLUGIN_CONTRACT_DEEP_DIVE.md(基础算子降级专题,作为本文的补充阅读)

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注