使用pow(x,2)代替x*x有什么好处吗?

时间:2022-06-01 11:48:14

is there any advantage to using this code

使用这段代码有什么好处吗?

double x;
double square = pow(x,2);

instead of this?

而不是这个吗?

double x;
double square = x*x;

I prefer x*x and looking at my implementation (Microsoft) I find no advantages in pow because x*x is simpler than pow for the particular square case.

我更喜欢x*x,看看我的实现(Microsoft),我发现pow中没有什么优点,因为x*x比pow更简单。

Is there any particular case where pow is superior?

有什么特殊情况下pow是上级的吗?

8 个解决方案

#1


52  

FWIW, with gcc-4.2 on MacOS X 10.6 and -O3 compiler flags,

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

x = x * x;

and

y = pow(y, 2);

result in the same assembly code:

结果相同的汇编代码:

#include <cmath>

void test(double& x, double& y) {
        x = x * x;
        y = pow(y, 2);
}

Assembles to:

组装:

    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

So as long as you're using a decent compiler, write whichever makes more sense to your application, but consider that pow(x, 2) can never be more optimal than the plain multiplication.

因此,只要你使用的是一个像样的编译器,你可以把它写在你的应用程序上,但是考虑一下,pow(x, 2)永远不会比普通的乘法更理想。

#2


21  

std::pow is more expressive if you mean x², xx is more expressive if you mean xx, especially if you are just coding down e.g. a scientific paper and readers should be able to understand your implementation vs. the paper. The difference is subtle maybe for x*x/x², but I think if you use named functions in general, it increases code-expessiveness and readability.

std::战俘更富有表现力的如果你的意思是x²,xx更富有表现力的如果你的意思是xx,特别是如果你只是编码如科学论文,读者应该能够理解您的实现与论文。对于x*x/x,这种差异是很微妙的,但是我认为,如果您通常使用命名函数,它会增加代码的扩展和可读性。

On modern compilers, like e.g. g++ 4.x, std::pow(x,2) will be inlined, if it is not even a compiler-builtin, and strength-reduced to x*x. If not by default and you don't care about IEEE floating type conformance, check your compiler's manual for a fast math switch (g++ == -ffast-math).

在现代编译器上,例如:g++ 4。std::pow(x,2)是内联的,如果它不是编译器构建的,并且强度降低到x*x。如果不是默认情况下,并且您不关心IEEE浮动类型一致性,请检查您的编译器的快速数学转换手册(g++ == -ffast-math)。


Sidenote: It has been mentioned that including math.h increases program size. My answer was:

旁注:包括数学在内。h增加程序的大小。我的回答是:

In C++, you #include <cmath>, not math.h. Also, if your compiler is not stone-old, it will increase your programs size only by what you are using (in the general case), and if your implentation of std::pow just inlines to corresponding x87 instructions, and a modern g++ will strength-reduce x² with x*x, then there is no relevant size-increase. Also, program size should never, ever dictate how expressive you make your code is.

在c++中,包括 ,而不是math.h。也,如果你的编译器不是stone-old,它会增加你的程序大小只有你使用(在一般情况下),如果你的implentation std::战俘只是内联相应x87指令,和现代g++ strength-reduce x²x * x,那么就没有相关的大小增加。而且,程序的大小永远不能决定代码的表达方式。

A further advantage of cmath over math.h is that with cmath, you get a std::pow overload for each floating point type, whereas with math.h you get pow, powf, etc. in the global namespace, so cmath increase adaptibility of code, especially when writing templates.

cmath优于数学的另一个优势。h是用cmath计算的,你得到std::对于每个浮点类型的pow重载,而数学。在全局命名空间中,您可以获得pow、powf等,因此cmath可以增强代码的适应性,尤其是在编写模板时。

As a general rule: Prefer expressive and clear code over dubiously grounded performance and binary size reasoned code.

一般的规则是:更喜欢有表现力和清晰的代码,而不是基于性能和二进制大小的代码。

See also Knuth:

参见Knuth:

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil"

“我们应该忘记小的效率,大约97%的时间:过早优化是万恶之源”

