# 准确性保证 (/zh/docs/guide/about/accuracy)

716,314 例金标测试如何保证 x-iztro 与 JS iztro 零差异，以及这个「准」的边界在哪。



*适合：所有人。「哈希比对怎么做」一节给开发者*

排盘库最重要的属性是**结果正确**。而「正确」在紫微斗数里没有权威裁判 ——
不同实现之间的差异往往来自流派取舍，很难说谁对谁错。

x-iztro 因此把目标定得很具体：**与 JS
[iztro](https://github.com/SylarLong/iztro) v2.6.1 逐字段一致**。
把它当作金标准，差异就从「见仁见智」变成了可以自动检测的 bug。

## iztro 是什么，为什么拿它当金标准 [#iztro-是什么为什么拿它当金标准]

iztro 是一个 TypeScript 写的开源紫微斗数排盘库，
是这个领域里最完整、维护时间最长的开源实现之一，
不少前端项目与小程序在用。

选它做基准的理由不是「它一定对」，而是三条工程上的性质：

1. **完整**：本命盘、六层运限、四组十二神、年系杂耀、中州派、六种盘面语言，
   一个不缺 —— 有得可对，才对得下去。
2. **确定**：同样的输入永远给同样的输出，没有随机与外部依赖，
   所以差异一定是逻辑差异，不是噪声。
3. **可锁版本**：把版本钉在 v2.6.1，基准就是稳定的；
   iztro 升级时重新生成基准数据，失败的用例清单就是版本间的行为差异清单。

## 这个「准」指什么、不指什么 [#这个准指什么不指什么]

<Callout type="warn" title="与 iztro 一致 ≠ 命理界唯一正解">
  x-iztro 保证的是：**在同一套流派取舍下，算得与一个成熟实现完全一样**。

  它**不保证**这套流派取舍本身是「对的」。
  庚干化科取太阴还是天府、年干支按正月初一还是立春换、晚子时归今天还是明天 ——
  这些历来就有分歧，iztro 选了一套，x-iztro 原样跟随，并把有分歧的地方
  做成[配置开关](/zh/docs/guide/guides/config)让你自己决定。

  如果你的流派与默认不同，改配置或用自定义四化表，别期待默认输出符合你的师承。
</Callout>

<Callout title="金标数据的年份区间">
  基准数据由 JS 侧生成，用例集中在 JS 实现能稳定生成的年份区间内，
  边界年代（1583–1983 与 2044–2100）另有按十年抽样的一层。

  x-iztro 本身支持公历 1583–9999 年，区间之外的年份能排出盘，
  但**没有金标数据逐例对照过** —— 用在极端年份上时请自行验证。
</Callout>

## 覆盖矩阵 [#覆盖矩阵]

全部基准数据由锁定版本的 JS iztro 生成，共 716,314 例：

| 层级        | 用例数         | 覆盖范围                                                          | 数据格式        |
| --------- | ----------- | ------------------------------------------------------------- | ----------- |
| Tier 1    | 1,560       | 60 年 × 13 时辰 × 男女，**全字段逐一比对**（含展示字段、来因宫与结构化日期）                | 完整 JSON     |
| Tier 2    | 37,440      | 60 年 × 每月 1/15 号 × 13 时辰 × 男女                                 | 压缩 JSON     |
| Tier 3    | 586,430     | 60 年**每一天** × 13 时辰 × 男女 × fix\_leap（闰月双份）                    | SHA-256 CSV |
| 边界年代      | 46,228      | 1583–1983 与 2044–2100 每 10 年抽样，补 Tier 1/2/3 只覆盖 1984–2043 的盲区 | SHA-256 CSV |
| Horoscope | 5,760       | 360 命盘 × 16 目标日期，六层级运限全字段                                     | 紧凑 JSON     |
| Variants  | 14,268      | by\_lunar 闰月逐日、中州派、六种盘面语言                                     | CSV / JSON  |
| Config    | 9,696       | 四个分界开关的非默认取值，含排盘层与运限层的组合                                      | CSV / JSON  |
| 中州派盘型     | 12,488      | 天盘 / 地盘 / 人盘                                                  | SHA-256 CSV |
| 1602 窗口   | 2,444       | 1602-2-20 至 4-25 逐日 × 13 时辰 × 男女（闰月日期含 fix\_leap 双份），锁定闰二月修正层 | SHA-256 CSV |
| **合计**    | **716,314** |                                                               |             |

以下不计入上表：

* **翻译反查 1,559 例**：逐条对照 iztro `kot` 的实际取值，守的是同形译名的消歧顺序
* **绑定契约 13 例**：把 DTO 与 iztro 的 `JSON.stringify` 输出逐键逐值对照
* **Python 端到端 209 例**（含自定义四化表与亮度表、全时辰覆盖、格局与知识包）
* **Go 端到端测试**：金标对照、星耀落宫、并发正确性、覆盖表、非法输入轰炸
* **C FFI 边界安全测试**：任何非法输入都必须返回错误 JSON 而非崩溃
* **Prompt 快照测试**：中英两种语言的本命与运限 prompt 逐字节比对

## 每一层在防什么 [#每一层在防什么]

**Tier 1** 抓字段级差异。它比对包括展示串、来因宫标记在内的每一个字段，
一旦某个字段的翻译或格式与 iztro 不同，立刻暴露。

**Tier 2 与 Tier 3** 抓边界日期。紫微斗数的错误常常只在特定日期出现 ——
闰月、月末、年初、节气交接。Tier 3 覆盖 60 年里的每一天，一天都不漏。

**边界年代**抓年份两端。Tier 1/2/3 集中在 1984–2043，
这一层按十年抽样把范围拉到 1583 与 2100，
防的是历法算法在远端年份上悄悄走样。

**Horoscope** 抓运限。16 个目标日期专门选在会出问题的位置：
12 个流年支各一、童限、高龄、闰月、晚子时。

**Variants** 抓流派与盘面语言。中州派与六种盘面语言各自完整比对，
确保切换算法派别或语言不引入偏差。

**Config** 与**中州派盘型**抓分界点与盘型。立春窗口逐日、晚子时、生日前后 ——
每个开关都在它会产生分歧的窗口里逐日验证。

**1602 窗口**抓农历依赖本身的缺陷。Rust 侧农历库的月表在 1602 年自相矛盾
（二月 31 天），x-iztro 在月表唯一读取入口按真值修正——真值经 lunar-typescript、
寿星天文历（sxtwl）与韩国天文研究院历表三个独立来源交叉确认——这一层把修正窗口
逐日锁死；另有 1583–9999 全域逐日扫描（约 614 万盘，标 `#[ignore]`）未发现
第二个同类窗口。

## 哈希比对怎么做的 [#哈希比对怎么做的]

<small>
  给开发者
</small>

Tier 3 有 58 万例，存完整 JSON 会有几十 GB。所以这几层比对的是**规范化串的 SHA-256**：

JS 侧的 `tests/golden/canonical.mjs` 与 Rust 侧的 `tests/common/mod.rs`
实现同一套序列化规则，**逐字节同构**。两边各自把排盘结果压成同一个规范化串，
比对哈希即可 —— 存的是 SHA-256 的前 32 个十六进制字符，而不是几十 KB 的 JSON。

哈希不一致时，用生成器的 `--inspect` 系列参数重放该例的 JS 输出，
与 Rust 的规范化串做 diff，直接定位到出错字段。

## 跑测试 [#跑测试]

```bash
# 常规层：单元 + Tier 1/2 + 运限 + 变体 + 配置 + 契约 + 1602 窗口，约 1 分钟
cargo test

# Tier 3 全量：586,430 例，约 70 秒
cargo test --release --test golden_tier3 -- --ignored

# 绑定端到端
cd python && pytest tests/     # 需先 maturin develop
cd go/iztro && go test ./...
```

## 重新生成基准数据 [#重新生成基准数据]

需要 Node.js 环境：

```bash
cd tests/golden
npm ci
npm run gen:all   # 全部层级；逐个生成器见 package.json 的 gen:* 脚本
```

tier3 与边界年代层按年跳过已存在的文件，便于断点续跑；tier3 另支持
`node generate_tier3.mjs --range <起> <止>` 分段，多进程并行可跑满 CPU（全量约 30 分钟）。

## 跟进 iztro 新版本 [#跟进-iztro-新版本]

流程是固定的：

1. 升级 `tests/golden/package.json` 里锁定的 iztro 版本
2. 重新生成全部基准数据
3. 跑 `cargo test`

失败的用例清单就是两个版本之间的行为差异清单 —— 不需要读 changelog，
测试直接告诉你哪些字段变了。

<Callout>
  这条流程覆盖的是**数值**差异。iztro 新增或删除 API 时测试不会报，
  那部分要按[从 iztro 迁移：API 对照](/zh/docs/guide/about/iztro-parity)一页逐条自查。
</Callout>

## 零容忍的含义 [#零容忍的含义]

任何一例不一致都当作 bug 处理，不接受「差异很小」「这个字段不重要」这类理由。
凡是 iztro 有的功能与数据，x-iztro 必须给出相同结果；
在此之上再谈扩展功能（语言无关标识、Prompt 生成、Config 开关的语义化）。
