使用pow(x,2)代替x*x,x double是否有任何优势?

Ale*_*son 44 c++ math floating-point

使用此代码有什么好处

double x;
double square = pow(x,2);
Run Code Online (Sandbox Code Playgroud)

而不是这个?

double x;
double square = x*x;
Run Code Online (Sandbox Code Playgroud)

我更喜欢x*x并且查看我的实现(Microsoft)我发现pow没有优势,因为x*x比特定方形情况下的pow更简单.

有什么特别的情况,战俘优越吗?

Aln*_*tak 54

FWIW,MacOS X 10.6上的gcc-4.2和-O3编译器标志,

x = x * x;
Run Code Online (Sandbox Code Playgroud)

y = pow(y, 2);
Run Code Online (Sandbox Code Playgroud)

导致相同的汇编代码:

#include <cmath>

void test(double& x, double& y) {
        x = x * x;
        y = pow(y, 2);
}
Run Code Online (Sandbox Code Playgroud)

装配到:

    pushq   %rbp
    movq    %rsp, %rbp
    movsd   (%rdi), %xmm0
    mulsd   %xmm0, %xmm0
    movsd   %xmm0, (%rdi)
    movsd   (%rsi), %xmm0
    mulsd   %xmm0, %xmm0
    movsd   %xmm0, (%rsi)
    leave
    ret
Run Code Online (Sandbox Code Playgroud)

因此,只要你使用一个像样的编译器,编写对你的应用程序更有意义的,但考虑到pow(x, 2)它永远不会比简单的乘法优化.

  • 顺便说一句,`-O6`与`-O3`相同.g ++不会达到11. (30认同)
  • 显然它甚至没有达到四个:) (7认同)
  • 对于它的价值,**微软的编译器绝对不会为`pow(x,2)`和`x*x`生成相同的代码**.它总是调用`pow`函数,导致一系列分支和大量慢代码.如果你的意思是`x*x`,那么把它写成`x*x`.避免使用过于通用的功能只是为了聪明.我们在这里讨论的是*大规模*速度差异.因此,你的回答很容易让人误解.如果您要分析反汇编,至少需要反汇编相关编译器生成的代码.特别是因为提问者确实懒得提起它. (4认同)
  • @Alnitak +1我非常喜欢你看待汇编代码的方法. (2认同)
  • 我一直对通过程序集来证明程序行为持谨慎态度。工具链(以及最重要的标准)不保证程序集输出,因此它可以随时更改。除了您现在碰巧看到的特定两个可执行文件之外,这几乎不是证明程序任何内容的万无一失的方法。 (2认同)

Seb*_*ach 24

std :: pow更具表现力,如果你的意思是x²,x x更具表现力,如果你的意思是x x,特别是如果你只是编写一篇例如科学论文,读者应该能够理解你的实现与论文.x*x /x²的区别可能很微妙,但我认为如果你通常使用命名函数,它会增加代码的可靠性和可读性.

在现代编译器上,例如g ++ 4.x,std :: pow(x,2)将被内联,如果它甚至不是编译器内置的,并且强度减少到x*x.如果不是默认情况下并且您不关心IEEE浮点类型一致性,请查看编译器手册以获得快速数学开关(g ++ == -ffast-math).


旁注:有人提到包括math.h会增加程序大小.我的回答是:

在C++中,你#include <cmath>,不是MATH.H.此外,如果你的编译器不是很老,它只会增加你的程序大小(在一般情况下),如果你的std :: pow实现只是内联到相应的x87指令,那么现代的g ++用x*x强度降低x²,然后没有相应的尺寸增加.此外,程序大小永远不应该决定你的代码是多么富有表现力.

cmath比math.h的另一个优点是,使用cmath,你得到每个浮点类型的std :: pow重载,而使用math.h你可以在全局命名空间中得到pow,powf等,所以cmath增加了适应性代码,特别是在编写模板时.

作为一般规则:首选表达式和清晰的代码,而不是基于可疑的性能和二进制大小的合理代码.

另见Knuth:

"我们应该忘记小的效率,大约97%的时间说:过早的优化是所有邪恶的根源"

和杰克逊:

程序优化的第一条规则:不要这样做.程序优化的第二条规则(仅限专家!):不要这样做.

  • 各种各样的东西+1,特别是你用正确的上标-2字符打扰:) (2认同)

Dav*_*nan 13

不仅x*x更清楚,它肯定至少会如此快pow(x,2).

  • 出于性能原因,我们甚至不得不将`pow(x,-2.5)`替换为`1.0 /(x*x*sqrt(x))`.在我们进行计算的特定机器上获得了令人难以置信的加速.(编译器是gcc 4.4,IIRC) (8认同)
  • @David确实编译_can_ optimize`pow(x,2)` - 看我的答案. (2认同)
  • @Sven:你如何定义"几乎每个编译器"?如果你不关心IEEE一致性,你可以使用g ++ -fast-math,因此它会将pow(x,2)强度降低到x*x,所以没有区别. (2认同)