and Jackson:

和杰克逊:

The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet.

程序优化的第一条规则:不要这样做。程序优化的第二条规则(只适用于专家!):不要这样做。

#3


12  

Not only is x*x clearer it certainly will be at least as fast as pow(x,2).

不仅x*x更清楚,它至少会和pow(x,2)一样快。

#4


9  

This question touches on one of the key weaknesses of most implementations of C and C++ regarding scientific programming. After having switched from Fortran to C about twenty years, and later to C++, this remains one of those sore spots that occasionally makes me wonder whether that switch was a good thing to do.

这个问题触及了C和c++在科学编程上大多数实现的一个关键弱点。从Fortran转到C,大约有20年,后来又转到c++,这仍然是一个很痛的地方,偶尔会让我怀疑这个开关是不是一件好事。

The problem in a nutshell:

简单地说:

  • The easiest way to implement pow is Type pow(Type x; Type y) {return exp(y*log(x));}
  • 实现pow的最简单方法是类型pow(类型x;y型){返回exp(y *日志(x));}
  • Most C and C++ compilers take the easy way out.
  • 大多数C和c++编译器都采用了简单的方法。
  • Some might 'do the right thing', but only at high optimization levels.
  • 一些人可能会“做正确的事”,但只是在高优化水平。
  • Compared to x*x, the easy way out with pow(x,2) is extremely expensive computationally and loses precision.
  • 与x*x相比,使用pow(x,2)的简单方法是非常昂贵的计算和丢失精度。

Compare to languages aimed at scientific programming:

与旨在科学规划的语言相比:

  • You don't write pow(x,y). These languages have a built-in exponentiation operator. That C and C++ have steadfastly refused to implement an exponentiation operator makes the blood of many scientific programmers programmers boil. To some diehard Fortran programmers, this alone is reason to never switch to C.
  • 你不写战俘(x,y)。这些语言有一个内置的指数运算符。C和c++坚决拒绝执行幂指数运算符,这使得许多科学程序员的血液沸腾。对于一些顽固的Fortran程序员来说,这仅仅是他们永远不会切换到C语言的原因。
  • Fortran (and other languages) are required to 'do the right thing' for all small integer powers, where small is any integer between -12 and 12. (The compiler is non-compliant if it can't 'do the right thing'.) Moreover, they are required to do so with optimization off.
  • Fortran(和其他语言)被要求对所有的小的整数幂“做正确的事情”,其中小的是-12和12之间的任何整数。(如果编译器不能“做正确的事情”,它就是不兼容的。)此外,他们还需要进行优化。
  • Many Fortran compilers also know how to extract some rational roots without resorting to the easy way out.
  • 许多Fortran编译器也知道如何在不使用简单方法的情况下提取一些合理的根。

There is an issue with relying on high optimization levels to 'do the right thing'. I have worked for multiple organizations that have banned use of optimization in safety critical software. Memories can be very long (multiple decades long) after losing 10 million dollars here, 100 million there, all due to bugs in some optimizing compiler.

依赖高优化级别来“做正确的事情”是有问题的。我曾为多个组织工作过,这些组织已经禁止在安全关键软件中使用优化。记忆可能很长(几十年),在这里损失了1000万美元,那里有1亿美元,所有这些都是由于一些优化编译器中的错误造成的。

IMHO, one should never use pow(x,2) in C or C++. I'm not alone in this opinion. Programmers who do use pow(x,2) typically get reamed big time during code reviews.

IMHO不应该在C或c++中使用pow(x,2)。在这个观点上,我并不孤单。使用pow(x,2)的程序员通常会在代码审查期间获得很大的重用。

#5


8  

In C++11 there is one case where there is an advantage to using x * x over std::pow(x,2) and that case is where you need to use it in a constexpr:

在c++ 11中,有一种情况是使用x * x优于std::pow(x,2),这种情况下,您需要在constexpr中使用它:

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

As we can see std::pow is not marked constexpr and so it is unusable in a constexpr function.

我们可以看到std::pow没有被标记为constexpr,因此它在一个constexpr函数中是不可用的。

Otherwise from a performance perspective putting the following code into godbolt shows these functions:

否则,从性能的角度来看,将以下代码放入到godbolt中就会显示这些函数:

#include <cmath>

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

double  mySqr2( double x )
{
      return std::pow( x, 2.0 );
}

generate identical assembly:

产生相同的装配:

mySqr(double):
    mulsd   %xmm0, %xmm0    # x, D.4289
    ret
mySqr2(double):
    mulsd   %xmm0, %xmm0    # x, D.4292
    ret

and we should expect similar results from any modern compiler.

我们应该期望任何现代编译器都有类似的结果。

Worth noting that currently gcc considers pow a constexpr, also covered here but this is a non-conforming extension and should not be relied on and will probably change in later releases of gcc.

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

#6


7  

x * x will always compile to a simple multiplication. pow(x, 2) is likely to, but by no means guaranteed, to be optimised to the same. If it's not optimised, it's likely using a slow general raise-to-power math routine. So if performance is your concern, you should always favour x * x.

x * x总是会编译成一个简单的乘法。pow(x, 2)很可能,但不能保证,优化到相同。如果它没有被优化,它很可能会使用一个缓慢的通用的raisep -power的数学例程。因此,如果性能是您的关注点,您应该始终支持x * x。

#7


6  

IMHO:

恕我直言:

  • Code readability
  • 代码的可读性
  • Code robustness - will be easier to change to pow(x, 6), maybe some floating point mechanism for a specific processor is implemented, etc.
  • 代码的健壮性——将更容易更改为pow(x, 6),可能会实现某些特定处理器的浮点机制,等等。
  • Performance - if there is a smarter and faster way to calculate this (using assembler or some kind of special trick), pow will do it. you won't.. :)
  • 性能——如果有更智能、更快的计算方法(使用汇编器或某种特殊技巧),pow将会这样做。你不会. .:)

