我天真地假设,复数乘法将由编译器内联,例如对于这个函数:

#include <complex>

void mult(std::complex<double> &a, std::complex<double> &b){
    a*=b;
}

但是,当 gcc 编译(使用 -O2 )时, resulting assembler 令人惊讶(至少对我而言):
mult(std::complex<double>&, std::complex<double>&):
        pushq   %rbx
        movsd   8(%rdi), %xmm3
        movsd   (%rdi), %xmm2
        movq    %rdi, %rbx
        movsd   8(%rsi), %xmm1
        movsd   (%rsi), %xmm0
        call    __muldc3
        movsd   %xmm0, (%rbx)
        movsd   %xmm1, 8(%rbx)
        popq    %rbx
        ret

有一个对这个函数 __multdc3 的调用,它以某种方式取代了对 operator*= 的调用(它的被破坏的名称将是 _ZNSt7complexIdEmLIdEERS0_RKS_IT_E 并且每个引用都会传递复数)。

然而,在 operator*=implementation 中似乎没有什么特别之处可以解释这个魔法:
// 26.2.5/13
  // XXX: This is a grammar school implementation.
  template<typename _Tp>
    template<typename _Up>
    complex<_Tp>&
    complex<_Tp>::operator*=(const complex<_Up>& __z)
    {
      const _Tp __r = _M_real * __z.real() - _M_imag * __z.imag();
      _M_imag = _M_real * __z.imag() + _M_imag * __z.real();
      _M_real = __r;
      return *this;
}

我一定遗漏了一些东西,因此我的问题是:产生汇编程序的原因是什么?

最佳答案

您应该注意,严格来说,通过公式实现复数浮点乘法是“错误的”

(a+i*b)*(c + i*d) = a*c - b*d + i*(b*c + a*d)

我在引号中写错了,因为 C++ 标准实际上并不需要正确的实现。 C 确实在某些附录中指定了它。

简单的实现不会在输入中使用 Inf 和/或 NaN 给出正确的结果。

考虑 (Inf + 0*i)*(Inf + 0*i) :显然,对于一致的行为,结果应该与实际浮点相同,即 Inf(Inf + 0*i) ,分别。但是,上面的公式给出了 Inf + i*NaN

所以我可以想象 __muldc3 是一个更长的函数,可以正确处理这些情况。

-ffast-math 调用消失时,这很可能是解释。

关于c++ - 当两个 std::complex 相乘时,为什么会调用 __muldc3?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49438158/

10-12 23:51