mpfr_expr 教程

本教程介绍如何用 GNU MPFR 计算出的参考结果检验 bin_float。支持三种面向行的格式:MPFR 自带的平方根测试数据、整数幂,以及初等函数矩阵。你将把文件解析为文档,执行它,并读取摘要。命令行运行器是 mpfr_expr_cli。

快速入门

将该包添加到 moon.pkg:

import {
  "Luna-Flow/floating/frontend/mpfr_expr",
}

检验一个平方根:在 53 位精度、就近舍入下,2−1074⋅252=2−511\sqrt{2^{-1074} \cdot 2^{52}} = 2^{-511}:

///|
test "quick start" {
  let rows =
    #|# input_precision output_precision rounding input expected
    #|53 53 n 0x10000000000000p-1074 0x10000000000000p-563
    #|
  let document = @mpfr_expr.parse_sqrt_data("sqrt.txt", rows).unwrap()
  let summary = @mpfr_expr.execute_sqrt_data(document)
  inspect(summary.passed_cases(), content="1")
  inspect(summary.success(), content="true")
}

数值以十六进制有效数和二进制指数书写:0x10000000000000p-1074 即 252⋅2−10742^{52} \cdot 2^{-1074}。

日常任务

带标志的初等函数

初等函数行依次给出函数名、输出精度与舍入模式、操作数、预期结果,以及三个异常标志(不精确、无效、除以零)。未使用的操作数写作 -(第二操作数)和 0(整数操作数):

///|
test "elementary rows" {
  let rows =
    #|# mpfr-elementary-v1
    #|# op prec rnd x y n expected inexact invalid divbyzero
    #|exp 24 n 0x0p0 - 0 0x1p0 0 0 0
    #|ln 53 n 0x0p0 - 0 -inf 0 0 1
    #|sqrt 53 z -0x1p0 - 0 nan 0 1 0
    #|rootn 53 n 0x1bp0 - 3 0x3p0 0 0 0
    #|hypot 53 d 0x3p0 0x4p0 0 0x5p0 0 0 0
    #|
  let document = @mpfr_expr.parse_elementary_data("elem.txt", rows).unwrap()
  let summary = @mpfr_expr.execute_elementary_data(document)
  inspect(summary.total_cases(), content="5")
  inspect(summary.passed_cases(), content="5")
}

ln(0) = -inf 引发除以零,sqrt(-1) 是返回 NaN 的无效运算;两个标志都是检验的一部分。

整数次幂

pow 行把每个数拆分为系数、二进制指数和符号:

///|
test "integer power rows" {
  // (3 * 2^-1)^3 = 27/8 at 11 bits, exact
  let rows =
    #|11 n 3 -1 0 3 6c0 -9 0 0
    #|
  let document = @mpfr_expr.parse_pow_data("pow.txt", rows).unwrap()
  let summary = @mpfr_expr.execute_pow_data(document)
  inspect(summary.passed_cases(), content="1")
}

各字段依次为:精度、舍入模式、输入系数(十六进制数字)、输入指数、输入符号(1 表示负)、整数指数、预期系数、预期指数、预期符号、不精确标志。此处 0x6c0p-9 即 1728/512=27/81728/512 = 27/8。

解读失败

///|
test "a failing row" {
  let rows =
    #|# mpfr-elementary-v1
    #|exp 24 n 0x0p0 - 0 0x2p0 0 0 0
    #|
  let summary = @mpfr_expr.execute_elementary_data(
    @mpfr_expr.parse_elementary_data("bad.txt", rows).unwrap(),
  )
  let result = summary.results()[0]
  inspect(result.id(), content="exp:2")
  inspect(
    result.message(),
    content="elementary mismatch: expected 0x1p1 flags=false/false/false, actual 0x1p0 flags=false/false/false",
  )
}

id 由运算名(或 sqrt/pow)和行号组成。

解析错误

每一行格式错误的行都会连同行号一起报告,并且不返回文档:

///|
test "parse errors" {
  match @mpfr_expr.parse_sqrt_data("bad.sqrt", "53 53 x 0x1p0 0x1p0\n53 53\n") {
    Err(diagnostics) =>
      inspect(
        diagnostics.map(d => d.line().to_string() + ": " + d.message()).join("; "),
        content="1: invalid or unsupported MPFR data_check directive; 2: MPFR data_check row must contain five fields",
      )
    Ok(_) => fail("expected diagnostics")
  }
}

深入了解

  • 语料。 just conformance run binary 将固定版本的 MPFR tests/data/sqrt 文件、仓库中提交的初等函数矩阵 testdata/bin_float/mpfr-4.2.2-elementary.txt 与 TestFloat 一起执行;参见验证。初等函数数据和幂运算数据的生成器分别是 tools/generate_mpfr_elementary_oracle.c 和 tools/generate_mpfr_pow_oracle.c。
  • 用哪个解析器? 当文件包含 mpfr-elementary-v1 时,CLI 选择初等函数格式;包含 input_coefficient_hex 时选择幂运算格式;否则选择平方根格式。请把标记放在注释行中。

常见陷阱

  • 初等函数行比较的是数值而非编码。 在这里 +0 与 -0 相等,任何 NaN 都与预期的 nan 匹配。平方根行和幂运算行则比较整个 BinFloat,包括零的符号。
  • 不检查平方根的标志。 sqrt 数据行只比较值。
  • 没有指数范围。 各行在无界上下文中执行:不存在上溢、下溢或次正规数范围;报告上溢或下溢的初等函数行或幂运算行会失败。
  • 二元运算需要第二操作数。 第二操作数为 - 的初等函数 pow、hypot 或 atan2 行能被解析器接受,但执行时会中止。

后续步骤