C 是否有一种对浮点操作数进行排序的首选方法?

C 是否有一种对浮点操作数进行排序的首选方法?,c,floating-point,C,Floating Point,假设我有一个非常小的浮点a(例如a=0.5),它输入以下表达式: 6000.f * a * a; 操作数的顺序有什么区别吗?写作更好吗 6000.f * (a*a); 甚至 float result = a*a; result *= 6000.f; 我查过经典,但什么也没找到 在浮点运算中是否有一种对操作数进行排序的最佳方法?通常不是,不是 这就是说,如果您正在使用大值执行多个操作,那么根据它们的大小,以避免溢出或减少精度错误的方式对它们进行排序可能是有意义的,前提是算法提供了一种方法使其

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

6000.f * a * a;
操作数的顺序有什么区别吗?写作更好吗

6000.f * (a*a);
甚至

float result = a*a;
result *= 6000.f;
我查过经典,但什么也没找到

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


这就是说,如果您正在使用大值执行多个操作,那么根据它们的大小,以避免溢出或减少精度错误的方式对它们进行排序可能是有意义的,前提是算法提供了一种方法使其变得明显。然而,这需要事先了解所涉及的值,而不仅仅是基于语法。

最佳方法实际上取决于目的

首先,乘法比除法快

所以如果你必须写
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);
有些编译器优化得很好,有些编译器优化得更少,但在这两种情况下,将所有常量保持在一起是没有风险的

float a = 6.3f * a * 2.0f * 3.1f;
在我们说了这些之后,我们应该花几个小时讨论处理器是如何工作的。 即使是像英特尔这样的同一个家族,在不同的世代之间也会以不同的方式工作! 有些编译器使用SSE指令,有些则不使用。 有些处理器支持SSE2,有些支持SSE,有些只支持MMX。。。有些系统也没有FPU! 每个系统在某些计算方面都比其他系统做得好,很难找到一个共同点

您应该只编写一个可读的代码,简洁明了,而不必担心这些不可预知的非常低级的优化

如果您的表达式看起来很复杂,请执行一些代数运算,然后\或者转到wolframalpha搜索引擎,让他为您优化:)

也就是说,您实际上不需要声明一个变量并反复替换其内容,在这种情况下,编译器通常可以进行较少的优化

a = 5 + b;
a /= 2 * c;
a += 2 - c;
a *= 7;
写下你的表达,避免这种混乱:)


关于您的特定示例,
6000.f*a*a
,只需按编写的方式编写,无需更改;这一切都很好。

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

确实有一些算法可以最小化浮点操作序列中的累积误差。其中之一就是。其他操作也存在:。

编译器不能用除法代替乘法,除非结果完全相同,这是很少见的。只有二次幂才能真正发生。(不过,GCC上的ffast math将启用此类优化。)我刚才说的是常数除法,如果这样做没有风险,microsoft visual C会这样做。它取代了x/=2;x*=0.5f时;从所有角度来看,这都是安全的。正如我所说的,只有二次幂才能实现。因此,
x/3
不能更改为乘法(除非
x
是一个整数,当然……)是的,2的幂可以用浮点编码而不会丢失精度,其他类型的小数可能不会,但我想这取决于编译器,正如您在优化设置中指出的那样。Microsoft visual C有一个标志,询问您是否需要精确或快速浮点运算。出于与除法->乘法转换相同的原因,编译器无法将
6.3f*a*2.0f*3.1f
转换为
a*(6.3f*2.0f*3.1f)
,因为这不是一回事。一个编译器在没有程序员提示它使用选项的情况下进行这种转换,它不是一个优化编译器,它是一个错误的编译器。我同意程序员可以自己进行转换,这通常对他来说很好,但编译器不这样做的原因并不是缺乏优化。另外,在现代处理器上,你不知道操作的执行顺序,甚至不知道它们是否会串行执行。@MartyTPS:不相关。处理器计算相同的结果,就像它连续执行操作一样。注意,迪特里希。发布的问题是更改订单是否相关。它最终不一定在你的控制之下,所以是的,它是不相关的