为什么(inf + 0j)* 1计算为inf + nanj?

时间:2019-09-20 16:15:51

标签: python nan ieee-754

>>> (float('inf')+0j)*1
(inf+nanj)

为什么?这在我的代码中造成了一个讨厌的错误。

为什么1不是(inf + 0j),而是赋予{{1}}?

4 个答案:

答案 0 :(得分:92)

首先将1转换为复数1 + 0j,然后将其转换为inf * 0乘积,从而得到nan

(inf + 0j) * 1
(inf + 0j) * (1 + 0j)
inf * 1  + inf * 0j  + 0j * 1 + 0j * 0j
#          ^ this is where it comes from
inf  + nan j  + 0j - 0
inf  + nan j

答案 1 :(得分:30)

从机械上讲,公认的答案当然是正确的,但我认为可以给出更深的答案。

首先,将问题澄清为 @PeterCordes在评论中确实如此:“是否存在用于 确实适用于inf + 0j的复数?” 或换句话说,什么是OP 看到计算机实现复杂乘法的弱点,或者 inf+0j

在概念上有些不完善

简短答案:

使用极坐标,我们可以将复数乘法视为缩放和旋转。即使将无限个“手臂”旋转0度(如乘以1的情况),我们也无法期望将其尖端以有限的精度放置。 因此,确实存在inf+0j根本不正确的东西,即 一旦我们达到无穷远,有限的偏移就变得毫无意义。

长答案:

