在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
。
答案 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) 公式更容易出错。