Cheers

干杯

#8


1  

I would probably choose std::pow(x, 2) because it could make my code refactoring easier. And it would make no difference whatsoever once the code is optimized.

我可能会选择std::pow(x, 2),因为它可以使我的代码重构更容易。一旦代码被优化,它就不会产生任何影响。

Now, the two approaches are not identical. This is my test code:

现在,这两种方法并不相同。这是我的测试代码:

#include<cmath>

double square_explicit(double x) {
  asm("### Square Explicit");
  return x * x;
}

double square_library(double x) {
  asm("### Square Library");  
  return std::pow(x, 2);
}

The asm("text"); call simply writes comments to the assembly output, which I produce using (GCC 4.8.1 on OS X 10.7.4):

asm(“文本”);调用简单地将注释写入到程序集输出,我使用(GCC 4.8.1在OS X 10.7.4上):

g++ example.cpp -c -S -std=c++11 -O[0, 1, 2, or 3]

You don't need -std=c++11, I just always use it.

你不需要-std=c++11,我只是一直用它。

First: when debugging (with zero optimization), the assembly produced is different; this is the relevant portion:

首先:当调试(零优化)时,产生的装配是不同的;这是相关部分:

# 4 "square.cpp" 1
    ### Square Explicit
# 0 "" 2
    movq    -8(%rbp), %rax
    movd    %rax, %xmm1
    mulsd   -8(%rbp), %xmm1
    movd    %xmm1, %rax
    movd    %rax, %xmm0
    popq    %rbp
LCFI2:
    ret
LFE236:
    .section __TEXT,__textcoal_nt,coalesced,pure_instructions
    .globl __ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_
    .weak_definition __ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_
__ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_:
LFB238:
    pushq   %rbp
LCFI3:
    movq    %rsp, %rbp
LCFI4:
    subq    $16, %rsp
    movsd   %xmm0, -8(%rbp)
    movl    %edi, -12(%rbp)
    cvtsi2sd    -12(%rbp), %xmm2
    movd    %xmm2, %rax
    movq    -8(%rbp), %rdx
    movd    %rax, %xmm1
    movd    %rdx, %xmm0
    call    _pow
    movd    %xmm0, %rax
    movd    %rax, %xmm0
    leave
