一次点击,屏幕上站着两个互不相干的数
在默认那组男性输入上(身高 170 cm、体重 70 kg、30 岁、腰围 80 cm、颈围 38 cm),点一次「计算体脂率」,页面会同时给出两个百分比:大字那格是 13.7%,下面那行小字是 19.8%,相差 6.1 个百分点。换成女性默认那组(同样参数再加臀围 95 cm),结果是 25.6% 与 30.6%,差 5.0 个百分点。
第一反应通常是「下面那个更粗、上面那个更细」。但它们不是同一个公式的两档精度,而是两套公式,输入集合完全不相交:13.7% 走美国海军体围法,只读身高与围度,式子里没有体重这一项;19.8% 走由 BMI 换算的那条式子,只读身高、体重、年龄、性别,式子里没有腰围这一项。我把 体脂率计算器 模板里那两条式子原样抽出来跑了一遍,下面每个数字都是同一次点击能复现的输出,不是估计。
把体重推满 10 kg,只有一个数在动
最能说明这层关系的是改体重。取男 175 cm、腰围 90 cm、颈围 38 cm、32 岁这一组,把体重从 73 kg 推到 83 kg:大字那侧始终是 20.7%,一个小数都没变;下面那侧从 19.8% 走到 23.7%,顺带把同行的 BMI计算器 口径也推着从 23.8 走到 27.1。同一只滑块推到底,一个纹丝不动,另一个整档搬家。
年龄和性别也只喂给下面那个数。性别切一次,Deurenberg 那侧恒差 10.8 个百分点,因为换算式里的性别项就是 male ? 1 : 0 这一个数;年龄从 20 推到 60,它涨 9.2 个百分点(式子里是 0.23 * age),而海军法停在原值上,差 0.0。
腰围 1 cm 到底值多少
围度这一侧比多数人以为的灵敏。男性基准(身高 175、颈围 38、腰围 90)上,腰围动 1 cm 就是 0.7 到 0.8 个百分点,动 5 cm 是 3.4 到 3.8 个百分点。女性基准(身高 163、腰 76、颈 33、臀 96)上,腰、颈、臀各自动 1 cm 都是 0.5 个百分点,动 5 cm 是 2.5 个百分点。颈围与腰围同量反号,因为式子里只出现 waist - neck 这一个差;女性那条多出的臀围与腰围同号。
图 1:两个数并排,喂给它们的输入一格都不重叠,不是同一个量的两次近似。
图 2:只动围度、其余项不变时海军法那一侧的位移;颈围与腰围同量反号,女性臀围与腰围同向。
两个数对不上是常态,但幅度看人
这 12 组现实里真会填进去的参数(含首屏两组默认值)我逐条跑过:两个数的绝对差最小 0.1 个百分点、最大 6.1 个百分点、平均 2.44,其中 2/12 组按页面自己的阈值分别查档会落到不同档位。只取 10 组真人输入看带符号的差,范围是 -4.3 到 +1.3、平均 1.82——方向都不固定,没人能保证大字那位一定比小字那位低。
| 输入 | 大字(海军法) | 小字(BMI 换算) |
|---|---|---|
| 男 170 / 70 kg / 30 岁 / 腰 80 / 颈 38 | 13.7% | 19.8% |
| 女 同参数 + 臀 95 | 25.6% | 30.6% |
| 女 166 / 60 kg / 60 岁 / 腰 78 / 颈 32 / 臀 98 | 30.2% | 34.5% |
| 男 175 / 腰 90 / 颈 38 / 32 岁 / 83 kg | 20.7% | 23.7% |
唯一的拦截只装在男性那一侧
整套逻辑里只有一条输入校验:男性那一支要求腰围严格大于颈围,等于也不放行。实测腰 80 颈 80 与腰 80 颈 81,页面只弹一句「腰围需大于颈围」然后直接返回,屏幕上上一次的结果一位都不改——旧数字留在原地,很容易被读成新算出来的。女性那一支在两条式子之间没有任何判断:同样腰 80 颈 80,配上臀 95 照样得出 3.1%;腰 70 颈 80 臀 95 得出 -3.8%。
这两组入参全在滑块量程之内。更极端的一组也合法:女 163 cm / 58 kg,腰 50、颈 80、臀 60,得出 -59.5%,同行的脂肪重量写着 -34.5 kg、瘦体重 92.5 kg 比体重本身还大,而档位那一格印的是「必需脂肪」;男 220 cm、腰 50、颈 49 得出 -95%。守卫管的是围度之间的大小关系,不管结果像不像话。
只有另一类必须往代码里注入才碰得到:女性那条式子的对数入参 腰 + 臀 - 颈 一旦非正就走到 NaN 或 -Infinity,而 UI 把这条路堵死了。6 个输入全是 range,模板里 step= 出现 0 次所以只能取整数,越界值写进去会被浏览器夹回量程(实测 30→50、250→200、空串→125、abc→125、75.5→76)。腰围最小 50 加臀围最小 60 减颈围最大 80,这个差的最小可能值是 30;1,192,296 组整数围度全枚举一遍,非正的有 0 组。
压线那一下,两句话互相打脸
档位全部用严格小于:男性四道门槛是 6、14、18、25,女性是 14、21、25、32。实测 24.9 写「正常水平」、25.0 写「肥胖」;男性 6.0 已经进了「运动员水平」,5.9 留在上一档。麻烦在下面那行建议句用的是另一个比较条件(体脂率大于 25 才换话术),于是 25.0 这组数字同时打出「体脂偏高」和「保持当前的运动和饮食习惯,定期监测体脂变化」两句相反的话,女性的 32.0 同理。上面引号里那几格字样只是页面上的字符串,不是测量或对照出来的属性。
它们只是公式与常量的直接输出
把这个页面当成两台互不通气的计算器看待就顺了:大字那位只对身高与围度有反应,小字那位只对身高、体重、年龄、性别有反应,档位那一行则是把其中一个数拿去和四道写死的常量比大小。这类把常量印成结论的写法站内不止一处,基础代谢那篇 里也碰到过同样的读法问题:屏幕上不会出现「这是判断,不是测量」这半句话。至于谁高谁低,它随输入变号,不构成结论。
Q:两个数我该看哪个?
页面没写优先级,我也不替它写。它们不是同一个量的两次近似,而是各自只对一半输入有反应;12 组输入里两者的绝对差最大到 6.1 个百分点,把它们当成互为验证是读错了版式。
Q:为什么我改了体重,那个数一点没动?
因为大字那位的式子里根本没有体重项。73 kg 推到 83 kg,它一直停在 20.7%;同一次改动把小字那位从 19.8% 推到 23.7%。想知道某个输入有没有参与,看它动不动就够了。
Q:卷尺量得不准,影响有多大?
男性基准上腰围 1 cm 就是 0.7 到 0.8 个百分点,5 cm 是 3.4 到 3.8;女性 1 cm 是 0.5,5 cm 是 2.5。而压线的容错是零——24.9 与 25.0 分属两档,一次读数的手劲足以决定屏幕上那句建议写什么。