C++ 什么会导致确定性进程生成浮点错误
读过这篇文章后,我可以合理地确定,使用相同输入(在相同硬件上,使用相同编译器编译)的浮点算术的给定进程应该是确定性的。我在看一个不符合事实的案例,并试图确定是什么导致了这一结果 我已经编译了一个可执行文件,并且在一台机器上运行(非多线程),给它提供了完全相同的数据,但是我得到了大约3.814697265625e-06的错误,在仔细搜索之后,我发现它实际上等于1/4^9=1/2^18=1/262144。这非常接近32位浮点数的精度级别(根据wikipedia,大约7位) 我怀疑这与应用于代码的优化有关。我使用英特尔C++编译器,并将浮点猜测转换为快速而非安全或严格。这会使浮点进程不确定吗?是否存在其他可能导致这种行为的优化等 编辑:根据Pax的建议,我重新编译了代码,将浮点推测转换为安全的,现在我得到了稳定的结果。这使我能够澄清这个问题——浮点推测实际上做了什么?当应用于完全相同的输入时,它如何导致相同的二进制文件(即一次编译、多次运行)生成不同的结果C++ 什么会导致确定性进程生成浮点错误,c++,floating-point,intel,deterministic,C++,Floating Point,Intel,Deterministic,读过这篇文章后,我可以合理地确定,使用相同输入(在相同硬件上,使用相同编译器编译)的浮点算术的给定进程应该是确定性的。我在看一个不符合事实的案例,并试图确定是什么导致了这一结果 我已经编译了一个可执行文件,并且在一台机器上运行(非多线程),给它提供了完全相同的数据,但是我得到了大约3.814697265625e-06的错误,在仔细搜索之后,我发现它实际上等于1/4^9=1/2^18=1/262144。这非常接近32位浮点数的精度级别(根据wikipedia,大约7位) 我怀疑这与应用于代码的优化
本编译我用英特尔(R)C++ +0.061(IA32),我在英特尔四核处理器上运行。p> 在几乎任何有快速模式和安全模式的情况下,您都会找到某种权衡。否则一切都将在快速安全模式下运行:-) 而且,如果你用相同的输入得到不同的结果,你的过程是不确定的,不管你多么相信它(尽管有经验证据) 我认为你的解释是最有可能的。把它放在安全模式,看看非决定论是否消失了。这肯定会告诉你的 至于是否还有其他优化,如果您使用相同的编译器/链接器在相同的硬件上进行编译,并且这些工具具有相同的选项,那么它应该生成相同的代码。除了快速模式(或者由于宇宙射线导致内存中有点腐烂,但这不太可能),我看不到其他任何可能性 更新后: 英特尔有一份文件解释了他们在安全模式下不允许做的一些事情,包括但不限于:
- 重新关联:
(a+b)+c->a+(b+c)
- 零折叠:
x+0->x,x*0->0
- 倒数乘法:
a/b->a*(1/b)
我的建议是,决定您是否需要原始的咕噜声或结果的完全可复制性,然后在此基础上选择模式。如果您的程序是并行的,就像它可能在四核上运行一样,那么它很可能是不确定的 假设有4个处理器将浮点值添加到同一内存位置。那你可能会
(((InitialValue+P1fp)+P2fp)+P3fp)+P4fp
或
或任何其他可能的订单
见鬼,你甚至可能会
InitialValue+(P2fp+P3fp)+(P1fp+P4fp)
如果编译器足够好
不幸的是,浮点加法不是交换的或结合的。由于舍入、溢出和下溢,实数运算是,浮点运算不是
因此,并行FP计算通常是非确定性的。“经常”,因为程序看起来像
on each processor
while( there is work to do ) {
get work
calculate result
add to total
}
将是不确定的,因为每次所需的时间可能会有很大的差异-您无法预测操作的顺序。(如果线程相互作用,情况会更糟。)
但并不总是这样,因为有一些类型的并行编程是确定性的
当然,许多关心决定论的人所做的是用整数或定点来避免这个问题。我特别喜欢超级累加器,512、1024或2048位的数字,浮点数可以添加到这些数字中,而不会出现舍入错误
对于单线程应用程序:编译器可能会重新排列代码。不同的汇编可能给出不同的答案。但任何特定的二进制都应该是确定性的 除非。。。您使用的是动态语言。它执行优化,对FP计算进行重新排序,并随时间变化
或者除非。。。很长的路要走:安腾有一些特性,比如ALAT,这使得即使是单线程编码也无法确定。您不太可能受到这些影响。什么处理器和什么编译器。。如果你已经知道是哪个标志导致了它,为什么不检查一下编译器文档?@Tal-我很难从文档中找到任何东西(它只是说fast启用fps,而safe/strict禁用fps)。据我所知,fps允许对操作进行重新排序(ac+bc=>c*(a+b)),但这是编译时优化,生成的二进制文件应该仍然是确定性的,我真的很想知道为什么不是。当您在单个内核上运行可执行文件时,不确定性会消失吗?谢谢您的解释和参考资料。您链接的文档中确实指出,此问题(全局堆栈地址和对齐方式可能因当前运行的进程之外的事件而更改)已解决
on each processor
while( there is work to do ) {
get work
calculate result
add to total
}