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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6321170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 19:55:03  来源:igfitidea点击:

Is there any advantage to using pow(x,2) instead of x*x, with x double?

c++mathfloating-point

提问by Alessandro Jacopson

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 更胜一筹?

回答by Alnitak

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

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

x = x * x;

and

y = pow(y, 2);

result in the sameassembly 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 moreoptimal than the plain multiplication.

因此,只要您使用的是不错的编译器,就可以编写对您的应用程序更有意义的内容,但要考虑到pow(x, 2)它永远不会比普通乘法优化。

回答by Sebastian Mach

std::pow is more expressive if you mean x2, 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/x2, but I think if you use named functions in general, it increases code-expessiveness and readability.

如果您的意思是 x2,则 std::pow 更具表现力,如果您的意思是 x x,则 x x 更具表现力,特别是如果您只是编写代码,例如科学论文,并且读者应该能够理解您的实现与论文。对于 x*x/x2 而言,差异可能很微妙,但我认为如果您通常使用命名函数,它会增加代码的表达性和可读性。

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.x,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:

旁注:有人提到包含 math.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 x2 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++ 中,你#include <cmath>而不是 math.h。此外,如果您的编译器不是旧的,它只会根据您使用的内容(在一般情况下)增加程序大小,并且如果您对 std::pow 的实现只是内联到相应的 x87 指令,以及现代 g++将强度减少 x2 与 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 相对于 math.h 的另一个优势是,使用 cmath,您会为每个浮点类型获得 std::pow 重载,而使用 math.h 您在全局命名空间中获得 pow、powf 等,因此 cmath 提高了适应性代码,尤其是在编写模板时。

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

作为一般规则:比起可疑的基础性能和二进制大小的推理代码,更喜欢表达性和清晰的代码。

See also Knuth:

另见克努斯:

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

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

and Hymanson:

和Hyman逊:

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

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

回答by David Heffernan

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

不仅x*x更清晰,它肯定至少会一样快pow(x,2)

回答by David Hammen

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 powis Type pow(Type x; Type y) {return exp(y*log(x));}
  • Most C and C++ compilers take the easy way out.
  • 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.
  • 最简单的实现方法powType pow(Type x; Type y) {return exp(y*log(x));}
  • 大多数 C 和 C++ 编译器都采用了简单的方法。
  • 有些人可能会“做正确的事”,但仅限于高优化级别。
  • 与 相比x*x,简单的方法 withpow(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.
  • 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.
  • Many Fortran compilers also know how to extract some rational roots without resorting to the easy way out.
  • 你不写pow(x,y)。这些语言有一个内置的取幂运算符。C 和 C++ 坚决拒绝实现取幂运算符,这让许多科学程序员的程序员热血沸腾。对于一些顽固的 Fortran 程序员来说,仅此一点就是永远不要切换到 C 的原因。
  • Fortran(和其他语言)需要为所有小整数幂“做正确的事”,其中 small 是 -12 到 12 之间的任何整数。(如果编译器不能“做正确的事”,则编译器不合规) .) 此外,他们需要在关闭优化的情况下这样做。
  • 许多 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 neveruse 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.

恕我直言,你应该从来没有使用pow(x,2)C或C ++。我并不孤单。确实使用的程序员pow(x,2)通常会在代码期间获得大量时间。

回答by Shafik Yaghmour

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

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

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

As we can see std::powis not marked constexprand so it is unusable in a constexprfunction.

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

Otherwise from a performance perspective putting the following code into godboltshows 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 herebut 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.

回答by AshleysBrain

x * xwill 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)很可能,但绝不保证,被优化为相同的。如果它没有优化,它可能会使用缓慢的一般提升到权力的数学例程。因此,如果您关心性能,则应始终青睐x * x.

回答by Hertzel Guinness

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.
  • 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(x, 6),也许实现了特定处理器的某些浮点机制等。
  • 性能 - 如果有更智能、更快的计算方法(使用汇编程序或某种特殊技巧),pow 会这样做。你不会.. :)

Cheers

干杯

回答by Escualo

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("text");调用只是将注释写入我使用的程序集输出(OS X 10.7.4 上的 GCC 4.8.1):

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    , %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    , %rsp
    movsd   %xmm0, -8(%rbp)
# 9 "square.cpp" 1
    ### Square Library
# 0 "" 2
    movq    -8(%rbp), %rax
    movl    , %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    , %rsp
LCFI3:
    popq    %rbx
LCFI4:
    popq    %rbp
LCFI5:
    ret
LFE239:
    .globl __Z15implicit_harderd
__Z15implicit_harderd:
LFB240:
    pushq   %rbp
LCFI6:
    pushq   %rbx
LCFI7:
    subq    , %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    , %rsp
LCFI9:
    popq    %rbx
LCFI10:
    popq    %rbp
LCFI11:
    ret

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

最后:在其他条件相同的情况下,该x * x方法不需要includeingcmath这将使您的编译稍微快一点。