c++ - 什么可能导致确定性过程产生浮点错误

标签 c++ floating-point intel deterministic

已阅读此 question我有理由相信,使用具有相同输入(在相同硬件上,使用相同编译器编译)的浮点算术的给定过程应该是确定性的。我正在查看一个不正确的案例,并试图确定可能导致这种情况的原因。

我已经编译了一个可执行文件并且我正在为它提供完全相同的数据,在一台机器上运行(非多线程)但是我得到了大约 3.814697265625e-06 的错误,经过仔细的谷歌搜索我发现实际上是等于 1/4^9 = 1/2^18 = 1/262144。这非常接近 32 位 float 的精度级别(根据维基百科,大约为 7 位数)

我怀疑它与已应用于代码的优化有关。我正在使用英特尔 C++ 编译器并将浮点推测转换为快速而不是安全或严格。这会使浮点过程变得不确定吗?是否有其他优化等可能导致这种行为?

编辑:根据 Pax 的建议,我重新编译了代码,其中浮点推测变为安全,现在我得到了稳定的结果。这使我能够澄清这个问题 - 浮点推测实际上做了什么,当应用于完全相同的输入时,这如何导致相同的二进制文件(即一次编译,多次运行)生成不同的结果?

@Ben 我正在使用英特尔(R) C++ 11.0.061 [IA-32] 进行编译,并且在英特尔四核处理器上运行。

最佳答案

几乎在任何存在快速模式和安全模式的情况下,您都会发现某种权衡。否则一切都会以快速安全模式运行:-)。

而且,如果您使用相同的输入得到不同的结果,则您的过程不是确定性的,无论您多么相信它(尽管有经验证据)。 p>

我会说你的解释是最有可能的。将其置于安全模式并查看不确定性是否消失。这会告诉你肯定的。

至于是否有其他优化,如果您使用相同的编译器/链接器和这些工具的相同选项在相同的硬件上编译,它应该生成相同的代码。除了快速模式(或由于宇宙射线而导致内存中的比特腐烂,但这种可能性很小),我看不到任何其他可能性。

更新后:

Intel 有文档here这解释了他们在安全模式下不允许做的一些事情,包括但不限于:

  • 重新关联:(a+b)+c -> a+(b+c)
  • 零折叠:x + 0 -> x, x * 0 -> 0
  • 倒数乘法:a/b -> a*(1/b)

虽然您说这些操作是编译时定义的,但英特尔芯片非常聪明。他们可以重新排序指令以在多 CPU 设置中保持管道充满,因此,除非代码明确禁止这种行为,否则事情可能会在运行时(而不是编译时)发生变化以保持全速运行。

有关矢量化的链接文档的第 15 页(“问题:不同的结果在同一处理器上对相同数据重新运行相同的二进制文件”)对此进行了(简要)介绍。

我的建议是决定您是需要原始的咕噜声还是结果的完全可重复性,然后根据此选择模式。

关于c++ - 什么可能导致确定性过程产生浮点错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/968435/

相关文章:

c++ - Qt "no matching function for call"

c++ - 浮点 C++ 编译器选项 |防止 a/b -> a* (1/b)

c - 浮点运算的 Do-s 和 Don't-s?

linux - 使用intel_pstate时如何设置特定的CPU频率

c++ - Visual Studio 调试器 - 可视化英特尔四倍精度 (_Quad)

debugging - 英特尔AT&T汇编程序的分步执行?

c++ - 这是解决多个生产者的消费者生产者问题的正确方法吗?

c# - 使用Direct2d Effects时出现 "error LNK2001: unresolved external symbol _CLSID_D2D1Blend"如何解决?

c++ - 如何获得路径的正确大小写?

math - float 学有问题吗?