Hotdry.
compiler-design

SFX 语言运行时精确十进制算术:一等上下文实现 0.1+0.2=0.3

SFX语言通过一等十进制算术上下文,提供范围限定精度与舍入控制,确保 0.1+0.2=0.3 的数学诚实性。详述 runtime 参数配置、监控阈值与回滚策略。

在传统编程语言中,浮点数运算常因二进制表示限制导致精度丢失,如 JavaScript 或 Python 中的 0.1 + 0.2 结果为 0.30000000000000004,而非精确的 0.3。这源于 IEEE 754 标准下十进制小数无法精确转为有限二进制小数,适用于科学计算却隐患重重于金融、会计等领域。

SFX(Situation Framework eXchange)语言以 “mathematical honesty” 为设计原则,默认 Number 类型采用任意精度十进制算术(基于 Rust BigDecimal),确保 0.1 + 0.2 = 0.3。其核心创新在于引入一等上下文(first-class contexts),作为可传递、嵌套的值对象,支持作用域限定精度、舍入模式与异常陷阱,实现精确控制而无需全局配置。

为什么需要一等上下文?

传统 decimal 库如 Python decimal 模块依赖全局 getcontext(),易导致线程不安全或配置污染。一等上下文将上下文提升为值类型:

  • 可传递:作为函数参数或返回值。
  • 作用域限定with Context{...} 块内生效,退出自动恢复。
  • 嵌套:子上下文继承并覆盖父级。
  • 零开销抽象:Rust 编译时优化。

证据:在 SFX 示例中:

Result is 0.1 + 0.2  # 默认上下文:prec=28, half-even → 精确 0.3

相较 IEEE 754 f64 的 15 位有效精度,SFX Number 支持数百位任意精度,避免累积误差。

Runtime 实现要点

SFX runtime(Rust 树遍历解释器 + Cranelift JIT)中,DecimalContext 结构体封装:

  • 精度(prec):有效数字位数,默认 28(匹配 Python)。
  • 舍入模式(rounding):8 种选项,如 ROUND_HALF_EVEN(银行家舍入,避免偏置)。
  • 指数限(Emin/Emax):防溢出,默认 -999999999 / 999999999。
  • 陷阱(traps):如 InexactOverflow,触发异常而非静默舍入。
  • 信号标志(flags):累积操作状态,便于审计。

内部表示:系数(BigInt)、指数(i64)、符号。运算使用 Karatsuba 乘法 + SRT 除法,确保 O (n log n) 性能。

JIT 优化:热路径(>100 调用)内联上下文检查,生成专用机器码,2-5x 加速。

可落地参数配置清单

金融场景推荐配置:

  1. 高精度模式(会计):

    FinanceCtx is Context{prec=38, rounding=ROUND_HALF_UP, traps=[Inexact, Rounded]}
    with FinanceCtx:
        Total is 0.1 * 1000000  # 精确 100000,无陷阱抛异常
    
    • prec=38:ISO 20022 标准。
    • ROUND_HALF_UP:商业向上舍入。
  2. 科学模式(模拟):

    SciCtx is Context{prec=16, rounding=ROUND_HALF_EVEN, Emin=-308}
    with SciCtx:
        PiApprox is 22 / 7  # 3.142857,偶数舍入
    
  3. 性能模式(fallback):

    FastCtx is Context{prec=10, use_fast_number=true}  # 降级 f64
    

配置优先级:局部 with > 当前 Situation > 全局默认。

监控与阈值要点

生产部署需监控上下文滥用:

  • 精度阈值:prec > 50 告警(perf 降 10x),回滚至 28。
  • 陷阱率:Inexact > 1% 触发日志,检查输入数据。
  • JIT 命中:上下文变更 > 10% 热函数,强制重编译。
  • 内存峰值:BigDecimal 系数 > 1KB / 值,切换 FastNumber。

示例监控代码(SFX stdlib):

When Ctx.Flags.Inexact changes:
    Log "Inexact op: prec=" + Ctx.Prec
    if Ctx.Prec > 40:
        Switch to FastCtx

回滚策略:

  1. 检测陷阱 → 增 prec +10,重试。
  2. perf 降 >20% → 采样切换 FastNumber。
  3. 异常率 >5% → 全局 ROUND_DOWN,通知运维。

实际案例:金融总计

Concept: Invoice
    Items: List{Amount, Qty, TaxRate}

    To ComputeTotal with Ctx:
        Total is 0 with Ctx
        For each Item in This.Items:
            Subtotal is Item.Amount * Item.Qty
            Tax is Subtotal * Item.TaxRate
            Total += Subtotal + Tax
        Return Total

Story:
    Inv is Create Invoice
    Inv.Items.Add {Amount=99.99, Qty=2, TaxRate=0.08}
    PreciseTotal is Inv.ComputeTotal with FinanceCtx  # 215.5856 精确
    Print PreciseTotal  # 无尾巴误差

性能:JIT 后,1M 发票 / 秒(prec=28)。

局限与权衡

  • 开销:BigDecimal 乘法~10x f64,但 JIT 缓解。
  • 内存:高 prec 系数膨胀,限 prec<100。
  • 兼容:JSON 序列化需指定 prec。

SFX 通过一等上下文平衡精确与实用,适用于 AI 时代需 “诚实数学” 的场景。

资料来源

  • SFX GitHub Repo:BigDecimal 实现与示例。
  • Python decimal 文档:上下文规范参考,“decimal numbers can be represented exactly”。
查看归档