LCFI5:
    ret
LFE238:
    .text
    .globl __Z14square_libraryd
__Z14square_libraryd:
LFB237:
    pushq   %rbp
LCFI6:
    movq    %rsp, %rbp
LCFI7:
    subq    $16, %rsp
    movsd   %xmm0, -8(%rbp)
# 9 "square.cpp" 1
    ### Square Library
# 0 "" 2
    movq    -8(%rbp), %rax
    movl    $2, %edi
    movd    %rax, %xmm0
    call    __ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_
    movd    %xmm0, %rax
    movd    %rax, %xmm0
    leave
LCFI8:
    ret

But when you produce the optimized code (even at the lowest optimization level for GCC, meaning -O1) the code is just identical:

但是,当您生成优化的代码时(即使在GCC的最低优化级别上,这意味着-O1)代码也是一样的:

# 4 "square.cpp" 1
    ### Square Explicit
# 0 "" 2
    mulsd   %xmm0, %xmm0
    ret
LFE236:
    .globl __Z14square_libraryd
__Z14square_libraryd:
LFB237:
# 9 "square.cpp" 1
    ### Square Library
# 0 "" 2
    mulsd   %xmm0, %xmm0
    ret

So, it really makes no difference unless you care about the speed of unoptimized code.

所以,除非你关心的是未优化代码的速度,否则这没什么区别。

Like I said: it seems to me that std::pow(x, 2) more clearly conveys your intentions, but that is a matter of preference, not performance.

就像我说的:在我看来std::pow(x, 2)更清楚地表达了你的意图,但那只是一个偏好问题,而不是表现。

And the optimization seems to hold even for more complex expressions. Take, for instance:

而且,优化似乎也适用于更复杂的表达式。,例如:

double explicit_harder(double x) {
  asm("### Explicit, harder");
  return x * x - std::sin(x) * std::sin(x) / (1 - std::tan(x) * std::tan(x));
}

double implicit_harder(double x) {
  asm("### Library, harder");
  return std::pow(x, 2) - std::pow(std::sin(x), 2) / (1 - std::pow(std::tan(x), 2));
}

Again, with -O1 (the lowest optimization), the assembly is identical yet again:

再一次,以-O1(最低的优化),组装再次相同:

# 14 "square.cpp" 1
    ### Explicit, harder
# 0 "" 2
    call    _sin
    movd    %xmm0, %rbp
    movd    %rbx, %xmm0
    call    _tan
    movd    %rbx, %xmm3
    mulsd   %xmm3, %xmm3
    movd    %rbp, %xmm1
    mulsd   %xmm1, %xmm1
    mulsd   %xmm0, %xmm0
    movsd   LC0(%rip), %xmm2
    subsd   %xmm0, %xmm2
    divsd   %xmm2, %xmm1
    subsd   %xmm1, %xmm3
    movapd  %xmm3, %xmm0
    addq    $8, %rsp
LCFI3:
    popq    %rbx
LCFI4:
    popq    %rbp
LCFI5:
    ret
LFE239:
    .globl __Z15implicit_harderd
__Z15implicit_harderd:
LFB240:
    pushq   %rbp
LCFI6:
    pushq   %rbx
LCFI7:
    subq    $8, %rsp
LCFI8:
    movd    %xmm0, %rbx
# 19 "square.cpp" 1
    ### Library, harder
# 0 "" 2
    call    _sin
    movd    %xmm0, %rbp
    movd    %rbx, %xmm0
    call    _tan
    movd    %rbx, %xmm3
    mulsd   %xmm3, %xmm3
    movd    %rbp, %xmm1
    mulsd   %xmm1, %xmm1
    mulsd   %xmm0, %xmm0
    movsd   LC0(%rip), %xmm2
    subsd   %xmm0, %xmm2
    divsd   %xmm2, %xmm1
    subsd   %xmm1, %xmm3
    movapd  %xmm3, %xmm0
    addq    $8, %rsp
