43 段「本段用时」相加是 12616 秒,总时长那一格写 12600
距离填 42.195,总用时填 3:30:00,段长停在 1 公里、递进 0,页面给出一张 43 行的分段表。把「本段用时」那一列的 43 个显示值挨个加起来是 12616 秒,比同一帧总时长的 12600 秒多出 16 秒。可「累计用时」那一列的末行照样写着 3:30:00,它拿的是没逐段取整过的累计秒。两列并排站在同一张表里,一列闭合,一列差 16 秒。
段数没错,43 行的起止与长度都对得上 42.195。不一致的是尺:每一行的用时先各自四舍五入成整数秒再上屏,43 个整数加起来就不再等于那个整数秒的总时长。
同一处口径差在半马示例那张表上更扎眼:第 3 行「本段用时」的前三格相加是 26:15 加 25:25 加 24:35,得 1:16:15,而同一行的「累计用时」列写着 1:16:14,那 1 秒的差就摆在挨着的两列里。
段长一换,漂移连方向一起变
距离与总用时不动,只拧段长那一格,差值在正负之间来回跳,也能正好落回 0。
| 场景 | 段长 | 段数 | 逐段相加 | 与总时长比 |
|---|---|---|---|---|
| 42.195 / 3:30:00 | 1 | 43 | 12616 秒 | 多 16 秒 |
| 同上 | 2 | 22 | 12595 秒 | 少 5 秒 |
| 同上 | 5 | 9 | 12599 秒 | 少 1 秒 |
| 同上 | 0.3 | 141 | 12658 秒 | 多 58 秒 |
| 21.0975 / 1:45:00 | 1 | 22 | 6308 秒 | 多 8 秒 |
| 26.2188 英里 | 1 | 未取回 | 未取回 | 多 11 秒 |
| 10 公里 / 1 小时 | 1 | 未取回 | 每段 360 整秒 | 差 0 秒 |
同一帧的读数各用一把尺
配速那一格走整数秒取整,总时长串、长格式串、带符号差又各走一次同样的取整;时速、英里/时、每秒米三格是乘 100 再除 100 的两位小数;距离回写框是乘 10000 再除 10000 的四位小数;段表的起止区间留一位小数、累计距离留三位;段数那一行是 Math.floor 之外再补一个 1e-9。这几把尺互不闭合,最直白的证据是拿显示值反推推不回显示值。
真值配速 298.61357980803410328 秒上屏成 4:59。用 4:59 那 299 秒反推时速是 12.04,页面时速格写着 12.06 km/h;反推每秒米得 3.34,页面写着 3.35 m/s;400 米圈显示 1:59,乘 2.5 是 297.5 秒每公里,与配速格那 299 秒差 1.5 秒每公里。每一格在自己那层都没错,跨一格就散。跑步配速计算器上这六格写的是同一个秒数。
图 1 竖着读就是那把尺的清单:同一秒数被切成整数、两位、四位、一位、三位五种写法。
把那个 1e-9 单独删掉,108 组一段都没少
段数那一行末尾的 epsilon 看着像段账漂移的嫌疑人。其余 416 行一字不动、另开一个上下文把它摘掉,扫 108 组距离与段长:段数差全部为 0,末段长度的最大差 4.86e-15,末段累计终点的差 7.1e-15,段说明串逐字相同。真正把那一段补回来的是紧接下一行的余数分支,剩下的长度小于段长乘 1e-6 就当没有余数。所以这个 epsilon 改的是第 14 位有效数字,一位可见显示都没动,那 16 秒跟它无关。
英里档回写四位小数,换回来就不是原来那个数
单位常数 RP_MILE 是 1.609344。42.195 公里切到英里档,距离框被回写成 26.2188,真值 26.218757456454305554,四位这一层把它抬高而不是截短。再把框里的 26.2188 当英里换算回公里,出来 42.195068467200002260,一趟往返漂了 0.0000685 公里。每英里配速也有两条路:直接用英里距离去除得 480.57197298258091678 秒,拿公里配速乘常数得 480.57197298258085993 秒,差 5.68e-14,两档上屏倒是都写着 8:01。
屏幕上那个 4:59 再填回去,完赛时间成 3:30:16
先在时间框打 3、30、0,总用时 12600 秒,配速格显示 4:59。再按这个显示值把 4 分 59 秒提交进配速框,页面拿 299 乘 42.195 得 12616.305 秒,总用时格上屏 3:30:16。一个来回多出 16 秒,跟段表那 43 段相加多出来的 16 秒是同一件事:整数秒的配速乘回带四位小数的距离,落不回原来那个整数秒。目标档那一行「3:30:00 对应 4:59」把右列乘回去也差 16.305 秒,同一张表左右两列互不相认。两串差多少,可以在时间加减计算里按秒相减。
图 2 那四行是同一件事的四种写法:加过一次的 16 秒,减回去也还是 16 秒。
同一个终点,三张表三种算法
42.195 公里这一帧里被算了三遍:逐段推进那套和「距离乘均速配速」都落在 12599.999999999998 秒,目标档的 3:30:00 是 12600 秒,显示层三个一律写 3:30:00,前两个带着 2e-12 的浮点尾巴。行数也各按各的口径:里程碑公里档 12 行、英里档 11 行、半马距离只给 7 行,预测表剔掉与当前距离同档那点剩 7 行,目标档全马 8 档、非标准距离按六个系数给 6 行。至于段与段之间该读成什么,是站内那篇分段配速表的范围,这一篇只停在取整与段账上。
Q:这些数字是浏览器里量出来的吗?
不是。模板 609 行 / 27084 字节,脚本块那 417 行逐行比对未命中 0 行,块内 42 个顶层函数用 toString 读回在原文里逐字命中 42/42,全程没重写没修 bug,读数全部来自纯 Node 的 vm。排版、滚动条与耗时一律未实测。
Q:段表相加对不上,是页面算错了吗?
每一格在自己那一层都对,只是层与层之间不闭合:逐段取整的那一列与未取整的累计列并排站着,16 秒就落在两列中间。
Q:距离框填 1e400 会怎样?
它被解析成 1400 静默写进距离,提示为空,大距离这一侧没有任何拦截;那一场景的段表行数在探针里因输出截断没取回,未实测。