我正在做一个需要大量数学计算的项目。打开新的测试机后,我注意到很多测试都失败了。但同样重要的是要注意,测试在我的开发机器以及其他开发人员的某些机器上也失败了。经过跟踪值并与旧机器的值进行比较后,我发现一些功能(此时我只发现cosine)math.h 有时会返回略有不同的值(例如:40965.8966304650828827e-01 和 40965.8966304650828816e-01,-3.3088623618085204e-08 和-3.3088623618085197e-08).
New CPU:英特尔至强金牌 6230R(Intel64 系列 6 型号 85 步进 7)
Old CPU:确切型号未知(Intel64 Family 6 Model 42 Stepping 7)
My CPU:英特尔酷睿 i7-4790K
测试结果不取决于Windows版本 (7 and 10进行了测试)。
我尝试使用与标准库静态链接的二进制文件进行测试,以排除为不同进程和 Windows 版本加载不同库的情况,但所有结果都是相同的。
使用 /fp:precise 编译的项目,切换到 /fp:strict 没有任何改变。
使用 Visual Studio 15 中的 MSVC:19.00.24215.1(适用于 x64)。
如何使计算完全可重现?
由于您使用的是 Windows,我很确定不同的结果是因为 UCRT 在运行时检测 FMA3(融合乘加)指令是否可用于 CPU,如果是,则在余弦等超越函数中使用它们。这给出了结果略有不同 https://stackoverflow.com/a/29086451/3740047。解决办法就是拨打电话set_FMA3_enable(0);
在你的一开始main()
or WinMain()
功能,如描述的here https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/get-fma3-enable-set-fma3-enable?view=msvc-170.
如果您希望在不同操作系统之间也具有可重复性,事情就会变得更困难甚至不可能。参见例如this https://randomascii.wordpress.com/2013/07/16/floating-point-determinism/博客文章。
作为对“你应该使用一些宽容”的评论的回应,我不同意将此作为一般性声明。当然,有许多应用程序都需要采用这种方法。但我确实认为它can是获得完全相同的浮点结果的合理要求对于某些应用,至少在使用同一操作系统时(在本例中为 Windows)。事实上,我们也遇到了同样的问题set_FMA3_enable
不久以前。我是一名交通模拟软件开发人员,诸如 10^-16 之类的微小差异经常会累积并最终导致完全不同的模拟结果。当然,人们应该使用不同的种子运行多次模拟并对所有种子进行平均,从而使不同的行为与最终结果无关。但是:有时客户会在特定种子的特定模拟时刻遇到问题(例如应用程序崩溃或实体的不正确行为),并且由于 CPU 不同而无法在我们的开发人员计算机上重现它,这使得更难诊断并解决问题。此外,如果测试系统由较旧和较新的 CPU 混合组成,并且测试用例未绑定到特定资源,则意味着有时测试可能会毫无理由地出现偏差(片状测试)。这当然是不希望的。要求精确的再现性也使编写测试变得更加容易,因为您不需要启发式阈值(例如样本数量的公差或一些猜测值)。此外,我们的客户希望特定版本的程序的结果保持稳定,因为他们将其流量网络校准(或多或少......)为真实数据。这有点值得怀疑,因为(再次)人们实际上应该关注平均值,但现实中的天真期望通常会获胜。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)