LCFI9:
    popq    %rbx
LCFI10:
    popq    %rbp
LCFI11:
    ret

Finally: the x * x approach does not require includeing cmath which would make your compilation ever so slightly faster all else being equal.

最后:x * x方法不需要包含cmath,这将使您的编译速度稍微快一些,其他条件都是相同的。

#1


52  

FWIW, with gcc-4.2 on MacOS X 10.6 and -O3 compiler flags,

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

x = x * x;

and

y = pow(y, 2);

result in the same assembly code:

结果相同的汇编代码:

#include <cmath>

void test(double& x, double& y) {
        x = x * x;
        y = pow(y, 2);
}

Assembles to:

组装:

    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

So as long as you're using a decent compiler, write whichever makes more sense to your application, but consider that pow(x, 2) can never be more optimal than the plain multiplication.

因此,只要你使用的是一个像样的编译器,你可以把它写在你的应用程序上,但是考虑一下,pow(x, 2)永远不会比普通的乘法更理想。

#2


21  

std::pow is more expressive if you mean x², xx is more expressive if you mean xx, especially if you are just coding down e.g. a scientific paper and readers should be able to understand your implementation vs. the paper. The difference is subtle maybe for x*x/x², but I think if you use named functions in general, it increases code-expessiveness and readability.

std::战俘更富有表现力的如果你的意思是x²,xx更富有表现力的如果你的意思是xx,特别是如果你只是编码如科学论文,读者应该能够理解您的实现与论文。对于x*x/x,这种差异是很微妙的,但是我认为,如果您通常使用命名函数,它会增加代码的扩展和可读性。

On modern compilers, like e.g. g++ 4.x, std::pow(x,2) will be inlined, if it is not even a compiler-builtin, and strength-reduced to x*x. If not by default and you don't care about IEEE floating type conformance, check your compiler's manual for a fast math switch (g++ == -ffast-math).

在现代编译器上,例如:g++ 4。std::pow(x,2)是内联的,如果它不是编译器构建的,并且强度降低到x*x。如果不是默认情况下,并且您不关心IEEE浮动类型一致性,请检查您的编译器的快速数学转换手册(g++ == -ffast-math)。


Sidenote: It has been mentioned that including math.h increases program size. My answer was:

旁注:包括数学在内。h增加程序的大小。我的回答是:

In C++, you #include <cmath>, not math.h. Also, if your compiler is not stone-old, it will increase your programs size only by what you are using (in the general case), and if your implentation of std::pow just inlines to corresponding x87 instructions, and a modern g++ will strength-reduce x² with x*x, then there is no relevant size-increase. Also, program size should never, ever dictate how expressive you make your code is.

在c++中,包括 ,而不是math.h。也,如果你的编译器不是stone-old,它会增加你的程序大小只有你使用(在一般情况下),如果你的implentation std::战俘只是内联相应x87指令,和现代g++ strength-reduce x²x * x,那么就没有相关的大小增加。而且,程序的大小永远不能决定代码的表达方式。

A further advantage of cmath over math.h is that with cmath, you get a std::pow overload for each floating point type, whereas with math.h you get pow, powf, etc. in the global namespace, so cmath increase adaptibility of code, especially when writing templates.

cmath优于数学的另一个优势。h是用cmath计算的,你得到std::对于每个浮点类型的pow重载,而数学。在全局命名空间中,您可以获得pow、powf等,因此cmath可以增强代码的适应性,尤其是在编写模板时。

As a general rule: Prefer expressive and clear code over dubiously grounded performance and binary size reasoned code.

一般的规则是:更喜欢有表现力和清晰的代码,而不是基于性能和二进制大小的代码。

See also Knuth:

参见Knuth:

"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil"

“我们应该忘记小的效率,大约97%的时间:过早优化是万恶之源”

and Jackson:

和杰克逊:

The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet.

程序优化的第一条规则:不要这样做。程序优化的第二条规则(只适用于专家!):不要这样做。

#3


12  

Not only is x*x clearer it certainly will be at least as fast as pow(x,2).

