我发现了C中log
函数的一个奇怪行为,并且对log
函数处理复数无穷数的行为感到困惑。具体来说,当我期望(inf + nan * 1j)
时,log(inf + inf * 1j)
等于(inf + 0.785398j)
。
取复数的对数时,真实的部是输入绝对值的对数,虚部是输入的相位。返回0.785398作为log(inf + inf * 1j)
的虚部意味着它假设实部和虚部中的inf
具有相同的长度。这个假设似乎与其他计算不一致,例如,inf - inf == nan
、inf / inf == nan
(假设2 × inf
)不必具有相同的值。
为什么log(inf + inf * 1j)
的假设不同?
复制C代码:
#include <complex>
#include <limits>
#include <iostream>
int main() {
double inf = std::numeric_limits<double>::infinity();
std::complex<double> b(inf, inf);
std::complex<double> c = std::log(b);
std::cout << c << "\n";
}
复制Python代码(numpy):
import numpy as np
a = complex(float('inf'), float('inf'))
print(np.log(a))
3条答案
按热度按时间lhcgjxsq1#
free final draft of the C99 specification在第491页上说
clog(+∞,+i∞)返回+∞ + iπ/4。
currently仍然是这种情况。C++ specification使用注解解释了相同的规则
此函数的语义旨在与C函数
clog
保持一致。我同意这种行为从数学的Angular 看是令人困惑的,并且可以说与其他
inf
语义不一致,正如你指出的,但是实际上,它是C标准的一部分,这使得它成为C++标准的一部分,并且由于NumPy通常依赖于C行为(即使在令人困惑的情况下),这在Python示例中是继承的。标准库
cmath.log()
函数具有相同的行为(if you test it right...):我没有办法研究C标准的基本原理,我假设这里有实用的选择,可能是在考虑这些复杂的函数如何相互作用时。
9vw9lbht2#
值0.785398(实际上是pi/4)至少与 * 一些 * 其他函数一致:正如你所说,复数的对数的虚部等于该数的相角。2这可以转化为一个问题:
inf + j * inf
的相位角是多少?我们可以通过
atan2(Im(z), Re(z))
计算复数z
的相位角,对于给定的数字,这可以归结为计算atan2(inf, inf)
,对于Numpy和C/C++,atan2(inf, inf)
也是0.785398(或pi/4),所以现在可以问一个类似的问题:为什么是atan2(inf, inf) == 0.785398
?对于后者,我没有答案(除了“C/C++规范是这么说的”,其他人已经回答了),我只有一个猜测:如
atan2(y, x) == atan(y / x)
对x > 0
,可能有人在此上下文中决定不将inf / inf
解释为“未定义”,而是解释为“一个非常大的数除以同一个非常大的数”。此比率的结果将是1,并且根据atan
的数学定义,atan(1) == pi/4
为atan
。也许这不是一个令人满意的答案,但至少我可以有希望地表明,给定边缘情况下的
log
定义与相关函数定义的类似边缘情况并非完全不一致。编辑:正如我所说,与 * 一些 * 其他函数一致:例如,它也与
np.angle(complex(np.inf, np.inf)) == 0.785398
一致。编辑2:查看source code of an actual
atan2
implementation时出现以下代码注解:注意,不明显的情况是y和x都是无穷大或都是零。有关详细信息,请参阅 * 复初等函数的分支切割,或W. Kahan著的关于无符号位的许多Ado *
我翻查了参考文档,你可以找到一个here的副本。在本参考的第8章,称为“复数零和无穷大”,William Kahan(他既是数学家又是计算机科学家,根据维基百科,“浮点之父”)涵盖了复数的零和无穷大边缘情况,并得出了pi/4用于将
inf + j * inf
输入arg
函数(X1 M15 N1 X是计算复数的相位角的函数,就像上面的np.angle
一样)。你可以在第17页的链接PDF中找到这个结果。我不是一个足够的数学家,无法总结Kahan的理论基础(也就是说:我真的不明白),但也许别人可以。anauzrmj3#
如果我们从纯数学的Angular 来考虑这个问题,那么我们可以从极限的Angular 来看待运算,例如当x趋于无穷大时,1/x趋于0(表示为lim(x =〉inf)1/x = 0),这就是我们在浮点数中观察到的。
对于2个无穷大的运算,我们分别考虑每个无穷大,因此:
一般来说,我们说inf/x = inf,x/inf = 0。
我们应该选择哪一个呢?floats的规范通过声明它是NaN来回避。
然而,对于复杂日志,我们观察到:
仍然有一个矛盾,但它不是在0和inf之间,而是在0和pi/2之间,所以规范的作者选择了折中的方法。我不知道他们为什么做出这个选择,但浮点无穷不是数学上的无穷。而是表示“这个数字太大了,无法表示”。(复数)可能比减法和除法更纯粹的数学,作者可能觉得保留身份im(log(x+xj))== pi/4是有用的。