From 126c4bf57f95e412c01c231a9314e5f4ab6f00aa Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Thu, 29 Oct 2026 17:29:15 +0800 Subject: [PATCH] =?UTF-8?q?docs(algo):=205.3=20=E8=8A=82=E8=A1=A5=E7=AA=97?= =?UTF-8?q?=E9=A6=96=E4=B8=8B=E6=A0=87=E7=9A=84=E5=9C=86=E5=91=A8=E5=9B=BE?= =?UTF-8?q?=EF=BC=88=E4=B8=89=E7=A7=8D=E5=86=99=E6=B3=95=E5=AF=B9=E7=85=A7?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增配图 采样环缓冲_窗首下标.svg:左侧 40 格圆环走一遍 head = 5、n = 20 的「往回退 19 步」(跨过 0 与 39),右侧把直接减 / 16 位回绕后再取模 / 先加 DEPTH 三种写法的结果并列,只有第三种落在 25。 - 与 5.2 的环形布局图分工:那张讲「窗在哪几格」,这张讲「下标怎么算出来」, 不重复同一张图的语义。 - 生成器 gen_sample_ring_start_svg.py 顺带把 OVERFLOW 明细改到末尾统一打印 (原先只在开头打,折行类告警只报计数、看不出是哪一条)。 - 门禁:doc-links --sections-strict 通过;id-check 新增行无未登记编号; generated-fresh 22 个生成器 17 一致、0 新漂移;产物连跑两次 sha1 不变。 --- docs/算法/采集与算法层设计说明.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/算法/采集与算法层设计说明.md b/docs/算法/采集与算法层设计说明.md index 1ed621c1..1e41d09e 100644 --- a/docs/算法/采集与算法层设计说明.md +++ b/docs/算法/采集与算法层设计说明.md @@ -205,15 +205,18 @@ flowchart TD 代码里写成 `(s_head + SAMPLE_RING_DEPTH - n) % SAMPLE_RING_DEPTH` 而不是 `(s_head - n) % DEPTH`, 是因为后者有两个独立的坑: -- **16 位无符号下溢后再取模不等价**:`s_head < n` 时减法先回绕成 `65536-(n-s_head)`, - 而 65536 不是 40 的倍数,`(x mod 65536) mod 40 ≠ x mod 40`。反例 `s_head = 3`、`n = 20`: - 正确窗首是 23,回绕后得 65519,`65519 % 40 = 39` → 整窗错位。 -- **有符号负取模**:两个 `uint16_t` 先提升成 `int`,`3 - 20 = -17`;C99 的 `%` 向零截断, - `(-17) % 40 == -17`(不是 23),再转回无符号就是一次**越界读**——不报错、不 HardFault。 +- **16 位无符号下溢后再取模不等价**:`s_head < n` 时减法先回绕成 `65536-(n-s_head)` = 65521, + 而 65536 不是 40 的倍数,`(x mod 65536) mod 40 ≠ x mod 40`。同一个例子:正确窗首是 25, + 回绕后 `65521 % 40 = 1` → 整窗错位,读到的是环尾附近。 +- **有符号负取模**:两个 `uint16_t` 先提升成 `int`,`5 - 20 = -15`;C99 的 `%` 向零截断, + `(-15) % 40 == -15`(不是 25),再转回无符号就是一次**越界读**——不报错、不 HardFault。 **先加一个 DEPTH** 同时解决两件事:`DEPTH ≡ 0 (mod DEPTH)`,加它不改变同余类、数学值不变; 而 `s_head + DEPTH - n ∈ [0, 2·DEPTH - 2]`,恒非负、远小于 65535,两种整数提升规则下都不下溢。 -逐条反例与边界自检(`n == DEPTH` 时 `start == s_head`)见 `App/algo/sample_ring.c` 同名函数的注释。 + +边界自检:`n == DEPTH` 时 `start == s_head`——环满时「下一拍要覆盖的那格」就是最老点; +未写满时 `s_head == s_filled`,故 `start >= 0`,窗一定不跨环尾。 +`App/algo/sample_ring.c` 里只留结论与指向本节的指针,推导以本节为准。 ### 5.4 API 契约