std :: norm(std :: complex)使用平方根而不是快速实现

时间:2014-04-30 21:34:17

标签: c++

在c ++中,复数数字norm的{​​{1}}被定义为c。这意味着它的abs(c)^2

这是实施:

re(c)^2+im(z)^2

为什么有人想要使用第二个实现?

第一个显然更快,因为它不使用template<bool> struct _Norm_helper { template<typename _Tp> static inline _Tp _S_do_it(const complex<_Tp>& __z) { const _Tp __x = __z.real(); const _Tp __y = __z.imag(); return __x * __x + __y * __y; } }; template<> struct _Norm_helper<true> { template<typename _Tp> static inline _Tp _S_do_it(const complex<_Tp>& __z) { _Tp __res = std::abs(__z); return __res * __res; } }; ,其中涉及abs

2 个答案:

答案 0 :(得分:2)

由于std :: abs函数可以由用户专门化,因此预期norm应该调用专用版本。后面的实现调用用户的abs,前者假设一个共同的实现。

答案 1 :(得分:1)

cppreference.com 上查找类似的函数 std::hypot,它说“计算 x 和 y 的平方和的平方根,在中间阶段没有过度上溢或下溢计算。"

我猜 std::abs 通常只是 std::hypot 的包装器)。

这是关键:关键在于数值稳定性。对于浮点表示,考虑 y=0 和 x>0 要么非常大要么非常小的情况。

因此,如果 x 非常大,以至于 x^2 = +inf,那么只执行 sqrt(x^2 + y^2) 就会溢出并给出 +inf。如果 x 很小,所以 x^2 = 0,你会得到 0。

(永远记住我们在谈论浮点表示)。

无论哪种情况,它都与正确答案 x 有很大不同。

正确算法是:考虑,例如,x > y > 0 的情况。让 r = y/x 并计算 x.sqrt( 1 + r^2),这是很多在数值上表现更好;请注意 0 < r < 1。通常您会考虑 |x| 中的较大者和 |y|并将其除掉,注意 x,y 何时都为零。

任何关心准确性而不是速度的好的 std::abs 实现都在做与上面类似的事情,我敢打赌,根本不做 sqrt(x^2 + y^2)。

你可以做一些简单的数值分析(如果你知道一点微积分和泰勒级数,和/或幂 p=1/2 的二项式展开):一些代数计算错误的可能大小将表明朴素的 sqrt(x^2 + y^2) 公式更容易出错。