是否有一种优先的方式来订购浮点操作数?

假设我有一个非常小的float a (例如a=0.5 ),它输入以下表达式:

 6000.f * a * a; 

操作数的顺序是否有所不同? 写作更好吗?

 6000.f * (a*a); 

甚至

 float result = a*a; result *= 6000.f; 

我已经检查了每个计算机科学家应该知道的关于浮点运算的经典之作但却找不到任何东西。

是否有一种在浮点运算中对操作数进行排序的最佳方法?

这实际上取决于价值观和目标。 例如,如果a非常小, a*a可能为零,而6000.0*a*a (这意味着(6000.0*a)*a )仍然可以是非零的。 为了避免上溢和下溢,一般规则是应用关联法则首先执行乘法,其中操作数的日志具有相反的符号,这意味着先行平方通常是最差的策略。 另一方面,出于性能原因,如果可以重用平方值,则首先平方可能是一个非常好的策略。 您可能会遇到另一个问题,如果您的数字永远不会非常接近于零或无穷大,那么正确性比溢出/下溢问题更重要:某些乘法可以保证具有精确答案,而其他则涉及舍入。 通常,您可以通过最小化发生的舍入步骤的数量来获得最准确的结果。

通常不是,不。

话虽这么说,如果你用大值进行多次操作,如果算法提供了一种明确的方法,那么根据它们的优先级和相关性 ,以避免溢出或减少精度错误的方式对它们进行排序可能是有意义的。 。 但是,这需要提前了解所涉及的值,而不仅仅是基于语法。

真正的最佳方式取决于目的。

首先,乘法比分裂快。

所以如果你要写a = a / 2; ,最好写a = a * 0.5f; 。 如果结果相同,你的编译器通常足够聪明,可以用常量乘法来代替除法,但当然它不会用变量做。

有时,您可以通过使用乘法替换除法来优化位,但可能存在精度问题。

其他一些操作可能更快但不太精确。 我们来举个例子吧。

 float f = (a * 100000) / (b * 10); float g = (a / b) * (100000 / 10); 

这些在数学上是等价的,但结果可能会略有不同。 第一个使用两个乘法和一个除法,第二个使用一个除法和一个乘法。 在这两种情况下都可能存在精度损失,这取决于a和b的大小,如果它们是小值则首先效果更好,如果它们是大值则第二个效果更好

然后……如果你有几个常数并且你想要速度,那么组合在一起。

 float a = 6.3f * a * 2.0f * 3.1f; 

写吧

 a = a * (6.3f * 2.0f * 3.1f); 

有些编译器优化得很好,有些编译器优化得更少,但在这两种情况下都没有将所有常量保持在一起的风险。

在我们这样说之后,我们应该谈谈处理器的工作原理。 甚至像英特尔这样的家族也会以不同的方式在世代之间运作! 有些编译器使用SSE指令,有些则不然。 有些处理器支持SSE2,有些SSE,有些仅支持MMX ……有些系统既没有FPU也没有! 每个系统都比其他系统做得更好一些,找到一个常见的东西很难。

你应该编写一个简洁易懂的代码,而不必担心这些不可预测的极低级优化。

如果你的表达看起来很复杂,做一些代数和\或去wolframalpha搜索引擎,并要求他为你优化:)

说,你真的不需要声明一个变量并反复替换它的内容,编译器通常可以在这种情况下优化得更少。

 a = 5 + b; a /= 2 * c; a += 2 - c; a *= 7; 

只是写你的表达避免这个混乱:)

 a = ((5 + b) / (2 * c) + 2 - c) * 7; 

关于您的具体示例, 6000.f * a * a ,只需在编写时编写,无需更改; 这很好。

确实存在一些算法来最小化一系列浮点运算中的累积误差。 其中一个是http://en.wikipedia.org/wiki/Kahan_summation_algorithm 。 其他存在用于其他操作: http : //www.cs.cmu.edu/~quake-papers/related/Priest.ps 。