苹果A5和A6处理器之间的浮点确定性问题

8
我正在为iOS开发一个基于Box2D物理引擎的多人游戏。多人游戏通常使用锁步方法,游戏会定时更新物理世界。在相同CPU的iOS设备上没有出现不同步的情况。
然而,在使用苹果A6芯片的新iOS设备进行测试时,出现了不同步的问题。查看日志文件让我觉得不同步很快就发生了,可能是由于某些浮点运算引起的,但我还不能确定具体是哪个操作。
我可以保证,在游戏设计中只需要同步Box2D模块,所有的多人游戏命令和输入都没有不同步的情况。
我已经尝试将所有的三角函数:sinf、cosf、pow、sqrtf、atan2f改成双精度版本,但没有任何效果。
有没有办法强制苹果A6将浮点数处理方式与苹果A5相同,比如一些编译器选项?
非常感谢您的回答。

1
你确定你的模拟真的会被这么小的事情偏移那么多吗? - Vaughan Hilts
我怀疑 A6 会提供另一个浮点值。 - AlexWien
2
虽然这样做真的很痛苦,但找出差异的最佳方法是在两个端点上记录每个时间步的世界状态,使用浮点数的十六进制表示确保您可以对比文本文件以查找即使是单个位的差异。每次运行都要运行完全相同的输入,以找出它们分歧的时间步,然后您可以考虑在这两个时间步之间发生了什么。 - iforce2d
1
谢谢大家迄今为止的帮助,我非常感激!--- 致Vaughan Hilts:虽然差异微小,但当物体碰撞时会很快产生差异。当没有任何碰撞时,很难用肉眼观察到任何变化。 --- 致Alex Wien:我已经尝试了随机数字测试,是的,在A5和A6上结果似乎是相同的。可能有些事情我还没有发现。--- 致iforce2d:我将记录Box2D的所有数学计算,以找出差异。实际上,这种不同几乎立即发生,所以我不需要重播。 - Nguyễn Trường Chung
我已经尝试在每次调用超越函数时记录日志,但没有成功。是的,在那里发生了不同步,但它发生在传递给函数的参数上! - Nguyễn Trường Chung
3个回答

5
许多数学库函数在A5和A6上使用不同的算法。如果它们相差超过一个或两个ulp,则可能会发现错误,请报告。否则,变化可能在好质量数学库的预期容差范围内。对于为什么会这样的原因,最好的参考是Ian Ollmann几年前在mac-games-dev邮件列表中的电子邮件"the math library is not a security tool",其中讨论了Mac OS X环境下的这个确切问题。(简而言之,跨架构提供位相同的结果的目标与在所有架构上尽可能高效地提供高精度答案的目标存在根本性冲突,前者是某些游戏开发人员想要的,后者是所有开发人员(以及用户,因为它有益于响应速度和电池寿命)想要的; 在通用系统库中,后者必然具有优先权)。苹果开发者论坛也是寻找信息的另一个好地方。

非常感谢Stephen提供的链接,它非常有帮助。我将使用crlibm进行测试。一旦我解决了这个问题,我会尽快报告我的结果。 - Nguyễn Trường Chung
不幸的是,浮点数变化似乎无处不在,不仅因为超越函数,还因为不同的CPU处理器。我在苹果开发者论坛上问了同样的问题,他们告诉我应该使用另一种方法来制作游戏。我真的陷入了困境。我唯一剩下的办法就是将不同iOS版本和CPU的玩家分开。但那太糟糕了... - Nguyễn Trường Chung
arm64将带来一个全新的问题,包括fma、子标准数、不同编译器调度重排加法以及舍入控制可能潜入您的浮点模型。在它让你发疯之前,我建议放弃这种方法。任命一台机器成为现实的仲裁者,并定期与所有人进行同步。对小错误要有容忍度。这只是一个游戏,完美是被高估了的。 - Ian Ollmann

3

实际上是Nguyen Truong Chung再次出现了。

非常感谢你们迄今为止的回答。我非常感激你们的答案,它们为我启示了继续调试的道路!目前,我已经找到了不同步的原因,但还没有具体的解决方案。我希望提供给您我得到的信息,并希望我可以得到更多的洞察力。

1. 发现:

我有一个使用cos函数的函数。我像这样打印日志:

void rotateZ(float angle)

{

 if( angle )

 {

      const float sinTheta = sin( angle );

      const float cosTheta = cos( angle );



      // I logged here

      myLog( "Vector3D::SelfRotateZ(%x) %x, %x", *(unsigned int*)&angle, *(unsigned int*)&cosTheta, *(unsigned int*)&sinTheta );



      ....
 }

}

Desync发生的情况如下:

iPad4上:Vector3D::SelfRotateZ(404800d2) bf7ff708, 3c8782bc iPhone4上:Vector3D::SelfRotateZ(404800d2) bf7ff709, 3c8782bc

2. 再次测试:

故事并没有结束,因为:

  1. 我在游戏开始时尝试了这些代码行:

{ unsigned int zz = 0x404800d2;

float yy = 0;

memcpy( &yy, &zz, 4 );

const float temp1 = cos( yy );


printf( "%x\n", *(unsigned int*)&temp1;

}

  1. 我在同一台iPhone4上运行了上面的代码,你猜怎么着?我得到了这个结果:bf7ff708。

  2. 我把那段代码放在游戏的更新循环中,每次循环得到的结果仍然是bf7ff708。

  3. 更有甚者,值0x404800d2是游戏的初始化值,所以每次游戏开始时,上述两行不同步的代码总是存在的。

3:问题:

所以,我决定忘记上述发生的事情,并暂时用我在dreamcode.net上找到的简单的Taylor实现代替sin、cos函数。不再出现不同步的问题了。

看起来,即使在同一台iPhone 4(OS版本为5)上,cos函数也不是确定性的。

我的问题是:我们有没有解释为什么cos函数在同一台手机上对相同的输入返回不同的结果?这里我有输入0x404800d2,但却得到了两个不同的输出:bf7ff708和bf7ff709。然而,我无法通过编码来重现bf7ff709的结果。

我想我需要操作系统数学函数的源代码(浮点版本),才能清楚地理解这个问题。上述我发现的问题足以作为一个错误报告吗?


1

这是Nguyen Truong Chung。

非常感谢您迄今为止的帮助。

我想报告一下,在我重写了所有超越函数,如cos、sin、sqrt、pow、atan2、atan、asin、acos等(尽可能多),不同步问题已经解决了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接