为什么 NaN^0 == 1?

67

受早期代码高尔夫的启发,为什么会出现:

>NaN^0
[1] 1
NA^0 等于1是有道理的,因为NA表示缺失数据,而任何数的零次方都等于1,包括-InfInf。然而NaN表示“非数字”,那么为什么会这样呢?当调用?NaN时,帮助页面甚至更加令人困惑/担忧:

在R中,基本上所有数学函数(包括基本算术)都应该能够正确处理+/-InfNaN作为输入或输出。

基本规则应该是,在涉及到Inf的运算中要考虑适当的数学极限值。

涉及到NaN的计算将返回NaN或者也许是NA:这两种情况没有保障,可能取决于R平台(因为编译器可能重新排列计算顺序)。

这背后是否存在哲学原因,还是只是与R如何表示这些常量有关?


我不知道R语言是怎么样的,但在我的机器上,Python也出现了同样错误的情况:1**nan返回1.0。 - hivert
6
在R语言中,符号“^”不仅仅是调用了C函数“pow”,它还会检查底数为1或指数为0的情况,如果其中有任何一种情况成立,它就会在调用“pow”之前返回1.。具体代码如下:if((x1 = INTEGER(s1)[i1]) == 1 || (x2 = INTEGER(s2)[i2]) == 0); REAL(ans)[i] = 1.; - Simon O'Hanlon
3
我并不确定"NA^0 == 1"是否有太多意义,因为"Inf^0"是一个不定形式。也就是说,当被视为极限时,我们无法仅从这个形式确定原始极限的值。例如,随着n趋近于正无穷,"exp(n)^*(1/n)"趋近于e,但"n^(1/n)"趋近于1,尽管两者都看起来像"Infinity^0"。 - orizon
关于这个问题,我想说一句话:"任何数的0次方都等于1,包括-无穷和+无穷":对于-无穷和+无穷,值应该是NaN,因为它们是不确定的极限。当x趋近于0时,请考虑(1 + 1/x)^x。 - user1196549
6个回答

28

这在帮助页面中由?'NaN'引用。

"IEC 60559标准,也称为ANSI/IEEE 754浮点数标准。

http://en.wikipedia.org/wiki/NaN."

在那里你可以找到有关应该创建NaN的语句:

 "There are three kinds of operations that can return NaN:[5]
       Operations with a NaN as at least one operand.

根据您提到的注释,很可能是特定C编译器引起的。GNU C文档如下所述:

http://www.gnu.org/software/libc/manual/html_node/Infinity-and-NaN.html

"另一方面,NaN会影响任何涉及它的计算。除非计算无论用什么实数替换NaN都会产生相同的结果,否则结果将为NaN。"

因此,在编写代码时,GNU-C人员似乎有不同的标准。報告指出,ANSI / IEEE 754浮点标准的2008年版本建议这样做:

http://en.wikipedia.org/wiki/NaN#Function_definition

已经发表的标准并非免费。如果您拥有访问权限或资金,可以在此处查看:

http://ieeexplore.ieee.org/xpl/mostRecentIssue.jsp?punumber=4610933


1
我从帮助页面中添加了注释。(我当然不打算涵盖“所有可能的编译器”。)而且我会说,GNU-C编译器的当前行为与“注释”不一致。 - IRTFM
1
@BlueRaja-DannyPflughoeft C函数的等效函数是 pow。在 R 中,内置的指数函数 ^ 通过对传递给它的参数进行一些检查来调用 pow。在 R 中,NaN^0 等价于 \^`(NaN,0)。请参见我在 OP 下面的评论,其中包含在调用 pow` 之前执行的 R 源代码(用 C 写成)。我认为 DWin 对 R 相当熟悉。 - Simon O'Hanlon
1
@BlueRaja-DannyPflughoeft:我开始在'NaN'的帮助页面上寻找权威描述。它引导我到IEEE标准。R是用C语言编写的,因此似乎有可能像这样的边缘情况会在GNU-C编译器中的NaN与"^"的“通常”行为中被确定。答案有许多不同的味道,往往是历史性的,就像在这里看到的情况一样。 - IRTFM
1
它还说:“在R中,基本上所有的数学函数(包括基本的‘算术’)都应该能够正确地处理‘+/- Inf’和‘NaN’作为输入或输出。”所以我认为这是R中的一个错误 - 特别是,“NA ^ 0 == 1”绝对是错误的。 - hadley
1
@SimonO101 我不明白元素的类为什么有关系:NA_real_ ^ 0 是1,显然违反了通常的缺失值传播规则:NA_real_ * 0NA,而不是0。 - hadley
显示剩余12条评论

20

答案可以总结为“出于历史原因”。

看起来IEEE 754引入了两个不同的幂函数- powpowr,后者在OP情况下保留NaN,并且对于Inf^00^01^Inf也返回NaN,但最终后者被简要解释放弃了。

