【问题标题】:Both cmath and numpy give "incorrect" value of asin(10)cmath 和 numpy 都给出了 asin(10) 的“不正确”值
【发布时间】:2020-10-29 02:54:12
【问题描述】:

我最近需要快速找到10的反正弦值。我决定用python帮我计算一下:

cmath.asin(10)

根据经验,我预计第四象限会出现结果(正实数 (pi/2) 和负虚数)。惊喜……它返回了第一象限的结果。我也尝试过 numpy.arcsin ......结果相同。虽然返回值的正弦值确实是 10,但我不认为这是反正弦函数的标准主值

>>> import math
>>> import numpy as np
>>> z=cmath.asin(10)
>>> z
(1.5707963267948966+2.993222846126381j)
>>> cmath.sin(z)
(9.999999999999998+6.092540900222251e-16j)
>>> z2=np.arcsin(10+0j)
>>> np.sin(z2)
(10+6.092540900222253e-16j)

我使用 numpy 发现了同样的结果(如上所示)。

是否有一个 python 模块,我可以期望从中获取复杂值函数的标准主值(即遵循主分支切割)?或者,标准主值的概念是否过于流畅在这一点上期待一致性?


【问题讨论】:

  • np.arcsin 解释了它如何处理复杂的输入。由于我对复杂的数学有点生疏,你能把它的branch cuts 翻译成你的象限吗?
  • sympy 给出1.5707963267949 - 2.99322284612638⋅ⅈ。我认为cmathnumpy 函数依赖于底层c 代码,而不是重新实现。
  • 很公平。关键选择是关于如何处理带符号的零和分支切割:您是否忽略零的符号以决定您在分支切割的哪一侧,或者您是否根据零的符号进行区分?基于零符号的区分提供了一些不错的属性,例如 asin(conj(z)) 始终与 conj(asin(z)) 相同,包括所有极端情况。但从数学上讲,这感觉有点像作弊。顺便说一句,我注意到 Python 和 NumPy 的文档在这里都具有误导性,使用“从下方连续”之类的措辞。
  • @hpaulj:不,cmath 不依赖于底层 C 代码。它确实重新实现了一些东西,但遵循 Kahan 的建议和 C 标准是一个深思熟虑的、有意识的选择。

标签: python numpy math


【解决方案1】:

cmath 行为有点标准,因为它不仅限于 cmath 和 NumPy:它还符合 C 标准的附录 G 中推荐的行为(至少 C99以及后来),以及 William Kahan 在他的 paper“复杂基本函数的分支切割”中列出的定义,副标题为“无事生非的标志位”。

但我们在这里真正看到的是纯数学世界和浮点算术世界之间的另一个分歧。

上述“标准”行为特定于使用浮点算术执行的数学运算,特别是使用浮点算术格式,其中存在与“正零”不同(相等但不同)的“负零”值.这包括现在几乎无处不在的 IEEE 754 浮点标准。

cmath 中的分支切口与“标准”数学分支相匹配(例如,对于 asin,我们沿着正实轴从 1 到无穷大,沿着负实轴从 @987654328 @ 到负无穷大),并且像往常一样,实线的子区间 [-1, 1] 上的值与标准数学和 cmath 模块中通常的实数 asin 函数相匹配。鉴于此,远离分支切割的连续性迫使 cmath.asin 与所有地方的标准数学定义一致,除了可能分支切割。

在数学上,要将asin 扩展到分支切割,您需要在每个切割上选择是“从上方连续”还是“从下方连续”,而asin 的通常选择是从下方连续在[1, inf) 上并从上方连续在(-inf, -1] 上,这将为您提供您所期望的asin(10) 的第四象限结果。但是,如果您使用 IEEE 754 浮点,则会出现另一种选择:在asin 的分支切割上,参数的虚部始终为零。您现在可以使用零的 符号 来确定您将参数解释为位于分支切口的哪一侧。所以我们得到例如:

>>> from cmath import asin
>>> asin(complex(10.0, 0.0))  # 'top' of the branch cut
(1.5707963267948966+2.993222846126381j)
>>> asin(complex(10.0, -0.0))  # 'bottom' of the branch cut
(1.5707963267948966-2.993222846126381j)

这类似于大多数语言中实值atan2 函数的工作方式:通常,atan2(0.0, -1.0) 被定义为pi,而atan2(-0.0, -1.0) 被定义为-pi;零的符号是用来区分的。从数学上讲,它有点作弊,但它在浮点域中具有一些不错的特性。例如,对于所有z,我们得到asin(z.conjugate()) 可以与asin(z).conjugate() 互换,包括所有浮点特殊情况。象限上的行为是明确定义的(如果您再次使用相关的零符号确定每个象限的成员资格)。

关于您关于提供“标准”值的 Python 模块的问题,我不知道有一个问题,尽管@hpaulj 在 cmets 中提到了 SymPy。或者您可以捏造零符号以强制以正确的方式解释值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-26
    • 2015-03-17
    • 1970-01-01
    • 2018-12-06
    • 2022-01-19
    • 2018-07-19
    • 1970-01-01
    相关资源
    最近更新 更多