当值加倍时,加法与乘法与左移操作[重复]

Posted

技术标签:

【中文标题】当值加倍时,加法与乘法与左移操作[重复]【英文标题】:Addition vs multiplication vs shift left operation when double the value [duplicate] 【发布时间】:2019-03-22 06:11:11 【问题描述】:

假设一个数字具有非常大的值,例如 9,046,744,073,709,551,615。

我使用加法、乘法和左移将值加倍。

#include <iostream>
using namespace std;

int main()
   unsigned long long int value = 9046744073709551615;

   cout << (value + value) << endl;
   cout << (value * 2) << endl;  
   cout << (value << 1) << endl;

   return 0;


哪个计算得更快?

【问题讨论】:

为什么不检查编译器的汇编输出并启用优化?您可能会发现每一个都转换为相同的代码。 编译器在微优化方面比人类要好得多。使用在代码上下文中有意义的那个。 (在这种特殊情况下,任何体面的编译器都会在编译时执行每个操作并打印一个常量。) 为什么你认为数字的大小很重要? value is hardcoded 或read at runtime 时,每种情况下的优化程序集相同。 Steve Maguire,“Writing Solid Code”,1993 年:“多年来,我发现了程序员使用移位来划分不能保证为正的有符号值的错误。我已经追踪了程序员转向错误方向的错误。我什至追踪了程序员通过不小心将表达式如 a=b+c/4 转换为 a=b+c>>2 而引入优先级错误的错误。我不知道不记得曾经追踪过一个错误,其中程序员打算除以 4 并在输入字符 / 和 4" 时出错 【参考方案1】:

简短的回答是,没有一个替代方案更快。当启用编译器优化时,它们会生成相同的代码。

对于在编译时知道值的情况,程序集是这样的:Compiler Explorer link

movabs  rsi, -353255926290448386
call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long long>(unsigned long long)

它将一个常量加载到一个寄存器中并调用operator&lt;&lt;()。所有三种备选方案都会产生这种优化的装配体。

对于在运行时读取值的情况,程序集是这样的:Compiler Explorer link

mov     rax, QWORD PTR [rsp+8]
mov     edi, OFFSET FLAT:_ZSt4cout
lea     rsi, [rax+rax]
call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long long>(unsigned long long)

它使用加法 (rax+rax) 并调用 operator&lt;&lt;()。同样,所有三个备选方案都产生相同的程序集。

【讨论】:

【参考方案2】:

我刚刚从godbolt 中挑选了一个编译器。确实完全没有区别。玩转编译器和优化级别。

-O0 中使用 x86-64_gcc 会产生 3 倍的结果:

    mov     rax, QWORD PTR [rbp-8]
    add     rax, rax
    mov     rsi, rax
    mov     edi, OFFSET FLAT:_ZSt4cout
    call    std::basic_ostream<char, std::char_traits<char> >::operator<<(unsigned long long)
    mov     esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
    mov     rdi, rax
    call    std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))

【讨论】:

以上是关于当值加倍时,加法与乘法与左移操作[重复]的主要内容,如果未能解决你的问题,请参考以下文章

请问怎样用加法-移位实现定点乘除法?

PHP在数据输出时进行乘法和加法运算

加法乘法线段树模板

深入理解Python列表(list)

02-线性结构2 一元多项式的乘法与加法运算

sap的四则运算中的加法