我对著名的NaN的存在有疑问,我想知道,它是干什么用的?你的“存在意义”是什么?
到目前为止,根据我在书籍和视频中看到的内容,我唯一能够理解的是,总而言之,这将是数学运算中的一种错误指示器,通常会警告我们存在“无效”(例如将字符串乘以整数);此外,它的数据类型是数字,它的值是未定义的,并且不同于任何其他值。
我是对的?它用于其他用途吗?
我添加了几个引号:
NaN 的意思是“不是数字”。当 JavaScript 中的函数或数学运算无法返回特定数字时,它会返回值 NaN。它是全局对象的属性,并且是对 Number.NaN 的引用
JavaScriptNotes for Professionals - GoalKicker.com - 第 13 页。
它是一个特殊的静态数值属性,NaN,相当于全局 NaN,相当于 Not-a-Number。每当您尝试在无法解析为数字的数值运算中使用值时,都会出现 NaN 错误。
Javascript 食谱,Shelley Powers - 第 56 页
NaN 是 Not-a-Number 的缩写,代表非法数字。它使用 isNaN() 函数根据 ECMA-262 规范确定数字是否合法或有效。
JavaScript 一步一步 2ed。Steve Suehring - 第 63 页
问题的上下文仅就其在 JavaScript 中的应用而言,因为谈论一般的编程、算术、计算或其他主题会使问题变得更广泛,并且会变得有点模棱两可;另一方面,我理解“错误”、“消息”或“警报”的存在可能是合乎逻辑的,例如在操作无理数或将数学运算从数学“上下文”中取出时可能出现的情况”,但是,NaN与“典型”错误消息有何不同?
谁发明了它?
NaN不是 JavaScript 的发明。它是定义如何以二进制表示实数的IEEE-754规范的一部分。目前不同编程语言使用的所有实数实现都是基于这个规范,比如类型
float
(对应IEEE-754单精度使用32位表示实数)和double
(对应IEEE-754单精度) .扩展精度,它使用 64 位来表示 C 的实数。不仅 C,而且几乎所有当前的语言都使用这个标准,因为硬件本身(CPU 的数学处理器)使用它。此外,在 JavaScript 的情况下,该语言缺少整数类型(int
) 所以任何 JavaScript 数字实际上都是一个double
. 我们稍后会回到 JavaScript。现在让我们关注 IEEE-754。撇开范围和精度的问题不谈,这将是另一个问题,它涉及如何可能将所有实数(它们是无限的,实际上是比自然数的无限高阶的无限)仅放入32 位或 64 位(剧透,你不能 :-),这就是为什么会发生奇怪的事情)还有一个额外的问题是,某些数学运算是为实数的子集定义的,但不是全部。我将在这里重点讨论这个问题。
为什么有必要?
以分区为例。可以将任何两个实数相除,结果将是另一个实数,除非除数为零。在这种情况下,采用数学约定(有充分的理由我不会讨论)结果是“无穷大”(如果分子为负数,则为负无穷大)。
“无穷大”不是一个实数。这是一个概念。没有实数等于无穷大。但是,如果我们有一个返回类型为
float
or的“除法”运算double
,那么我们必须提供一个(32 或 64)位组合来表示“无穷大”(以及一个表示“负无穷大”)。IEEE-754 标准对此进行了规定。无限是不够的。还有其他数学运算没有明确的结果,因此说“输出无穷大”是没有意义的。例如,任何负数的平方根都没有实数结果。零的对数或任何负数也是如此。或者用任何不介于 0 和 1 之间的数的反正弦,等等。有许多仅针对实数子集定义的函数示例。尝试在不在该子集中的元素上评估它们应该被视为错误。
在支持异常的语言中,如 JavaScript、Python、Java 等。可以利用这种机制来发出问题的信号。事实上,例如 Python 就是这样做的,并且在尝试计算
math.log(-1)
异常时获得了ValueError
. 但是在其他更原始的语言如C或汇编中,没有例外,需要提供一定的位组合,类似于上面看到的无穷大的情况,表示该操作无效. 或者说“结果不是真的”。不是数字。南。IEEE-754 也提供了这一点,并且有一个代码(实际上有很多)来编码“不是数字”的概念。CPU 的数学处理器是它使用的机制,以及它作为非法操作的结果返回什么。C 函数也可以返回该值(因为它是
float
o类型的有效元素double
)。高级语言可以选择捕获这个结果并引发异常(如 Python 所做的那样),或者“让它通过”并返回 NaN(如 JavaScript 所做的那样)。它有什么二进制表示?
在 IEEE-754 单精度(32 位)中表示 NaN 的二进制代码是 x11111111xxxxxxxxxxxxxxxxxxxxxxxx,每个 x 是一个可以是 1 或 0 的位,除了最后 23 个 x 不能全部为 0(因为在这种情况下它将是代表无穷大,第一位是符号)。正如我们所看到的,该标准没有为 NaN 定义唯一的代码,但实际上我们有 2^(24)-2 种可能的表示。但是,它们都被认为是等价的,也就是说,从概念上讲,有一个可以用多种方式表示的“单个 NaN”。
在双精度 IEEE-754 的情况下,还有更多的可能性,因为在这种格式中,NaN 是 x11111111111xxxx...xxx 类型的任何位模式,其中我们有一个可以是 1 或 0 的第一位,另外 11 个位必须是 1,另外 52 位可以取任何值,只要它们不全为 0。使用这种模式,可以生成 2^(53)-2 个不同的代码。
为什么有这么多不同的代码来表示 NaN?事实是我不知道。只有一个会做。我的猜测是,该标准的设计者找不到任何其他方法来利用剩余的 2^(24)-1 [或双精度中的 2^(53)-1] 代码。
为什么是 NaN != NaN?
基本上是为了避免逻辑错误。如果你有,
f(x) == f(y)
你可能会这么想x == y
。这对于许多功能都是正确的。例如,平方根。如果Math.sqrt(a) == Math.sqrt(b)
有人能推断出来的话a == b
。事实上,如果例如Math.sqrt(a) == 5
和 相同Math.sqrt(b)
,我们可以推断它们a
值b
25。但是如果
a=-1
whileb=-4
呢?在这种情况下,Math.sqrt(a)
将Math.sqrt(b)
导致NaN
. 如果我们接受这一点NaN == NaN
,我们可能会得出错误的结论a==b
。因此,根据定义NaN
,它总是不同于NaN
(即使“下面”用相同的二进制代码表示)。事实上,一旦一个操作产生了
NaN
,它NaN
用作参数的任何其他操作都应该NaN
依次返回。如果我们允许NaN == NaN
这意味着它NaN/NaN
应该是 1,这将使得不可能在操作之间“传播”错误结果。稀有物品
NaN
float
如前所述,是(odouble
)类型的值。由于 JavaScript 是它拥有的唯一数字类型,它通常称为“数字”,因此它NaN
是一个有效的“数字”。这不是没有它的恩典。JavaScript
NaN
在超出标准预期的上下文中使用。例如,如果您尝试将一个数字除以一个字符串,您将得到一个错误,但其性质与Math.log(-1)
. 如果1/"cadena"
是打字错误。在这种情况下,甚至不会定义返回类型。JavaScript 决定结果是“数字”类型,因为它不能给它一个值,所以它给它一个值NaN
。最后,可以认为提出的辩护理由同样
NaN != NaN
适用于“无限”,因为如果a/0 == b/0
这并不意味着a == b
,而是 IEEE-754 标准不这样认为。我们可以用 JavaScript 检查它:NaN 还是异常?
可以说,尝试计算
Math.log(-1)
是应该引发异常的错误。一些语言通过检测结果是 NaN 来做到这一点。但是,设计 IEEE-754 标准的工程师更喜欢返回“特殊值”而不是生成硬件异常,因为这简化了当时的实现。我还坚持这样一个事实,即,随着 的行为的定义
NaN
,一旦产生了一个操作NaN
,任何其他使用先前结果的操作都将继续产生NaN
. 具体来说NaN - NaN = NaN
(另一个论点为什么NaN!=NaN
,因为比较通常在硬件级别通过减法实现)。这种“病毒”行为NaN
让人想起异常如何从引发它的函数向上传播到调用它的函数。也可能存在返回
NaN
比抛出异常更有用的情况。一个典型的例子是在一个函数中寻找零点,使用类似于牛顿的方法。问题是,给定一个函数,f(x)
找到一个x
为零f(x)
的值。牛顿法和其他类似的方法是基于尝试几个值,x
如果它们f(x)
不为零,则使用结果更好地“调整”下一次尝试。无需详细说明,我们可以看到,如果f(x)
没有为所有可能x
的情况定义它,那么我们可能会尝试x
未定义它的 a。抛出异常会中止方法,同时返回NaN
这将允许他继续尝试另一个x
。但实际上,这个理由对我来说似乎是站不住脚的。通过捕获异常并尝试另一个x
.NaN
必须从其历史根源中寻找其存在的真正原因。在设计时,这个解决方案更容易实现,只需对现有编译器和语言进行少量修改或无需修改。例如,当时(甚至现在)最重要的语言 C 没有例外,而是将其错误处理基于返回“特殊值”以指示存在错误的函数(如果返回的类型为 -1int
,NULL
如果返回类型是指针,NaN
如果返回类型是float
ordouble
)。JavaScript 似乎是这种潮流的继承者,因为
NaN
它不是这种语言选择返回“特殊”值而不是生成异常的唯一情况。例如,访问一个超出其边界的数组(在其他语言中会产生异常),在 JS 中会导致获得的值为undefined
:他是为了
NaN
什么?isNaN
NaN
,你已经知道该值为 null 或空字符串)知道这一点:
为什么他们返回假?
它们返回 false 因为空字符串
""
或 a的值null
被转换为0
所以我们可以对它们进行操作:
那么……他在哪里
NaN
?好吧,正如问题开头的引用中所说,如果我们在使用它们之前解析这些值,我们将获得
NaN
正确的检查方法
isNaN
是: