我天真地假设,复数乘法将由编译器内联,例如对于这个函数:
#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/