用 Python 验证
- 跟踪一个公式从 SymPy 推导到页面、再到测试的全过程。
- 用相对差(relative difference)比较两个计算结果,并选择一个容差(tolerance),使舍入不会导致失败,而真正的错误无法通过。
- 在自己的计算机上运行本站的检查。
一个来源,两种实现
Section titled “一个来源,两种实现”本站的每个公式都只在 packages/pe-core/equations/equations.yaml 中出现一次,以 SymPy 表达式的形式存放,并附有其符号、测试和引用。Python 包 python/pe_core 以该文件为基础工作:
- 在可能的情况下,
python/pe_core/derive/中的脚本从基本原理推导公式,用 SymPy 逐步求解电路的平衡方程(balance equation)。推导页面按代码的计算过程展示这些步骤,pytest 则检查每个结果是否等于公式目录中的表达式。 python scripts/gen_equations.py生成页面上显示的 LaTeX 和测试向量(test vector),测试向量包括每个公式自身的测试,以及由 SymPy 计算的随机输入。CI 会重新运行它,只要有任何差异就失败。
页面和工具背后的 TypeScript 引擎有自己的求值函数,它们是手工编写的,而不是从同一文件生成的,这样一方的错误就不会被复制到另一方。vitest 用测试向量检查每个求值函数。
两个计算结果用它们的相对差来比较:
- 相对差
- 参考值
- 被检验的值
假设、约定与出处
- 假设
- 定义
- 备注
- 物理量按比例缩放(例如单位从伏特换成毫伏)时,相对差不变,因此同一个容差适用于所有物理量(零点会移动的单位是例外,例如摄氏度相对于开尔文);参考值为零时相对差无定义,参考值接近零时,很小的绝对差也会变成很大的相对差。本站对同一公式的 TypeScript 与 Python 计算结果要求 1e-9,对仿真与其公式的比较要求 1 % 或 2 %
- 出处
- Higham 2002, Sec. 1.2 (Relative Error and Significant Digits)
- 公式 id
num.rel_diff· equations.yaml 条目
物理量按比例缩放时(例如单位从伏特换成毫伏),相对差不变,因此同一个容差适用于所有物理量。参考值为零时相对差无定义 [Higham 2002, Sec. 1.2],参考值接近零时它会夸大很小的差异,因此在那里应与一个尺度比较。两个正确的程序不一定产生逐位相同的结果:每次运算都对其结果舍入,而结果取决于运算的顺序。一次舍入使一个数改变的量,相对于其大小,至多为该格式的单位舍入误差(unit roundoff):
- 单位舍入误差(unit roundoff)
- 基数(二进制为 2)
- 精度(尾数位数)
假设、约定与出处
- 假设
- 舍入到该格式中最接近的数,即 IEEE 754 的默认方式
- 备注
- 将实数舍入到该格式中最接近的数,所引起的变化至多为其自身的这一比例(在规格化数范围内)。对于 binary64,即 JavaScript 的数值和 Python 的 float 所采用的 IEEE 754 双精度,基数为 2,精度为 53 位,因此 。Higham 将精度记为
- 出处
- Higham 2002, Ch. 2 (Floating Point Arithmetic: the unit roundoff, Sec. 2.1, and its value in IEEE double precision)
- 公式 id
num.unit_roundoff· equations.yaml 条目
本站要求同一公式的 TypeScript 计算结果与 Python 计算结果相差在十亿分之一以内。这是单位舍入误差的数百万倍,因此只要计算过程不像相消(cancellation)即几乎相等的两数相减那样放大误差 [Higham 2002, Sec. 1.7],舍入就能通过;同时它又远低于任何真正的错误,例如常数错误或漏掉一个因子。仿真与其公式比较时采用较宽松的容差,因为公式把电路理想化了:它假定纹波很小 [Erickson & Maksimović 2020, Ch. 2],而 SPICE 库中的开关管和二极管只是接近理想。SPICE 库的各个值与理想公式之差必须在 1 % 以内(以各物理量在一个周期内的最大绝对值为基准),浏览器内仿真器的电压变换比和纹波则须在 2 % 以内。
用另一个求解器校验
Section titled “用另一个求解器校验”scripts/spice_crosscheck.py 根据电路原理图,而不是仿真器的方程,把浏览器内仿真器的电路重新写成网表,并在 ngspice 中运行 [ngspice – open source SPICE simulator]。CI 将仿真器与 ngspice 的结果比较:每个值之差都不得超过该物理量在一个周期内最大绝对值的 0.5 %,并为 SPICE 元件引入的影响留出少量余量;每个峰峰值摆幅之差须在 2 % 以内。
将仿真所得的电压变换比与理想值比较,并计算 binary64,即 JavaScript 的数值和 Python 的 float 所采用的 IEEE 754 双精度的单位舍入误差:
| 符号 | 数值 | 求法 |
|---|---|---|
| 被检验的值 | 0.496 | 已知 |
| 参考值 | 0.5 | 已知 |
| 基数(二进制为 2) | 2 | 已知 |
| 精度(尾数位数) | 53 | 已知 |
| 相对差 | 0.008 | 数值相对于参考值的相对差 |
| 单位舍入误差(unit roundoff) | 1.11e-16 | 浮点格式的单位舍入误差 |
该变换比与理想值相差 0.008,在库的 1 % 以内。单位舍入误差为 1.11e-16:十亿分之一的容差可以容纳数百万次舍入。
▶ 在公式浏览器中打开:数值相对于参考值的相对差
预设取自例题 num-parity
扫描被测值:相对差在参考值两侧线性增大,只在参考值本身处为零。
在实验台上用万用表测量变换器的输入电压和输出电压,用 Python 计算两者之比相对于理想值的相对差,并列出可能造成这一差异的因素:二极管压降、开关管和绕组的电阻、纹波。然后改变负载,观察其中哪些因素随电流增大。
- 参考值接近零时,相对差失去意义;应像库的检查那样,与一个尺度比较,例如该物理量在一个周期内的最大值。
- 共用代码的两种实现也共有其中的错误。生成的求值函数即使在源文件出错的地方,也会与该文件一致。
- 浮点数(floating-point)不作相等比较。在 binary64 中,0.1 + 0.1 + 0.1 不等于 0.3:应在容差范围内比较(单位舍入误差)。
- 工具 SymPy: Solve Equations
SymPy 如何针对某个符号求解方程或方程组;本站的推导脚本就是这样求解平衡方程的。 - 技术文章 Floating-Point Arithmetic: Issues and Limitations
为什么在计算机中 0.1 + 0.1 + 0.1 不等于 0.3,为什么每次运算都可能引入最多 2**53 分之一的舍入误差,以及为什么比较结果时看其是否在容差之内,而不是看是否相等。
本站显示的公式来自哪里?
答案与解析
正确答案:B 生成器根据该条目生成 LaTeX 和测试向量;若已提交的文件与生成结果不同,CI 就会失败。
为什么 TypeScript 求值函数是手工编写的,而不是从 equations.yaml 生成的?
答案与解析
正确答案:A 生成的代码即使在源文件出错的地方也会与之一致;独立的实现则必须凭自身与测试向量一致。
将仿真得到的电压变换比 0.495 与理想值 0.5 比较。相对差是多少?
答案与解析
正确答案:D 差值 0.005 除以参考值 0.5 得 0.01,即 1 %。
binary64 的单位舍入误差约为 1.1e-16。为什么一致性检查的容差是十亿分之一,而不是单位舍入误差?
答案与解析
正确答案:B 十亿分之一是单位舍入误差的数百万倍,因此在不放大舍入误差的计算中,舍入能够通过;而错误的常数或因子造成的差异要大得多。
为什么参考值接近零时相对差会失效?
答案与解析
正确答案:A 在这种情况下,应像库的检查那样,与一个尺度比较,例如该物理量在一个周期内的最大值。
在 Python 中,0.1 + 0.1 + 0.1 == 0.3 的计算结果是:
答案与解析
正确答案:C 0.1 无法用二进制精确表示,三者之和经舍入后为 0.30000000000000004,是与 0.3 相邻的双精度数;应在容差范围内比较。
本站例题、预设和练习中的数值均为合成值:为教学而凑整的数值,并非来自任何实际设计的数据。