不仅x*x更清楚,它至少会和pow(x,2)一样快。

#4


9  

This question touches on one of the key weaknesses of most implementations of C and C++ regarding scientific programming. After having switched from Fortran to C about twenty years, and later to C++, this remains one of those sore spots that occasionally makes me wonder whether that switch was a good thing to do.

这个问题触及了C和c++在科学编程上大多数实现的一个关键弱点。从Fortran转到C,大约有20年,后来又转到c++,这仍然是一个很痛的地方,偶尔会让我怀疑这个开关是不是一件好事。

The problem in a nutshell:

简单地说:

  • The easiest way to implement pow is Type pow(Type x; Type y) {return exp(y*log(x));}
  • 实现pow的最简单方法是类型pow(类型x;y型){返回exp(y *日志(x));}
  • Most C and C++ compilers take the easy way out.
  • 大多数C和c++编译器都采用了简单的方法。
  • Some might 'do the right thing', but only at high optimization levels.
  • 一些人可能会“做正确的事”,但只是在高优化水平。
  • Compared to x*x, the easy way out with pow(x,2) is extremely expensive computationally and loses precision.
  • 与x*x相比,使用pow(x,2)的简单方法是非常昂贵的计算和丢失精度。

Compare to languages aimed at scientific programming:

与旨在科学规划的语言相比:

  • You don't write pow(x,y). These languages have a built-in exponentiation operator. That C and C++ have steadfastly refused to implement an exponentiation operator makes the blood of many scientific programmers programmers boil. To some diehard Fortran programmers, this alone is reason to never switch to C.
  • 你不写战俘(x,y)。这些语言有一个内置的指数运算符。C和c++坚决拒绝执行幂指数运算符,这使得许多科学程序员的血液沸腾。对于一些顽固的Fortran程序员来说,这仅仅是他们永远不会切换到C语言的原因。
  • Fortran (and other languages) are required to 'do the right thing' for all small integer powers, where small is any integer between -12 and 12. (The compiler is non-compliant if it can't 'do the right thing'.) Moreover, they are required to do so with optimization off.
  • Fortran(和其他语言)被要求对所有的小的整数幂“做正确的事情”,其中小的是-12和12之间的任何整数。(如果编译器不能“做正确的事情”,它就是不兼容的。)此外,他们还需要进行优化。
  • Many Fortran compilers also know how to extract some rational roots without resorting to the easy way out.
  • 许多Fortran编译器也知道如何在不使用简单方法的情况下提取一些合理的根。

There is an issue with relying on high optimization levels to 'do the right thing'. I have worked for multiple organizations that have banned use of optimization in safety critical software. Memories can be very long (multiple decades long) after losing 10 million dollars here, 100 million there, all due to bugs in some optimizing compiler.

依赖高优化级别来“做正确的事情”是有问题的。我曾为多个组织工作过,这些组织已经禁止在安全关键软件中使用优化。记忆可能很长(几十年),在这里损失了1000万美元,那里有1亿美元,所有这些都是由于一些优化编译器中的错误造成的。

IMHO, one should never use pow(x,2) in C or C++. I'm not alone in this opinion. Programmers who do use pow(x,2) typically get reamed big time during code reviews.

IMHO不应该在C或c++中使用pow(x,2)。在这个观点上,我并不孤单。使用pow(x,2)的程序员通常会在代码审查期间获得很大的重用。

#5


8  

In C++11 there is one case where there is an advantage to using x * x over std::pow(x,2) and that case is where you need to use it in a constexpr:

在c++ 11中,有一种情况是使用x * x优于std::pow(x,2),这种情况下,您需要在constexpr中使用它:

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

As we can see std::pow is not marked constexpr and so it is unusable in a constexpr function.

我们可以看到std::pow没有被标记为constexpr,因此它在一个constexpr函数中是不可用的。

Otherwise from a performance perspective putting the following code into godbolt shows these functions:

否则,从性能的角度来看,将以下代码放入到godbolt中就会显示这些函数:

#include <cmath>

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

double  mySqr2( double x )
{
      return std::pow( x, 2.0 );
}

generate identical assembly:

产生相同的装配:

mySqr(double):
    mulsd   %xmm0, %xmm0    # x, D.4289
    ret
mySqr2(double):
    mulsd   %xmm0, %xmm0    # x, D.4292
    ret

and we should expect similar results from any modern compiler.

我们应该期望任何现代编译器都有类似的结果。

Worth noting that currently gcc considers pow a constexpr, also covered here but this is a non-conforming extension and should not be relied on and will probably change in later releases of gcc.

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

#6


7  

x * x will always compile to a simple multiplication. pow(x, 2) is likely to, but by no means guaranteed, to be optimised to the same. If it's not optimised, it's likely using a slow general raise-to-power math routine. So if performance is your concern, you should always favour x * x.

x * x总是会编译成一个简单的乘法。pow(x, 2)很可能,但不能保证,优化到相同。如果它没有被优化,它很可能会使用一个缓慢的通用的raisep -power的数学例程。因此,如果性能是您的关注点,您应该始终支持x * x。

#7


6  

IMHO:

恕我直言:

  • Code readability
  • 代码的可读性
  • Code robustness - will be easier to change to pow(x, 6), maybe some floating point mechanism for a specific processor is implemented, etc.
  • 代码的健壮性——将更容易更改为pow(x, 6),可能会实现某些特定处理器的浮点机制,等等。
  • Performance - if there is a smarter and faster way to calculate this (using assembler or some kind of special trick), pow will do it. you won't.. :)
  • 性能——如果有更智能、更快的计算方法(使用汇编器或某种特殊技巧),pow将会这样做。你不会. .:)