背景:这个问题所围绕的“大事情”就是问题 扩展数字系统(考虑实数或复数)。一个理由 一个人可能想做的就是添加一些无限的概念,或者 如果碰巧是数学家,则“压缩”。还有其他 原因(https://en.wikipedia.org/wiki/Galois_theoryhttps://en.wikipedia.org/wiki/Non-standard_analysis),但我们对此处的内容不感兴趣。

单点压实

关于这种扩展的棘手之处当然是我们想要这些新的 数字以适合现有的算法。最简单的方法是添加一个 无穷大的单个元素 (https://en.wikipedia.org/wiki/Alexandroff_extension),并使其等于零除以零。这适用于 实数(https://en.wikipedia.org/wiki/Projectively_extended_real_line)和复数(https://en.wikipedia.org/wiki/Riemann_sphere)。

其他扩展名...

虽然单点压缩是简单的并且在数学上是合理的,但是已经寻求了包括多个无限量的“更丰富”的扩展。实际浮点数的IEEE 754标准具有+ inf和-inf(https://en.wikipedia.org/wiki/Extended_real_number_line)。看起来 自然而直接,但已经迫使我们跳了圈 发明诸如-0 https://en.wikipedia.org/wiki/Signed_zero

之类的东西

...复杂平面的

复杂平面的不止一个inf扩展如何?

在计算机中,复数通常是通过将两个fp实数放在一起而实现的,一个实数,一个虚数部分。只要一切都是有限的,那是完全可以的。但是,一旦考虑到无限性,事情就会变得棘手。

复杂平面具有自然的旋转对称性,这与复杂的运算法则很好地结合在一起,因为整个平面乘以e ^ phij与围绕0的绕弧旋转相同。

那个附件G物

现在,为了简单起见,复杂的fp仅使用基础实数实现的扩展名(+/- inf,nan等)。这种选择似乎很自然,甚至没有被认为是一种选择,但让我们仔细看看它的含义。复杂平面的这种扩展的简单可视化效果类似于(I =无限,f =有限,0 = 0)

I IIIIIIIII I

I fffffffff I
I fffffffff I
I fffffffff I
I fffffffff I
I ffff0ffff I
I fffffffff I
I fffffffff I
I fffffffff I
I fffffffff I

I IIIIIIIII I

但是由于真正的复平面是尊重复数乘法的平面,因此会有更多信息的投影

     III    
 I         I  
    fffff    
   fffffff   
  fffffffff  
I fffffffff I
I ffff0ffff I
I fffffffff I
  fffffffff  
   fffffff   
    fffff    
 I         I 
     III    

在此投影中,我们看到无限大的“不均匀分布”不仅丑陋,而且是OP这类问题的根源:大多数无限大(形式(+/- inf,有限)和(有限度(+/- inf)集中在四个主要方向上,所有其他方向仅由四个无限度(+/- inf,+ -inf)表示。将复数乘法扩展到此几何体并不奇怪是一场噩梦。

C99规范的附件G尽其最大努力,包括制定关于infnan如何交互(本质上是inf胜过nan)的规则。 OP的问题是通过不将实数和纯虚构类型提倡为复数来避开的,但是让实数1与复数1的行为不同并不能解决我的问题。可以说,附件G没有充分说明两个无限性的乘积应该是什么。

我们能做得更好吗?

尝试通过选择更好的无限性几何来尝试解决这些问题。类似于扩展的实线,我们可以为每个方向添加一个无穷大。此构造类似于投影平面,但不会将相反的方向聚集在一起。 无穷将以极坐标inf x e ^ {2 omega pi i}表示, 定义产品将很简单。特别是OP的问题将很自然地解决。

但这是好消息结束的地方。在某种程度上,我们可以不拘一格地(而不是无理地)要求我们的新式无限性支持提取其实部或虚部的函数。加法是另一个问题。添加两个非对映的无穷大,我们必须将角度设置为未定义,即nan(一个人可能会说该角度必须位于两个输入角度之间,但是没有简单的方式来表示“部分南极”)< / p>

里曼来营救

鉴于所有这些,也许最好的做法是老旧的一点压实。也许附录G的作者在强制将所有无限性集中在一起的函数cproj时有同样的感觉。


a related question在此主题上比我更有能力的人回答。

答案 2 :(得分:6)

这是在CPython中如何实现复杂乘法的实现细节。与其他语言(例如C或C ++)不同,CPython采用了一种较为简单的方法:

  1. 整数/浮点数被乘以复数
  2. 简单的school-formula is used,一旦涉及到无限数,它就不会提供期望/预期的结果:
Py_complex
_Py_c_prod(Py_complex a, Py_complex b)
{
    Py_complex r;
    r.real = a.real*b.real - a.imag*b.imag;
    r.imag = a.real*b.imag + a.imag*b.real;
    return r;
}

上述代码的一个有问题的情况是:

(0.0+1.0*j)*(inf+inf*j) = (0.0*inf-1*inf)+(0.0*inf+1.0*inf)j
                        =  nan + nan*j

但是,有人希望将-inf + inf*j作为结果。

在这方面,其他语言并不遥不可及:复数乘法长期以来一直不是C标准的一部分,仅作为附录G包含在C99中,该附录G描述了应如何执行复数乘法,但并非如此。就像上面的学校公式一样简单! C ++标准没有指定复杂乘法的工作方式,因此大多数编译器实现都回落到C实现上,这可能是C99兼容(gcc,clang)或不兼容(MSVC)。

对于上述“有问题”的示例,符合C99的实现(比学校公式高more complicated)会给(see live)预期结果:

(0.0+1.0*j)*(inf+inf*j) = -inf + inf*j 

即使使用C99标准,也没有为所有输入定义明确的结果,即使对于符合C99的版本也可能有所不同。

在C99中未将float提升为complex的另一个副作用是将inf+0.0j1.01.0+0.0j相乘会导致不同的结果(请参见这里直播):

  • (inf+0.0j)*1.0 = inf+0.0j
  • (inf+0.0j)*(1.0+0.0j) = inf-nanj,虚部是-nan而不是nan(对于CPython)在这里不起作用,因为所有安静的nan都是等效的(请参阅this ),甚至其中一些设置了符号位(因此打印为“-”,请参见this),而另一些则没有。

至少是违反直觉的。


我的主要收获是:“简单”的复数乘法(或除法)并不简单,当在语言或什至是编译器之间切换时,人们必须为微妙的错误/差异做好准备。

答案 3 :(得分:3)

Python的有趣定义。如果我们用笔和纸解决此问题,我会说预期结果将是expected: (inf + 0j),正如您所指出的,因为我们知道我们指的是1的规范,所以(float('inf')+0j)*1 =should= ('inf'+0j)

但是事实并非如此……运行时,我们得到:

>>> Complex( float('inf') , 0j ) * 1
result: (inf + nanj)

Python将此*1理解为复数而不是1的范数,因此它解释为*(1+0j),并且当我们尝试将inf * 0j = nanj理解为{时,会出现错误{1}}无法解决。

您实际上想做什么(假设1是1的范数):

回想一下,如果inf*0是具有实部x和虚部y的复数,则z = x + iy的复共轭定义为z,并且绝对值也称为z* = x − iy定义为:

enter image description here

假设norm of z1的准则,我们应该做类似的事情:

1

我知道不是很直观...但是有时编码语言的定义方式与我们日常使用的方式不同。