如果在数字世界里,1 加 1 永远等于 2,那么为什么一段十年前完美运行的代码,在今天会突然“背叛”它的创造者?
2013 年,Valve 公司的工程师 Tom Forsyth 在移植 VR 版《半条命 2》时,就撞上了这堵“幽灵墙”。游戏开场那扇本该自动开启的大门,毫无征兆地将玩家困死在走廊里 。当恐慌的开发团队回溯到十年前的原始代码并重新编译时,他们惊恐地发现:这个 Bug 竟然穿越回了过去,连当年的原始版本也坏了 。
经过漫长的排查,真相令人啼笑皆非:仅仅是因为编译器底层的数学指令集变了。2004 年老旧的 x87 指令集计算精度参差不齐,那种“模糊”恰好让门后的守卫被撞开;而 2013 年现代 CPU 标配的 SSE 指令集太过“精确”,导致守卫被撞开的角度微乎其微地减小了——那多出来的几微米脚趾,卡死了整扇大门 。
正如 Forsyth 所遭遇的那样,代码的逻辑虽然是静态的,但承载它的数字土壤却是流动的。
在游戏中,这种浮点数(Floating Point)的幽灵只是让工程师不得不把守卫往后挪了 1 毫米 ;但在现实世界,类似的数学幽灵却曾索取过血的代价。
1991 年海湾战争期间,美军的“爱国者”导弹防御系统也遭遇了浮点数的背刺。由于系统内部时钟将“0.1 秒”转换为二进制时产生了微小的截断误差,在连续开机运行 100 小时后,这个微不足道的误差被放大了 0.34 秒。对于高速飞行的飞毛腿导弹而言,0.34 秒意味着超过 500 米的距离偏差。最终,雷达在错误的空域搜索目标,导致拦截失败,28 名美军士兵在睡梦中丧生。
这两个故事放在一起,构成了一则关于现代文明脆弱性的深刻寓言。《半条命 2》的门是因为计算“太精确”而卡死,爱国者导弹则是因为计算“不够精确”而失灵。它们共同指向了一个事实:我们引以为傲的数字大厦,建立在并不完美的浮点数地基之上。
当你在屏幕前看着那个被移动了 1 毫米的守卫而会心一笑时,请记得,在代码的深渊里,那是从喜剧到悲剧之间,最微窄的距离。
原贴:https://mastodon.gamedev.place/@TomF/115589925206309168
爱国者导弹事件:
https://www.cs.unc.edu/~smp/COMP205/LECTURES/ERROR/lec23/node4.html
https://www.gao.gov/products/imtec-92-26
https://www-users.cse.umn.edu/~arnold/disasters/patriot.html