Cheers

干杯

#8


1  

I would probably choose std::pow(x, 2) because it could make my code refactoring easier. And it would make no difference whatsoever once the code is optimized.

我可能会选择std::pow(x, 2),因为它可以使我的代码重构更容易。一旦代码被优化,它就不会产生任何影响。

Now, the two approaches are not identical. This is my test code:

现在,这两种方法并不相同。这是我的测试代码:

#include<cmath>

double square_explicit(double x) {
  asm("### Square Explicit");
  return x * x;
}

double square_library(double x) {
  asm("### Square Library");  
  return std::pow(x, 2);
}

The asm("text"); call simply writes comments to the assembly output, which I produce using (GCC 4.8.1 on OS X 10.7.4):

asm(“文本”);调用简单地将注释写入到程序集输出,我使用(GCC 4.8.1在OS X 10.7.4上):

g++ example.cpp -c -S -std=c++11 -O[0, 1, 2, or 3]

You don't need -std=c++11, I just always use it.

你不需要-std=c++11,我只是一直用它。

First: when debugging (with zero optimization), the assembly produced is different; this is the relevant portion:

首先:当调试(零优化)时,产生的装配是不同的;这是相关部分:

# 4 "square.cpp" 1
    ### Square Explicit
# 0 "" 2
    movq    -8(%rbp), %rax
    movd    %rax, %xmm1
    mulsd   -8(%rbp), %xmm1
    movd    %xmm1, %rax
    movd    %rax, %xmm0
    popq    %rbp
LCFI2:
    ret
LFE236:
    .section __TEXT,__textcoal_nt,coalesced,pure_instructions
    .globl __ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_
    .weak_definition __ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_
__ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_:
LFB238:
    pushq   %rbp
LCFI3:
    movq    %rsp, %rbp
LCFI4:
    subq    $16, %rsp
    movsd   %xmm0, -8(%rbp)
    movl    %edi, -12(%rbp)
    cvtsi2sd    -12(%rbp), %xmm2
    movd    %xmm2, %rax
    movq    -8(%rbp), %rdx
    movd    %rax, %xmm1
    movd    %rdx, %xmm0
    call    _pow
    movd    %xmm0, %rax
    movd    %rax, %xmm0
    leave
LCFI5:
    ret
LFE238:
    .text
    .globl __Z14square_libraryd
__Z14square_libraryd:
LFB237:
    pushq   %rbp
LCFI6:
    movq    %rsp, %rbp