从概念上讲,我支持保留NaN,因为我从极限的角度来看待这个问题,但从便利性的角度来看,我认为当前的惯例稍微更容易处理,即使它们在某些情况下没有多少意义(例如,在所有操作都是实数的情况下,sqrt(-1)^0等于1几乎没有什么意义)。


1
我相信 IEEE754-2008 标准的最终版本确实同时具有 powpowr,以及 pown,用于将任意浮点数提高到整数幂。pow(qNaN, 0)pown(qNaN, 0) 被定义为 1powr(qNaN, 0) 触发无效操作异常,并在默认 FP 异常处理下返回 qNaN - Mark Dickinson
1
IEEE 754的历史非常有趣。NaN保留还有另一个优点(用于min/max或其他任何事情):NaN可能已经出现在先前的计算中,在其他情况下,它将给出可用的double值,可以进行比较/使用/... NaN然后被视为异常值,仅仅是一个错误(由于某种原因,如溢出,计算失败)。保留NaN允许最终至少看到某处存在错误,并且不会默默地给出错误答案。当发生错误时,Signaling NaNs也是捕获错误的一种方式。 - user1220978
“NaN保留”概念与“NA保留”几乎相同。因此,在数值计算中,NA和NaN始终被(你能找到一个例外吗?)等同/类比地处理。更多信息请参见下面的单独“回复”。关于 sqrt(-1)^0:这正是为什么 NaN^0 应该给出 1 的好例子:sqrt(-1 + 0i)^0 确实给出了 1 (+0i):sqrt(-1+0i)^0 == 1 确实是 TRUE - Martin Mächler
@MartinMächler,你没有理解重点。假设我定义了一个仅在集合[0,Inf)上定义且在其他地方未定义的函数:f = function(x) {if (x >= 0) x else NaN}。从任何意义上讲,f(-1)^0都不等于1,但是R会告诉你它是相等的。 - eddi
在这个例子中,没有你所说的 x - 我会在你的答案中加上一条评论,可能会让你更清楚。 - eddi
显示剩余2条评论

18

是的,我来晚了,但作为参与设计的R核心成员,让我回顾一下我之前评论的内容。在R中,保留NaN和保留NA可以“等效地”工作,因此如果您同意NA ^ 0应该给出1,则NaN ^ 0 |-> 1是一个结果。

确实(正如其他人所说),您应该阅读R的帮助页面而不是C或IEEE标准,以回答这些问题,并且SimonO101正确引用了

1 ^ y和y ^ 0始终为1

我非常确定我曾经积极参与过那个规则的制定(如果不是作者的话)。请注意,能够提供非NaN答案而不是NaN答案是“好的”,而不是“坏的”,也适用于其他编程语言中不同的情况。

这种规则的结果是更多的事情会自动正确地工作;在另一种情况下,R程序员将被迫自己进行更多的特殊处理。

或者换句话说,像上面的简单规则(在所有情况下返回非NaN)是一条好规则,因为它会在数学意义上传播连续性:lim_x f(x)= f(lim x)。我们有过一些情况,清楚地表明坚持上述“= 1”规则而不是传播NaN更有优势。就像我之前说的那样,sqrt(-1)^ 0也是这样一个例子,因为只要您扩展到复平面,1就是正确的结果。


哈哈,如果有人认为NaN^0不应该得到1,那么为什么会同意NA^0应该得到1呢?NANaN的超集。你把if-else的方向搞错了。 - eddi
好的,楼主确实说了这句话! - Martin Mächler
公平地说,你们两个都应该知道得更好。 - eddi
为什么我的总结性评论会被,而这个问题的答案只有我知道,因为我是共同实现者呢?天哪! - Martin Mächler
-1 是我给的,主要是因为你说这个选择是“好”的,但没有任何论据来支持它。 - eddi
显示剩余2条评论

5
这里有一种解释。来自Goldberg的内容:
在IEEE 754中,NaN通常被表示为具有指数e_max + 1和非零有效数字的浮点数。
因此,NaN是一个浮点数,但具有特殊含义。将一个数的幂设为零会将其指数设置为零,因此它将不再是NaN。
还要注意:
> 1^NaN
[1] 1

一个数的指数为零时,它的幂已经是一了。

那么,你的说法是他们想避免处理特殊情况?但是,浮点计算已经不得不处理由NaN(以及+/- Inf、+/- 0和非规格化数字)引起的许多特殊情况,所以... - BlueRaja - Danny Pflughoeft
1
如果NaN被表示为例如2,按照您的逻辑1+NaN将等于3。您不能从您选择的表示方式中得出某些结果“应该是什么”的结论。 - eddi

