Delphi中的浮点运算是否具有确定性?

8
Delphi中的浮点数运算是否是确定性的?
换句话说,当我在使用Delphi Win32编译器、Win64编译器、OS X编译器、iOS编译器或者Android编译器对同一程序进行相同的浮点数操作时,是否会得到相同的结果?
这是至关重要的问题,因为我正在实现游戏引擎的多人支持功能,我担心客户端的预测结果经常与服务器端的权威决策不同。
如果出现这种情况,客户端将出现“延迟”或“卡顿”。
由于我实际上没有能力在任何类似于“控制条件”的情况下测试不同平台、不同编译器编译的几十个不同设备,所以我想请Delphi开发者回答这个问题,看看有没有人对编译器底层的浮点数确定性有内部理解。

2
“浮点运算”是什么意思?如果您包括像 sincos 这样的超越函数,我认为您不能期望得到相同的结果。如果您将问题限制在对 DoubleSingle 类型进行的四则运算上,也许您可以获得相同的结果,但我不喜欢依赖它,因为浮点运算模式的改变甚至可以在单个平台上改变浮点运算的结果。 - kludg
2
我不明白为什么这对你是个问题。浮点数据类型和操作总是近似值。 - David Heffernan
@DavidHeffernan:错误。 - tmyklebu
1
@DavidHeffernan:这对于发帖人的应用程序是有影响的;他想知道是否可以信任客户端浮点数运算来准确复制一系列计算。无论两个状态机在 Delphi 级别上是否相同,不同平台处理浮点数的方式可能会导致它们分歧。 - tmyklebu
显示剩余14条评论
3个回答

7
我认为没有简单的答案。类似的任务在此处进行讨论:这里。 一般来说,有两种浮点数表示标准:IEEE 754-1985和IEEE 754-2008。所有现代CPU(实际上包括旧的CPU)都遵循这些标准,并保证以下几点:
  • 同一类型的二进制浮点数表示值相等
  • 某些操作的结果是相等的(只有基本操作!),但仅在编译器使用相同类型的指令时才能保证,我不确定这是正确的。

但是,如果您使用一些扩展操作,例如平方根,甚至对于不同型号的台式机CPU,结果也可能会有所不同。您可以阅读此文章获取更多详细信息:http://randomascii.wordpress.com/2013/07/16/floating-point-determinism/

P.S. 正如tmyklebu所提到的,平方根也由IEEE 754定义,因此可以保证Add、Subtract、Multiply、Divide和Square root的相同输入具有相同的结果。其他一些操作也由IEEE定义,但要获取所有详细信息,最好阅读IEEE。


这正是我所推测的(并想要确认的)。令人恼火的是,物理引擎使用了几乎所有复杂的浮点运算(包括正弦、余弦,我正在使用正切来确定将精灵渲染到哪个3D层上,以及平方根)。为了使游戏在客户端看起来流畅,我可能需要依靠基于后备缓冲区的“平滑处理”,而实际上它会不断地被权威服务器计算所“调整”(即使只有千分之一的单位)。虽然不够优雅,但现在看来似乎是必要的。 - LaKraven
3
"sqrt" 没问题。但是三角函数和指数函数就要小心使用了。在 x86-32 上,一般使用 FPU 而不是 SSE 指令,这真的很可怕。为了从中获得性能提升,编译器需要生成舍入方式不同于程序指定的代码。 - tmyklebu
你确定IEEE754规定了sqrt吗? - David Heffernan
1
@DavidHeffernan 是的。6个基本操作+,-,*,/,sqrt,fma必须正确舍入。 - Pascal Cuoq
1
重申一遍,sqrt 完全由 IEEE 754 标准定义,并且不会因为不同型号的台式电脑 CPU 而“有所不同”。 - Pascal Cuoq
1
@DavidHeffernan:是的;请看第5节开头段落。 - tmyklebu

2

暂时不考虑浮点计算的标准,考虑一下32位和64位编译器编译使用旧的FPU和较新的SSE指令。我会发现很难相信每个计算结果在不同的硬件实现上总是完全相同的。最好走安全路线,假设如果它们之间的差异很小,就将其评估为相等。


这是我所期望的“最佳情况”。在一个理想的世界里,数值应该是精确的,但由于我们知道现实世界并不完美,我只希望我正在准备的多人游戏测试的结果能够表明数值之间的差异非常微小,以至于呈现出来的结果仍然相同。 - LaKraven
标准的存在是为了确保结果的一致性。 - David Heffernan

1

根据经验,我可以告诉您结果是不同的:32位默认使用扩展精度,而64位默认使用双精度。

考虑以下语句:

x,y,z: double; x := y * z;

在Win32中,这将执行为“x := double(Extended(y)*Extended(z)); 在Win64中,这将执行为“x := double(double(y)*double(z));

您会花很多精力确保您使用相同的精度和模式。然而,每当您调用第三方库时,您需要考虑它们可能会在内部更改这些标志。


我很幸运,因为这个游戏引擎在内部完全使用Double值,而FMX仅在渲染精灵时使用Single(事实上,在Single值上不执行任何复杂的数学计算)。 谢谢你提供信息 :) - LaKraven
@La 这个答案是关于 x87 中间值的,这些值是您无法控制的。 - David Heffernan

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