itl_expr 教程

本教程介绍如何针对 ball_float 运行以 ITF1788 套件 ITL 格式编写的 IEEE 1788 区间测试用例。你将把 ITL 文本解析为用例,逐个执行,并汇总结果。处理整个文件的命令行运行器是 itl_expr_cli。

快速入门

将该包添加到 moon.pkg:

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

解析一个测试用例块并运行:

///|
test "quick start" {
  let source =
    #|testcase demo {
    #|  add [1.0,2.0] [3.0,4.0] = [4.0,6.0];
    #|  intersection [1.0,3.0] [2.0,4.0] = [2.0,3.0];
    #|}
  let cases = @itl_expr.parse_itl(source).unwrap()
  let summary = @itl_expr.summarize_results(
    cases.map(case => @itl_expr.execute_case(case)),
  )
  inspect(summary.passed_cases(), content="2")
  inspect(summary.success(), content="true")
}

每条语句 operation operand… = expected; 成为一个 ItlCase,其 id 为 testcase:index。

日常任务

检查解析后的用例

///|
test "parsed cases" {
  let source =
    #|testcase bounds {
    #|  inf [-0x1.8p1,2.5] = -3.0;
    #|  mul [1.0,2.0]
    #|      [entire] = [entire];
    #|}
  let cases = @itl_expr.parse_itl(source).unwrap()
  inspect(cases.map(c => c.id()).join(" "), content="bounds:1 bounds:2")
  inspect(cases[1].operation(), content="mul")
  inspect(cases[1].operands().join(" "), content="[1.0,2.0] [entire]")
  inspect(cases[1].expected(), content="[entire]")
}

一条语句可以跨越多行,以 ; 结束。端点可以是十进制或十六进制(0x1.8p1)文本,并且可以识别字面量 [empty]、[entire] 和 [nai]。

检查装饰

预期值上的装饰(decoration)后缀(_com、_dac、_def、_trv、_ill)使装饰成为测试的一部分。没有后缀时只比较集合。失败消息以中点-半径形式打印区间:

///|
test "decorations" {
  let source =
    #|testcase deco {
    #|  add [1.0,2.0]_com [3.0,4.0]_com = [4.0,6.0]_com;
    #|  add [1.0,2.0]_com [3.0,4.0]_com = [4.0,6.0]_def;
    #|  add [1.0,2.0]_com [3.0,4.0]_com = [4.0,6.0];
    #|}
  let cases = @itl_expr.parse_itl(source).unwrap()
  let passed = cases.map(c => @itl_expr.execute_case(c).passed().to_string())
  inspect(passed.join(" "), content="true false true")
  inspect(
    @itl_expr.execute_case(cases[1]).message(),
    content="expected 5p0 +/- 1p0_def, got 5p0 +/- 1p0_com",
  )
}

数值、布尔值与重叠状态

除区间结果外,ITL 用例还可以预期一个数值(inf、sup、mid、rad、wid、mag、mig)、一个布尔值(isEmpty、subset、isMember、…)或一个重叠状态名:

///|
test "other result kinds" {
  let source =
    #|testcase kinds {
    #|  wid [1.0,3.5] = 2.5;
    #|  subset [1.0,2.0] [0.0,3.0] = true;
    #|  isMember 4.0 [1.0,3.0] = false;
    #|  overlap [1.0,2.0] [2.0,3.0] = meets;
    #|}
  let cases = @itl_expr.parse_itl(source).unwrap()
  let summary = @itl_expr.summarize_results(
    cases.map(c => @itl_expr.execute_case(c)),
  )
  inspect(summary.passed_cases(), content="4")
}

不支持的用例与格式错误的用例

执行器未实现的用例为 Unsupported;操作数无法读取的用例为 Diagnostic。两者都不计为失败,但诊断用例会使 success() 为 false:

///|
test "dispositions" {
  let source =
    #|testcase odd {
    #|  mulRevToPair [1.0,2.0] [3.0,4.0] = [1.0,2.0];
    #|  sqrt [one,two] = [1.0,2.0];
    #|}
  let results = @itl_expr.parse_itl(source)
    .unwrap()
    .map(c => @itl_expr.execute_case(c))
  let summary = @itl_expr.summarize_results(results)
  inspect(summary.unsupported_cases(), content="1")
  inspect(summary.diagnostic_cases(), content="1")
  inspect(summary.success(), content="false")
}

深入了解

  • 语料运行。 just conformance run interval 获取固定版本的 ITF1788 语料,构建 native 运行器,并运行 testdata/interval/interpreter_stages.json 中列出的严格阶段;参见验证。
  • 按运算过滤。 CLI 的 --operation NAME 选项只保留该运算的用例;在代码中,可在执行前按 operation() 过滤 cases。
  • 精度。 execute_case(case, precision=p) 以 p 位解析端点;区间运算本身舍入到 binary64,因此对 ITF1788 数据请保持默认值 53。

常见陷阱

  • 十进制端点按就近舍入。 诸如 0.1 的端点被读作最接近的 binary64 数,而不是向外舍入,因此 [0.1,0.1] 是由该 binary64 数构成的单点区间。
  • 不检查信号。 预期值之后的 signal … 等 ITL 注解会使预期值无法读取;这类用例会成为不支持或诊断用例,而不会通过。
  • 解析错误会使输入被拒绝。 只要有任何语句缺少 = 或运算,或者文本在语句中途结束,parse_itl 就只返回诊断。

后续步骤