LCFI7:
    subq    $16, %rsp
    movsd   %xmm0, -8(%rbp)
# 9 "square.cpp" 1
    ### Square Library
# 0 "" 2
    movq    -8(%rbp), %rax
    movl    $2, %edi
    movd    %rax, %xmm0
    call    __ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_
    movd    %xmm0, %rax
    movd    %rax, %xmm0
    leave
LCFI8:
    ret

But when you produce the optimized code (even at the lowest optimization level for GCC, meaning -O1) the code is just identical:

但是,当您生成优化的代码时(即使在GCC的最低优化级别上,这意味着-O1)代码也是一样的:

# 4 "square.cpp" 1
    ### Square Explicit
# 0 "" 2
    mulsd   %xmm0, %xmm0
    ret
LFE236:
    .globl __Z14square_libraryd
__Z14square_libraryd:
LFB237:
# 9 "square.cpp" 1
    ### Square Library
# 0 "" 2
    mulsd   %xmm0, %xmm0
    ret

So, it really makes no difference unless you care about the speed of unoptimized code.

所以,除非你关心的是未优化代码的速度,否则这没什么区别。

Like I said: it seems to me that std::pow(x, 2) more clearly conveys your intentions, but that is a matter of preference, not performance.

就像我说的:在我看来std::pow(x, 2)更清楚地表达了你的意图,但那只是一个偏好问题,而不是表现。

And the optimization seems to hold even for more complex expressions. Take, for instance:

而且,优化似乎也适用于更复杂的表达式。,例如:

double explicit_harder(double x) {
  asm("### Explicit, harder");
  return x * x - std::sin(x) * std::sin(x) / (1 - std::tan(x) * std::tan(x));
}

double implicit_harder(double x) {
  asm("### Library, harder");
  return std::pow(x, 2) - std::pow(std::sin(x), 2) / (1 - std::pow(std::tan(x), 2));
}

Again, with -O1 (the lowest optimization), the assembly is identical yet again:

再一次,以-O1(最低的优化),组装再次相同:

# 14 "square.cpp" 1
    ### Explicit, harder
# 0 "" 2
    call    _sin
    movd    %xmm0, %rbp
    movd    %rbx, %xmm0
    call    _tan
    movd    %rbx, %xmm3
    mulsd   %xmm3, %xmm3
    movd    %rbp, %xmm1
    mulsd   %xmm1, %xmm1
    mulsd   %xmm0, %xmm0
    movsd   LC0(%rip), %xmm2
    subsd   %xmm0, %xmm2
    divsd   %xmm2, %xmm1
    subsd   %xmm1, %xmm3
    movapd  %xmm3, %xmm0
    addq    $8, %rsp
LCFI3:
    popq    %rbx
LCFI4:
    popq    %rbp
LCFI5:
    ret
LFE239:
    .globl __Z15implicit_harderd
__Z15implicit_harderd:
LFB240:
    pushq   %rbp
LCFI6:
    pushq   %rbx
LCFI7:
    subq    $8, %rsp
LCFI8:
    movd    %xmm0, %rbx
# 19 "square.cpp" 1
    ### Library, harder
# 0 "" 2
    call    _sin
    movd    %xmm0, %rbp
    movd    %rbx, %xmm0
    call    _tan
    movd    %rbx, %xmm3
    mulsd   %xmm3, %xmm3
    movd    %rbp, %xmm1
    mulsd   %xmm1, %xmm1
    mulsd   %xmm0, %xmm0
    movsd   LC0(%rip), %xmm2
    subsd   %xmm0, %xmm2
    divsd   %xmm2, %xmm1
    subsd   %xmm1, %xmm3
    movapd  %xmm3, %xmm0
    addq    $8, %rsp
LCFI9:
    popq    %rbx
LCFI10:
    popq    %rbp
LCFI11:
    ret

Finally: the x * x approach does not require includeing cmath which would make your compilation ever so slightly faster all else being equal.

最后:x * x方法不需要包含cmath,这将使您的编译速度稍微快一些,其他条件都是相同的。