提问者:小点点

为什么在C/Python/NumPy中log(inf-inf j)等于(inf 0.785398 j)?


我一直在C中发现log函数的奇怪行为,以及log函数处理复杂无限数的行为。具体来说,log(inf inf*1j)等于(inf 0.785398j)当我期望它是(inf nan*1j)时。

取复数的对数时,实部是输入绝对值的对数,虚部是输入的相位。返回0.785398作为< code>log(inf inf * 1j)的虚部意味着它假定实部和虚部中的< code>inf具有相同的长度。这种假设似乎与其他计算不一致,例如,< code>inf - inf == nan,< code>inf / inf == nan假设2个< code>inf不一定具有相同的值。

为什么对数(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个答案

匿名用户

C99规范的免费最终草案在第491页

clog( ∞,i∞)返回∞ iπ/4。

目前还是这种情况,C规范用注释解释了同样的规则

此函数的语义旨在与C函数< code>clog保持一致。

我同意,从数学的角度来看,这种行为是令人困惑的,而且正如你指出的,可以说与其他< code>inf语义不一致。但实际上,它是C标准的一部分,这使它成为C标准的一部分,并且由于NumPy通常依赖于C行为(即使在令人困惑的情况下),这在Python示例中被继承。

标准库cmath.log()函数具有相同的行为(如果您测试正确…):

>>> import cmath

>>> cmath.log(complex(float('inf'), float('inf')))
(inf+0.7853981633974483j)

我没有办法调查C标准的基本原理。我假设在考虑这些复杂功能如何相互作用时,这里可能会做出务实的选择。

匿名用户

0.785398的值(实际上是π/4)至少和其他一些函数是一致的:就像你说的,一个复数的对数的虚部和该数的相角是一致的。这可以重新表述为一个自己的问题:< code>inf j * inf的相角是多少?

我们可以通过atan2(Im(z), Re(z))计算复数z的相位角。使用给定的数字,这归结为计算 atan2(inf, inf),对于 Numpy 和 C/C 来说,这也是 0.785398(或 pi/4)。所以现在可能会问一个类似的问题:为什么 atan2(inf, inf) == 0.785398

我对后者没有答案(除了其他人已经回答的“C/C规范这么说”),我只有一个猜测:atan2(y,x)==atan(y/x)对于x

也许这不是一个令人满意的答案,但至少我可以希望表明,在给定的边缘情况下的< code>log定义与相关函数定义的类似边缘情况并不完全不一致。

编辑:正如我所说,与其他一些函数一致:它也与一致np.angle(复杂(np.inf,np.inf )) == 0.785398,例如。

编辑 2:查看实际 atan2 实现的源代码,会出现以下代码注释:

请注意,不明显的情况是y和x都是无穷大或都是零。有关详细信息,请参阅W. Kahan的“复初等函数的分支割裂,或关于无符号位的多Ado”

我找到了引用的文档,你可以在这里找到一份副本。在第8节,称为“复数零和无穷大”,卡汉涵盖了零和无穷大边缘情况,并到达pi/4,用于将inf j*inf输入arg函数(arg是计算复数相位角的函数)。您将在链接的PDF中的第17页上找到这个结果。我不是足够的数学家,无法总结卡汉的基本原理,但也许其他人可以。

匿名用户

如果我们从纯数学的角度考虑这一点,那么我们可以从极限的角度来看待运算,例如,当 x 走向无穷大时,1/x 趋向于 0(表示为 lim(x =

对于2个无穷大的运算,我们分别考虑每个无穷大。因此:

lim(x => inf) x/1 = inf
lim(x => inf) 1/x = 0

一般来说,我们说inf/x=inf,x/inf=0。因此:

lim(x => inf) inf/x = inf
lim(x => inf) x/inf = 0

我们应该选择这 2 个中的哪一个?浮点数的规范通过声明其为 NaN 来回避。

然而,对于复杂的日志,我们观察到:

lim(x=>inf) log(x + 0j) = inf + 0j
lim(x=>inf) log(0 + xj) = inf + pi/2j
lim(x=>inf) log(inf + xj) = inf + 0j
lim(x=>inf) log(x + infj) = inf + pi/2j

仍然存在矛盾,但不是在 0 和 inf 之间,而是在 0 和 pi/2 之间,因此规范作者选择拆分差异。他们为什么做出这个选择我不能说,但浮点无穷大不是数学无穷大,而是表示“这个数字太大而无法表示”。鉴于log(complex)的使用可能比减法和除法的使用更纯粹的数学,作者可能觉得保留恒等式im(log(x xj)) == pi/4是有用的。