numpy的angle函数为同一输入返回不同答案?

3

我正在使用Python 3.7.7和numpy 1.19.1。这是代码:

import numpy as np
a = 55.74947517067784019673 + 0j
print(f'{-a == -1 * a}, {np.angle(-a)}, {np.angle(-1 * a)}')

这是输出结果:

True, -3.141592653589793, 3.141592653589793

我有两个问题:
  1. 为什么角度函数会给出相同输入不同的输出结果?
  2. 根据文档,角度输出范围是(-pi, pi],那么为什么有一个输出结果是-np.pi
2个回答

2
如果您查看np.angle源代码,它使用了函数np.arctan2。现在,根据numpy文档np.arctan2使用底层C库,该库有以下规则:请注意,+0和-0是不同的浮点数,+inf和-inf也是如此。这导致在使用+/-0进行计算时会产生不同的行为。因此,在这种情况下,规则是:
y: +/- 0
x: <0
angle: +/- pi

现在,如果您尝试:
a = 55.74947517067784019673
print(f'{-a == -1 * a}, {np.angle(-a)}, {np.angle(-1 * a)}')
#True, 3.141592653589793, 3.141592653589793

如果你尝试:

a = 55.74947517067784019673 + 0j
print(-a)
#(-55.74947517067784-0j)
print(-1*a)
#(-55.74947517067784+0j)
print(f'{-a == -1 * a}, {np.angle(-a)}, {np.angle(-1 * a)}')
#True, -3.141592653589793, 3.141592653589793

这符合库协议。

至于你的第二个问题,我猜这是一个打字错误/错误,因为np.arctan2文档中写道:

角度数组(以弧度为单位),范围为[-pi, pi]。如果x1和x2都是标量,则为标量。

-a-1*a的解释:

首先,55.74947517067784019673 + 0j不是复数的构造,仅仅是将一个浮点数加到一个复数上(要显式地构造一个复数,请使用complex(55.74947517067784019673, 0.0),并注意整数没有带符号的零,只有浮点数有)。-a仅仅是将符号取反,非常容易理解。现在来看一下计算-1*a时会发生什么:

为了简单起见,假设a = 55.5 + 0j

  • 首先,a = 55.5+0j转换为complex(55.5, 0.0)
  • 其次,-1等于complex(-1.0, 0.0)
  • 然后,complex(-1.0, 0.0)*complex(55.5, 0.0)等于complex((-1.0*55.5 - 0.0*0.0), (-1.0*0.0 + 0.0*55.5))等于complex((-55.5 - 0.0), (-0.0 + 0.0)),这等于complex(-55.5, 0.0)

注意,-0.0+0.0等于0.0,符号规则仅适用于乘法和除法,正如此链接中所述并在下面的评论中引用。为了更好地理解它,请参见以下内容:

print(complex(-1.0, -0.0)*complex(55.5, 0.0))
#(-55.5-0j)

虚部为(-0.0*55.5 - 1.0*0.0) = (-0.0 - 0.0) = -0.0


顺便问一下,为什么-a-1 * a一开始就不完全相等呢? - Julien
@Julien,他们在这里解释了类似的情况:https://dev59.com/mm855IYBdhLWcg3w6IzV - Ehsan
@Ehsan 在你提供的链接中的答案中,它说:“当乘法或除法涉及有符号零时,在计算答案的符号时应用通常的符号规则。”所以朱利安问的问题没有得到回答。我期望 -1 *(0+0j)的结果为(-0-0j),那么为什么是(-0+0j)? - Wim van der Meer
@WimvanderMeer,它确切地解释了您正在处理的问题。您的误解来自于对复数的理解。我将添加一个编辑来更好地解释它。 - Ehsan
@WimvanderMeer 我尽力清晰地解释了在帖子编辑中发生了什么。如果您的问题已得到解决,请随意接受/投票支持该答案并关闭问题。谢谢。 - Ehsan
1
好巧妙啊。我天真地以为实数复数只是像标量向量乘法一样分配...像往常一样,要始终质疑你的假设。 :) +1 - Julien

1

如果打印-a-1*a,你会发现它们是不同的。

-a
Out[4]: (-55.74947517067784-0j)

-1*a
Out[5]: (-55.74947517067784+0j) # note +0j not -0j

不知道numpy实现的细节,虚部的符号可能被用来计算角度...这可以解释为什么这种退化情况会给出不同的结果。

对于第二个问题,我认为这看起来像是一个bug或文档错误...


1
不确定2)在技术上是否算作文档中的错误。 pi = -3.141592653589793..... <-3.141592653589793 在数学意义上是正确的。因此,-3.141592653589793实际上在(-pi,pi]范围内。 - Emil Vatai
1
我认为可以合理地假设,当文档中提到 pi 时,它们指的是其数值表示 np.pi 而不是其真实的超越值。这是任何程序员关心的唯一事情,在这个意义上,文档是错误的... 至少看起来像一个讨厌的瑕疵。 - Julien
感谢您的评论。我修改了问题,以表明我的意思是-np.pi。 - Wim van der Meer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接