为啥 typeof NaN 返回“数字”?
Posted
技术标签:
【中文标题】为啥 typeof NaN 返回“数字”?【英文标题】:Why does typeof NaN return 'number'?为什么 typeof NaN 返回“数字”? 【发布时间】:2011-02-17 14:22:53 【问题描述】:只是出于好奇。
typeof NaN
是数字似乎不太合乎逻辑。顺便说一下,就像 NaN === NaN
或 NaN == NaN
返回 false 一样。这是 javascript 的特性之一,还是有原因?
编辑:感谢您的回答。不过,要吸引人并不是一件容易的事。阅读答案和维基,我理解得更多,但仍然像
这样的句子与 NaN 的比较总是返回一个无序的结果,即使与它自身进行比较也是如此。比较谓词是信令或非信令,信令版本表示此类比较的无效异常。等式和不等式谓词是无信号的,因此 x = x 返回 false 可用于测试 x 是否是安静的 NaN。
只是让我头晕目眩。如果有人可以将其翻译成人类(而不是数学家)可读的语言,我将不胜感激。
【问题讨论】:
+1: "NaN 是一个数字但不是一个数字。嗯...什么?!" 为了额外的乐趣; (NaN !== NaN) == 真 更有趣(但想想也可以理解,isNaN 返回一个布尔值): isNaN(parseInt('nodice')) === isNaN(parseInt('someOtherNaN')) ===是的; 如果使用jQuery,我更喜欢isNumeric
而不是检查类型:$.isNumeric(NaN);
返回false,而$.type(NaN);
返回数字。 api.jquery.com/jQuery.isNumeric
作为专业数学家,我必须说这句话与精确的数学语言几乎没有共同之处。
【参考方案1】:
嗯,将“非数字”视为数字可能有点奇怪,但NaN
仍然是数字类型,尽管如此 :-)
NaN
只是表示特定值不能在数字类型的限制范围内表示(尽管对于所有必须四舍五入的数字都可以这么说,但NaN
是一种特殊情况)。
一个特定的NaN
不被认为等于另一个NaN
,因为它们可能是不同的值。但是,NaN
仍然是数字类型,就像 2718 或 31415 一样。
至于你更新的问题,用外行的话来解释:
与 NaN 的比较总是返回无序的结果,即使与它自身进行比较也是如此。比较谓词是信令或非信令,信令版本表示此类比较的无效异常。等式和不等式谓词是无信号的,因此 x = x 返回 false 可用于测试 x 是否为安静的 NaN。
所有这些意思是(分解成部分):
与 NaN 的比较总是返回无序的结果,即使与它自身比较也是如此。
基本上,NaN
不等于任何其他数字,包括另一个 NaN
,甚至包括自身。
比较谓词是信号或非信号,信号版本表示此类比较的无效异常。
尝试在NaN
和另一个数字之间进行比较(小于、大于等)操作可能会导致抛出异常(信号)或结果为假(非信号或安静)。
等式和不等式谓词是无信号的,因此 x = x 返回 false 可用于测试 x 是否为安静的 NaN。
相等测试(等于,不等于)永远不会发出信号,因此使用它们不会导致异常。如果您有一个常规号码x
,那么x == x
将始终为真。如果x
是NaN
,那么x == x
将始终为假。它为您提供了一种轻松(安静地)检测NaN
的方法。
【讨论】:
很好的解释,虽然我不同意最后两句话:检查 x 是否为 NaN 的更好方法是使用 isNaN() 函数 @DominicRodger 我是这么想的:typeof a === 'number'
表示“a 在内部存储为 IEEE 754 浮点数”
如果Infinity
可以由不同的值产生:为什么Infinity === Infinity
返回true
:1.0 / 0.0 或2.0 / 0.0?
@Hashem,很可能是因为它们被认为是相同的无穷大。将除法视为重复减法,无论您从 2 还是从 1 开始都没有区别,达到(或者更准确地说,不达到)零所需的步数相同。我了解数学大师确实有不同类的无穷大,但(1)我怀疑1/0
和2/0
属于同一类,(2)IEEE754 中只有一类无穷大(除了+/-
当然)。
我不知道以任何有意义的方式定义这些特殊的“实际数字”。在数学中,负数的对数和根只能通过将实数扩展到复数来获得,在复数中它们评估为多个值,并且0/0
没有以任何有意义的方式定义,除了说它的“值”是整组数字。即使它们被定义,Math.log(-1) == Math.log(-1)
仍然评估为false
。因此,除了NaN
之外,不仅没有“实际数字”,即使有,也没有用于比较。【参考方案2】:
这意味着不是一个数字。这不是 javascript 的特性,而是常见的计算机科学原理。
来自http://en.wikipedia.org/wiki/NaN:
共有三种操作 返回 NaN:
以 NaN 作为至少一个操作数的操作
不确定的形式
0/0、∞/∞、∞/-∞、-∞/∞和-∞/-∞等分法 乘法 0×∞ 和 0×−∞ 1^∞ 的幂 ∞ + (−∞)、(−∞) + ∞ 加法和等效减法。具有复杂结果的实际操作:
负数的平方根 负数的对数 90 度(或 π/2 弧度)的奇数倍的正切 小于 -1 或的数的反正弦或余弦 大于 +1。
所有这些值可能不同。一个对 NaN 的简单测试是测试 value == value
是否为假。
【讨论】:
一个更简单的测试是isNaN(value)
@Alsciende 但它并不等同。 isNaN(undefined)
返回 true
,但 undefined == undefined
也是如此。除null
之外的所有其他非数字类型也是如此。
换句话说,value !== value
可能是测试value
是否真的是NaN
的最短方法。
看来你是对的,@Andy。现在这是一个特点。
短语“这些值可能不相同”没有任何意义,因为这些值不存在。【参考方案3】:
ECMAScript (JavaScript) 标准规定 Numbers
是 IEEE 754 浮点数,其中包括 NaN
作为可能值。
ECMA 262 5e Section 4.3.19:数值
与双精度 64 位二进制格式 IEEE 754 值对应的原始值。
ECMA 262 5e Section 4.3.23: NaN
数字值是 IEEE 754“非数字”值。
***上的IEEE 754
IEEE 浮点算术标准是由电气和电子工程师协会制定的技术标准,是浮点计算中使用最广泛的标准 [...]
标准定义
算术格式:二进制和十进制浮点数据集,由有限数(包括有符号零和次正规数)、无穷大和特殊“非数字”值 (NaN) 组成[...]
【讨论】:
【参考方案4】:typeof NaN
返回'number'
因为:
ECMAScript 规范说 Number 类型包括 NaN:
4.3.20 Number type
所有可能的数字值的集合,包括特殊的“非数字” (NaN) 值、正无穷和负无穷
所以typeof
相应地返回:
11.4.3 The typeof Operator
产生式 UnaryExpression :
typeof
UnaryExpression 是 评价如下:令 val 为计算 UnaryExpression 的结果。 如果Type(val) 是Reference,那么
如果IsUnresolvableReference(val) 为true,则返回
根据表 20 返回由Type(val) 确定的字符串。 "undefined"
。 设 val 为 GetValue(val)。Table 20 — typeof Operator Results ================================================================== | Type of val | Result | ================================================================== | Undefined | "undefined" | |----------------------------------------------------------------| | Null | "object" | |----------------------------------------------------------------| | Boolean | "boolean" | |----------------------------------------------------------------| | Number | "number" | |----------------------------------------------------------------| | String | "string" | |----------------------------------------------------------------| | Object (native and does | "object" | | not implement [[Call]]) | | |----------------------------------------------------------------| | Object (native or host and | "function" | | does implement [[Call]]) | | |----------------------------------------------------------------| | Object (host and does not | Implementation-defined except may | | implement [[Call]]) | not be "undefined", "boolean", | | | "number", or "string". | ------------------------------------------------------------------
此行为符合IEEE Standard for Floating-Point Arithmetic (IEEE 754):
4.3.19 Number value
对应于双精度 64 位二进制的原始值 格式化 IEEE 754 值
4.3.23 NaN
数字值是 IEEE 754 “非数字”值
8.5 The Number Type
Number 类型正好有 18437736874454810627(即 253−264+3) 值,表示双精度 64 位格式 IEEE 754 二进制浮点的 IEEE 标准中指定的值 算术,除了 9007199254740990(即 253-2)不同 IEEE 标准的“非数字”值表示为 ECMAScript 作为单个特殊的 NaN 值。 (注意 NaN 值 由程序表达式
NaN
产生。)
【讨论】:
【参考方案5】:NaN != NaN
因为它们不需要相同的非数字。因此,这很有意义...
还有为什么浮点数的 +0.00 和 -0.00 都不一样。舍入可能会导致它们实际上不为零。
至于 typeof,这取决于语言。而且大多数语言会说 NaN 是浮点数、双精度数或数字,具体取决于它们的分类方式……我知道没有任何语言会说这是未知类型或 null。
【讨论】:
ehr,考虑:var x = parseInt('no dice'), y = x;现在我会说两个 NaN 完全一样?但是不,x === y 也返回 false。 是的,但你不能确定,因此它们不一样。它与数据库中的 NULLable 逻辑相同。尽管许多人认为它们与其他编程语言中的空指针相同,但实际上它们具有完全不同的语义。它们是“未知的”,因此一个 NULL 值与另一个相比总是错误的。对 NULL 值进行计算最终结果为 NULL。尝试从被称为 UNKNOWN 的值的角度来查看它 作为number
类型,NaN
是原始的,因此由其值唯一确定。【参考方案6】:
NaN 是一个有效的浮点值 (http://en.wikipedia.org/wiki/NaN)
and NaN === NaN 为假,因为它们不一定是相同的非数字
【讨论】:
对不起,但我不得不说这不是一个好的思考方式。 “不一定相同的非数字”并不意味着它们总是不同,并且比较它们应该产生错误。最好不要量化 NaN,而只是将其视为我们知识库中的一个奇怪现象。 那么为什么所有Infinity
s 都是一样的呢?有什么想法吗?【参考方案7】:
NaN
代表Not a Number。它是数值数据类型(通常是浮点类型,但不总是)的值,表示除以零等无效操作的结果。
虽然它的名字说它不是数字,但用来保存它的数据类型是数字类型。所以在 JavaScript 中,询问NaN
的数据类型将返回number
(正如alert(typeof(NaN))
清楚地展示的那样)。
【讨论】:
实际上除以零被评估为Infinity
而不是NaN
【参考方案8】:
Javascript 使用 NaN 来表示它遇到的任何其规范无法以其他方式表示的事物。这并不意味着它不是一个数字。这只是描述相遇的最简单的方式。 NaN 表示它或引用它的对象不能用 javascript 以任何其他方式表示。出于所有实际目的,它是“未知的”。作为“未知”,它不能告诉你它是什么,即使它是它自己。它甚至不是分配给它的对象。它只能告诉你它不是什么,而不是或虚无只能用编程语言在数学上描述。由于数学是关于数字的,javascript 将虚无表示为 NaN。这并不意味着它不是一个数字。这意味着我们无法以任何其他有意义的方式阅读它。这就是为什么它甚至不能与自己相等。因为没有。
【讨论】:
【参考方案9】:NaN
的更好名称是数值异常,可以更准确、更简洁地描述其含义。它实际上是另一种伪装成具有原始类型的异常对象(通过语言设计),同时在错误的自我比较中它不被视为原始类型。混乱从何而来。而且只要语言“不会下定决心”在适当的异常对象和原始数字之间进行选择,混乱就会继续存在。
NaN
与 ==
和 ===
之间臭名昭著的不相等性是强制此异常对象成为原始类型的令人困惑的设计的表现。这打破了原语由其值唯一确定这一基本原则。如果NaN
更倾向于被视为例外(其中可以有不同的种类),那么它不应该作为原始“出售”。如果它想要是原始的,那么这个原则就必须成立。只要它被破坏,就像我们在 JavaScript 中那样,并且我们无法在两者之间做出真正的决定,导致每个相关人员不必要的认知负担的混乱就会继续存在。但是,只需在两者之间做出选择,就很容易解决这个问题:
NaN
成为一个特殊的异常对象,其中包含有关异常如何产生的有用信息,而不是将这些信息作为当前实现的内容丢弃,从而导致代码更难调试;
或将NaN
设为基本类型number
的实体(可以不那么容易混淆地称为“数字”),在这种情况下,它应该等于自身并且不能包含任何其他信息;后者显然是次等选择。
将NaN
强制转换为number
类型的唯一可想象的优势是能够将其重新转换为任何数值表达式。然而,这使它成为一个脆弱的选择,因为任何包含NaN
的数值表达式的结果要么是NaN
,要么导致不可预测的结果,例如NaN < 0
评估为false
,即返回boolean
而不是保留异常。
即使“事情就是这样”,也没有什么能阻止我们为自己做出明确的区分,以帮助我们的代码更可预测和更容易调试。在实践中,这意味着识别这些异常并将它们作为异常处理。不幸的是,这意味着更多的代码,但希望会被 Flowtype 的 TypeScript 等工具减轻。
然后我们有凌乱的quiet vs noisy aka signalling NaN
distinction。这实际上是关于如何处理异常,而不是异常本身,与其他异常没有什么不同。
同样,Infinity
和 +Infinity
是出现在 extension of the real line 中的数字类型的元素,但它们不是实数。在数学上,它们可以用收敛到+
或-Infinity
的实数序列来表示。
【讨论】:
【参考方案10】:这仅仅是因为NaN
是JS中Number对象的一个属性,与它是一个数字无关。
【讨论】:
与其他所有对象一样,Number 可以具有任何类型的属性。Number.fu = "bar"; alert(typeof Number.fu);
NaN
不是存储在Number.NaN
中的值,不管它是什么。 NaN
是 Number 类型的原始值。此外,Number.NaN
的值是NaN
,但这无关紧要。【参考方案11】:
考虑 NAN 的最佳方式是它不是一个已知数。这就是为什么 NAN != NAN 因为每个 NAN 值都代表一些唯一的未知数。 NAN 是必要的,因为浮点数的值范围有限。在某些情况下,舍入会发生在低位丢失的情况下,这会导致看起来像 1.0/11*11 != 1.0 这样的废话。真正更大的值是无穷大的 NAN 就是一个很好的例子。
鉴于我们只有十根手指,任何显示大于 10 的值的尝试都是不可能的,这意味着这些值必须是 NAN,因为我们已经失去了大于 10 值的真实值。浮点值也是如此,其中的值超出了浮点数的限制。
【讨论】:
无穷大不是由 NaN 表示的。尝试表示超出范围的数字将向下舍入(到 max/-inf)或向上(到 min/+inf)。【参考方案12】:NaN 仍然是数字类型,但它表示的值不能表示有效数字。
【讨论】:
【参考方案13】:因为 NaN 是数值数据类型。
【讨论】:
【参考方案14】:NaN
从类型的角度来看是一个数字,但不是像 1、2 或 329131 这样的普通数字。“Not A Number”这个名称指的是值表示是特殊的,是关于 IEEE 格式规范域,而不是 javascript 语言域。
【讨论】:
【参考方案15】:如果使用 jQuery,我更喜欢 isNumeric
而不是检查类型:
console.log($.isNumeric(NaN)); // returns false
console.log($.type(NaN)); // returns number
http://api.jquery.com/jQuery.isNumeric/
【讨论】:
谢谢伙计。我遇到了来自util
Typescript 包的 isNumber
的问题。很好,我们在项目中仍然使用jQuery
,所以改用你的建议。
对于rec,typescript的util
中的isNumber
也为NaN
返回true
。【参考方案16】:
Javascript 只有一种数字数据类型,即标准的 64 位双精度浮点数。一切都是双重的。 NaN 是 double 的一个特殊值,但它仍然是一个 double。
parseInt
所做的只是将您的字符串“转换”为数字数据类型,因此结果是 always "number";仅当原始字符串不可解析时,其值为 NaN。
【讨论】:
【参考方案17】:我们可以说 NaN 是一个特例对象。在这种情况下,NaN 的对象表示一个没有数学意义的数字。数学中还有一些其他特殊情况对象,例如 INFINITE 等。
你仍然可以用它做一些计算,但这会产生奇怪的行为。
更多信息在这里:http://www.concentric.net/~ttwang/tech/javafloat.htm(基于 java,不是 javascript)
【讨论】:
【参考方案18】:你必须喜欢 Javascript。它有一些有趣的小怪癖。
http://wtfjs.com/page/13
如果你停下来从逻辑上解决它们,或者如果你对数论有所了解,大多数这些怪癖都可以解释,但如果你不了解它们,它们仍然可以抓住你。
顺便说一句,我建议阅读http://wtfjs.com/ 的其余部分——还有比这个更有趣的怪癖!
【讨论】:
【参考方案19】:值 NaN 实际上是 Number.NaN 因此当您询问它是否是数字时,它会说是。您通过使用 isNaN() 调用做了正确的事情。
有关信息,NaN 也可以通过对未定义的数字的操作返回,例如除以零或负数的平方根。
【讨论】:
在什么意义上它是“价值”?NaN == Number.NaN
计算结果为 false
!
@DmitriZaitsev 你读过帖子吗?你试过 if (parseInt("nan") == Number.NaN) 吗?也试试 != 看看它告诉你什么。
对不起,忘记了愚蠢的NaN==NaN
是false
,一定是一个虐待狂发明了这个让每个人都受苦。【参考方案20】:
它是数字类型的特殊值,如 POSITIVE_INFINITY
为什么?按设计
【讨论】:
【参考方案21】:一个例子
假设我们正在将字符串转换为数字:
Number("string"); // returns NaN
我们将数据类型更改为数字,但它的值不是数字!
【讨论】:
您似乎错过了问题的重点。NaN
属于 number 类型。问题是问为什么。
@Quentin 我在最后一行解释了。以上是关于为啥 typeof NaN 返回“数字”?的主要内容,如果未能解决你的问题,请参考以下文章