假设 a 是双倍的,2.0*a 比 2*a 快吗?

Assuming a is double, is 2.0*a faster than 2*a?

本文关键字:快吗 假设      更新时间:2023-10-16

很久以前,在一本关于古代FORTRAN的书中,我看到过这样的说法,即使用带有浮点变量的整数常量会更慢,因为常量需要先转换为浮点形式:

double a = ..;
double b = a*2;   // 2 -> 2.0 first
double c = a*2.0; 

在现代C++中编写 2.0 而不是 2 仍然有益吗?如果没有,可能应该首选"整数版本",因为 2.0 更长并且对人类读者没有任何区别。

我使用复杂的长表达式,其中这些".0"将在性能或可读性方面产生影响(如果有的话)。

首先要涵盖其他答案,没有2vs2.0不会造成性能差异,这将在编译时检查以创建正确的值。但是要回答这个问题:

在现代C++中写 2.0 而不是 2 仍然有益吗?

绝对。

但这不是因为性能,而是可读性和错误。想象一下以下操作:

double a = (2 / someOtherNumber) * someFloat;  

someOtherNumber的类型是什么?因为如果它是integer类型,那么您会因为整数除法而遇到麻烦。2.02.0f具有明显的优势:

  1. 告诉代码的读者您的意图。
  2. 避免您无意的整数除法错误。

原始问题:

让我们比较一下程序集输出。

double foo(double a)
{
return a * 2;
}
double bar(double a)
{
return a * 2.0f;   
}
double baz(double a)
{
return a * 2.0;   
}

结果在

0000000000000000 <foo>: //double x int
0:   f2 0f 58 c0             addsd  %xmm0,%xmm0          // add with itself
4:   c3                      retq                        // return (quad)
5:   90                      nop                         // padding
6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1) // padding
d:   00 00 00 
0000000000000010 <bar>: //double x float
10:   f2 0f 58 c0             addsd  %xmm0,%xmm0          // add with itself
14:   c3                      retq                        // return (quad)
15:   90                      nop                         // padding
16:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1) // padding
1d:   00 00 00 
0000000000000020 <baz>: //double x double
20:   f2 0f 58 c0             addsd  %xmm0,%xmm0          // add with itself
24:   c3                      retq                        // return (quad)

如您所见,它们都是相等的,根本不执行乘法。

即使做实乘法(a*5),它们都是相等的,并且表现得很差

0:  f2 0f 59 05 00 00 00    mulsd  0x0(%rip),%xmm0        # 8 <foo+0x8>
7:  00 
8:  c3                      retq   

加法:

@Goswin-Von-Brederlow评论说,使用非常量表达式将导致不同的组装。让我们像上面一样测试一下,但使用以下签名。

double foo(double a, int b); //int, float, double for foo/bar/baz

这导致输出:

0000000000000000 <foo>: //double x int
0:   66 0f ef c9             pxor   %xmm1,%xmm1  // clear xmm1
4:   f2 0f 2a cf             cvtsi2sd %edi,%xmm1 // convert edi (second argument) to double
8:   f2 0f 59 c1             mulsd  %xmm1,%xmm0  // mul xmm1 with xmm0
c:   c3                      retq                // return
d:   0f 1f 00                nopl   (%rax)       // padding
0000000000000010 <bar>: //double x float
10:   f3 0f 5a c9             cvtss2sd %xmm1,%xmm1 // convert float to double
14:   f2 0f 59 c1             mulsd  %xmm1,%xmm0   // mul
18:   c3                      retq                 // return
19:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)     // padding
0000000000000020 <baz>: //double x double
20:   f2 0f 59 c1             mulsd  %xmm1,%xmm0   // mul directly
24:   c3                      retq                 // return

在这里,您可以看到从类型到双精度的(运行时)转换,这当然会导致(运行时)开销。

No.

以下代码:

double f1(double a) {
double b = a*2;
return b;
}
double f2(double a) {
double c = a*2.0;
return c;
}

。当使用 Clang 在 gcc.godbolt.org 上编译时,生成以下程序集:

f1(double): # @f1(double)
addsd xmm0, xmm0
ret
f2(double): # @f2(double)
addsd xmm0, xmm0
ret

您可以看到两个函数完全相同,编译器甚至用加法替换了乘法。我希望这个千年的任何C++编译器都是一样的 - 相信他们,他们非常聪明。

不,它不是更快。如果编译器知道整数是什么,为什么还要等到运行时才将整数转换为浮点数?我想如果你完全禁用了优化,你可能会说服一些非常迂腐的编译器这样做,但我所知道的所有编译器都会理所当然地进行优化。

现在,如果您使用浮点类型aa*bb整数类型,并且两者都不是编译时文字,则在某些体系结构上可能会导致严重的性能影响(特别是如果您最近计算b)。但是在文字的情况下,编译器已经支持你了。