我们需要通过在C / C ++中实现特定算法来解决的大多数科学计算问题要求精度远低于双精度。例如,1e-6
,1e-7
准确度涵盖了ODE求解器或数值积分案例的99%
。即使在我们确实需要更高精度的极少数情况下,通常数值方法本身也会在我们梦想达到接近双精度的精度之前失败。示例:即使在求解标准nostiff常微分方程时,由于舍入误差,我们也不能期望简单Runge-Kutta方法的1e-16精度。在这种情况下,双精度要求类似于要求更好地逼近错误答案。
然后,在大多数情况下,积极的浮点优化似乎是一个双赢的局面,因为它使您的代码更快(更快!)并且它不会影响您的特定问题的目标准确性。也就是说,确保特定的实现/代码对fp优化是稳定的似乎非常困难。古典(有点令人不安)的例子:GNL,GNU科学图书馆,不仅是市场上的标准数字图书馆,而且它是一个写得很好的图书馆(我无法想象自己做得更好)。但是,GSL对fp优化不稳定。实际上,例如,如果您使用intel编译器编译GSL,那么除非您打开关闭fp优化的-fp-model strict
标志,否则其内部测试将失败。
因此,我的问题是:是否存在编写代码的一般准则,这些代码对于积极的浮点优化是稳定的。这些指南语言(编译器)是否具体。如果是这样,那么C / C ++(gcc / icc)最佳实践是什么?
注1:这个问题不是询问gcc / icc中的fp优化标志是什么。
注2:这个问题不是询问有关C / C ++优化的一般指导原则(比如不要将虚函数用于经常调用的小函数)。
注3:这个问题不是要求大多数标准fp优化列表(如x / x - > 1)。
注4:我坚信这不是类似于传统的“最酷的服务器名称”的主观/偏离主题的问题。如果您不同意(因为我没有提供具体的示例/代码/问题),请将其标记为社区维基。我对答案比对获得一些状态点更感兴趣(不是它们不重要 - 你明白了!)。
答案 0 :(得分:13)
编译器制造商证明了-ffast-math
类型的优化,并断言这些优化对numerically stable algorithms的影响很小。
因此,如果您想编写对这些优化具有鲁棒性的代码,那么充分条件就是只编写数值稳定的代码。
现在你的问题可能是,“我如何编写数值稳定的代码?”。这是你的问题可能有点广泛的地方:有完整的书籍专门讨论这个主题。我已经链接到的维基百科页面有一个很好的例子,here是另一个很好的例子。我不能特别推荐一本书,这不是我的专业领域。
注1:数值稳定性的可取性超出了编译器优化。如果您有选择,即使您不打算使用-ffast-math
样式优化,也要编写数值稳定的代码。即使使用严格的IEEE 754浮点语义进行编译,数值不稳定的代码也可能提供错误的结果。
注意2:使用-ffast-math
样式标志编译时,不能指望外部库可以工作。这些由浮点专家编写的库可能需要使用IEEE 754计算的属性进行微妙的技巧。这种技巧可能会被-ffast-math
优化打破,但它们提高了性能,甚至比你想象的还要好。对于浮点计算,具有领域知识的专家每次都会击败编译器。许多例子中的例子是CRlibm中的三重双重实现。如果未使用严格的IEEE 754语义编译,则此代码会中断。编译器优化中断的另一个更基本的算法是Kahan summation:当使用不安全的优化进行编译时,c = (t - sum) - y
优化为c = 0
。当然,这完全违背了算法的目的。