4
从概念上讲,NaN^0 == 1 的唯一问题在于零值可能以至少四种不同的方式出现,但IEEE格式对其中三种使用相同的表示形式。上述公式等于最常见的情况(其中之一),但对其他情况则不然。
顺便说一下,我认为可以识别四种情况:
- 字面零 - 无符号零:两个无法区分的数字之间的差异 - 正无穷小:匹配符号的两个数字的乘积或商,太小而无法与零区分。 - 负无穷小:两个相反符号的数字的乘积或商,太小而无法与零区分。
这些中的一些可能通过其他方式产生(例如,字面零可以作为两个字面零的和产生;通过将一个非常小的数字除以一个非常大的数字产生正无穷小等)。
如果浮点数能够识别上述内容,那么将NaN提高到字面零会有用,并且将其提高到任何其他类型的零会导致NaN;这样的规则将允许在许多情况下假定常量结果,在这些情况下,编译器可以将可能是NaN的东西提高到编译器可以识别为常量零的东西,而这种假设不会改变程序语义。否则,我认为问题在于大多数代码并不关心如果xNaNx^0可能会是NaN,而且没有必要让编译器添加代码来处理代码不关心的条件。请注意,问题不仅是计算x^0的代码,还包括基于此的任何计算,如果x^0是常量,则这些计算将是常量。

NaN通常用于指示结果不在实域中,例如sqrt(-1)。在您的自定义函数中,它可能是比复数更奇特的东西,甚至可能没有为其定义^运算符,在这种情况下,您正在查看哪种类型的零是无关紧要的。 - eddi
@eddi:如果将x^literalZero的语义定义为“忽略x并返回1”,那么sqrt(-1)^0的正确结果应该是1。有人可能会质疑这是否是定义指数运算符的最理想方式,但我认为语言应该避免定义需要额外代码处理的边角情况。顺便问一下,您使用的语言是否区分右操作数为整数和浮点数的情况?在某些语言中,(-2.0)^2等于4.0,但(-2.0)^(2.0)是无效的。如果零是一个整数... - supercat
@eddi:你的意思是f_real(-1)^ 0还是f_real ^ 0.0?我认为从数学上讲,f(x)^ N可以定义为{当N = 0时为1,当N> 0时为(f(x)^(N-1))* f(x),当N <0时为(f(x)^(N + 1))}。因此,它要求函数被评估abs(N)次;由于数学没有副作用函数的概念,评估函数abs(N)次等价于仅评估一次,前提是只关心使用结果发生了什么。请注意,我的异议仅适用于指数为“整数”零的情况。顺便说一下,我之前错误地认为IEEE不... - supercat
定义一个整数N的幂函数。实际上,它定义了三个幂函数,其中一个假定一个整数N并指定报告NaN ^ 0为1.0; 其他使用实数N,并在NaN ^ 0.0的行为上有所不同。好奇的是,他们定义了两种形式的real ^ real,但没有定义两种比较形式,考虑到有许多情况下,==和!=运算符最重要的特征是它们应该定义等价关系。 - supercat
无论您想将 f_real(-1) 提升到多少次幂,包括 0,不管您称其为 0.0 还是 10,这都是完全无关紧要的。在所有情况下,答案都应该是相同的 - 应该是 NaN。您希望定义并随意执行的 "提升到整数 0 的幂" 操作仅对实数有定义(对于某些其他自定义类,它可能会被定义,但会产生不同的结果),而这就是 NaN 在这里的含义以及 R 出错的原因。重申一遍 - 您正在定义的操作仅对实数有定义,而 NaN 不仅限于实数。 - eddi
显示剩余2条评论

-1

如果你看一下NaN的类型,它仍然是一个数字,只是不是可以用数值类型表示的特定数字。

编辑:

例如,如果你计算0/0,结果是什么?如果你试图在纸上解决这个方程,你会卡在第一个数字上,有多少个零适合另一个零?你可以放0,你可以放1,你可以放8,它们都适合0*x=0,但是无法知道哪一个是正确的答案。然而,这并不意味着答案不再是一个数字,只是不能被表示为数字。

无论如何,任何数字,甚至是你无法表示的数字,乘以零次幂仍然是1。如果你分解一些数学x^8 * x^0可以进一步简化为x^(8+0),等于x^8x^0去哪了?如果x^0 = 1,那么方程x^8 * 1解释了为什么x^0就消失了。


1
@H2CO3 我知道你知道。只是开个无害的玩笑。 - Daniel Fischer
@DanielFischer 这是允许的,我绝不反对 :) - user529758
1
这个答案(特别是编辑部分)完全是胡说八道,与数学毫无关系。 - eddi
3
我希望有人能够写出一个回答,直接说明“因为R遵循X标准,而标准就是这样规定的”,这样我们就可以一起点赞并结束这个话题了。 - joran
3
我不确定 R 是否遵循任何标准,但在此领域中压倒性的标准是 IEEE 754。根据第9.2.1节所述:“对于任何x(包括零、quiet NaN或无穷大),pow(x,±0)为1”。从标准的措辞上来看,我不太清楚这是一个建议还是必须遵守的要求。 - Mark Dickinson
显示剩余11条评论

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