在对Autodesk TinkerBox的更新中,我在 Windows 上运行的仅限内部开发版本与在我们的最终目标 iOS 上运行的版本之间遇到了意外的浮点计算差异(以下信息基于调试构建在 iPad1 上运行)。
我们使用Chipmunk来满足我们的物理需求。这绝不可能是这个问题的唯一计算,但它是我正在分析的一个特殊计算:
static inline cpFloat
cpvcross(const cpVect &v1, const cpVect &v2)
{
return v1.x*v2.y - v1.y*v2.x;
}
我正在查看的特定情况v1
为 (0xC0A7BC40 [-5.241729736328125], 0xC0E84C80 [-7.25933837890625]) 和v2
(0x428848FB [68.14253997802734], 0x42BCBE40 [94.371582032])。我专注于值的十六进制版本,因为它们是两个平台上输入的确切值,通过检查两个平台上的内存位置来v1
验证v2
。作为参考,括号中的浮点值是从将十六进制值放入此站点中获取的。
在 Windows 上,结果是 0xBA15F8E8 [-0.0005720988847315311],在 iOS 上,结果是 0xBA100000 [-0.00054931640625]。当然,差异很小,但当您考虑百分比时,差异并不大,它会随着时间的推移而累积,以显示物理行为的偏差。(请不要建议使用双打。它会减慢游戏速度,当然,不使用双打不是这里的问题。:))
作为参考,这是两个平台上的调试版本,代码编译为:
Windows
static inline cpFloat
cpvcross(const cpVect &v1, const cpVect &v2)
{
01324790 push ebp
01324791 mov ebp,esp
01324793 sub esp,0C4h
01324799 push ebx
0132479A push esi
0132479B push edi
0132479C lea edi,[ebp-0C4h]
013247A2 mov ecx,31h
013247A7 mov eax,0CCCCCCCCh
013247AC rep stos dword ptr es:[edi]
return v1.x*v2.y - v1.y*v2.x;
013247AE mov eax,dword ptr [v1]
013247B1 fld dword ptr [eax]
013247B3 mov ecx,dword ptr [v2]
013247B6 fmul dword ptr [ecx+4]
013247B9 mov edx,dword ptr [v1]
013247BC fld dword ptr [edx+4]
013247BF mov eax,dword ptr [v2]
013247C2 fmul dword ptr [eax]
013247C4 fsubp st(1),st
013247C6 fstp dword ptr [ebp-0C4h]
013247CC fld dword ptr [ebp-0C4h]
}
013247D2 pop edi
013247D3 pop esi
013247D4 pop ebx
013247D5 mov esp,ebp
013247D7 pop ebp
013247D8 ret
iOS
invent`cpvcross at cpVect.h:63:
0x94a8: sub sp, sp, #8
0x94ac: str r0, [sp, #4]
0x94b0: str r1, [sp]
0x94b4: ldr r0, [sp, #4]
0x94b8: vldr s0, [r1]
0x94bc: vldr s1, [r1, #4]
0x94c0: vldr s2, [r0]
0x94c4: vldr s3, [r0, #4]
0x94c8: vmul.f32 s1, s2, s1
0x94cc: vmul.f32 s0, s3, s0
0x94d0: vsub.f32 s0, s1, s0
0x94d4: vmov r0, s0
0x94d8: add sp, sp, #8
0x94dc: bx lr
据我所知,这些计算是相同的,假设每条指令都以相同的方式计算操作数的结果。出于某种原因(Visual Studio 确实允许),Xcode 不允许我逐条指令执行,因此与英特尔 FP 单元相比,我无法缩小哪些指令偏离的范围。
那么,为什么这么简单的计算结果在两个 CPU 之间会如此不同呢?