Dav*_*men 11

这个问题触及了大多数关于科学编程的C和C++实现的关键弱点之一.从Fortran转到C大约二十年,之后转到C++,这仍然是那些偶尔让我怀疑这种转换是否是一件好事的痛处之一.

问题简而言之:

  • 最简单的实施方法powType pow(Type x; Type y) {return exp(y*log(x));}
  • 大多数C和C++编译器都采用了简单的方法.
  • 有些人可能"做正确的事情",但只有在高优化水平.
  • 相比之下x*x,简单的方法pow(x,2)是计算上非常昂贵并且失去精度.

与针对科学编程的语言相比:

  • 你不写pow(x,y).这些语言具有内置的取幂运算符.C和C++坚决拒绝实现取幂运算符,这使得许多科学程序员的血液沸腾了.对于一些顽固的Fortran程序员来说,仅此一点就是永远不会切换到C的理由.
  • Fortran(和其他语言)需要为所有小整数幂做"正确的事",其中small是-12到12之间的任何整数.(如果编译器不能'做正确的事',则编译器不符合要求.)此外,他们需要优化关闭.
  • 许多Fortran编译器也知道如何在不诉诸简单方法的情况下提取一些合理的根.

依赖高优化级别来"做正确的事"是一个问题.我曾为多家禁止在安全关键软件中使用优化的组织工作过.在这里损失1000万美元,其中有1亿美元,记忆可能会很长(数十年之久),这都归功于一些优化编译器中的错误.

恕我直言,你应该从来没有使用pow(x,2)C或C++.在这个意见中,我并不孤单.pow(x,2)通常使用的程序员在代码审查期间会大量使用.

  • 没有专门的战俘操作员(这并不难以承担)不仅仅是操作员对更复杂类型(作为矢量和矩阵)的补偿.远离优化是非常适得其反的(并且肯定不会在科学应用中完成).如果您的代码在优化时不起作用,那就是错误的代码.你必须依赖某些东西(比如一个正常工作的编译器).但我同意不依赖于高级优化功能而不是所有编译器都支持(就像我提到的那样提到的pow优化). (4认同)

Sha*_*our 10

在C++ 11中,有一种情况是使用x * xover 有一个优点std::pow(x,2),那就是你需要在constexpr中使用它的情况:

constexpr double  mySqr( double x )
{
      return x * x ;
}
Run Code Online (Sandbox Code Playgroud)

我们可以看到std :: pow没有标记为constexpr,因此它在constexpr函数中无法使用.

否则从性能角度看,将以下代码放入godbolt会显示以下函数:

#include <cmath>

double  mySqr( double x )
{
      return x * x ;
}

double  mySqr2( double x )
{
      return std::pow( x, 2.0 );
}
Run Code Online (Sandbox Code Playgroud)

生成相同的汇编:

mySqr(double):
    mulsd   %xmm0, %xmm0    # x, D.4289
    ret
mySqr2(double):
    mulsd   %xmm0, %xmm0    # x, D.4292
    ret
Run Code Online (Sandbox Code Playgroud)

我们应该期待任何现代编译器的类似结果.

值得注意的是,目前gcc认为pow是一个constexpr,也包括在这里,但这是一个不合格的扩展,不应该依赖,并可能会在以后的版本中更改gcc.


Ash*_*ain 7

x * x将始终编译为简单的乘法. pow(x, 2)很可能,但绝不保证,优化到相同.如果没有进行优化,则可能会使用缓慢的通用升频数学例程.因此,如果您关注性能,那么您应该始终青睐x * x.


Her*_*ess 6

恕我直言:

  • 代码可读性
  • 代码健壮性 - 将更容易更改pow(x, 6),可能实现特定处理器的某些浮点机制等.
  • 性能 - 如果有更智能,更快速的方法来计算(使用汇编程序或某种特殊技巧),pow将会这样做.你不会.. :)

干杯

  • 关于pow(x,6)的论证是不现实的.编译器也可以优化x*x! (6认同)
  • 我也对可读性提出质疑.在任何大型方程中,战俘都是可怕的.如果认为操作员超载对可读性有益,则应认为pow有害. (2认同)
  • @David Heffernan:恕我直言,`pow(x,2)`并不比`x*x`更可怕.至少它给了一些分组; 如果你有可读性问题,你应该分开计算,const来到win:`const float x = pow(x,2)/ sqrt(y)` - >`const float num = pow(x,2) ,denom = sqrt(y),x = num/denom;`.当然最好是`x²`,但这是C++. (2认同)