NaN,一个不是数字的数字,但它又不是NaN。
NaN, the not-a-number number that isn't NaN

原始链接: https://piccalil.li/blog/nan-the-not-a-number-number-that-isnt-nan/

## 理解 JavaScript 中的 NaN 在 JavaScript 中,`NaN`(非数字)出现在算术运算不产生有效数字时。 关键是,*任何* 涉及 `NaN` 的数学运算都会产生 `NaN`,从而将错误传播到计算中。 与 `NaN` 的比较总是评估为 `false`,即使 `NaN !== NaN` 也是如此——这是因为 `NaN` 代表计算中断,而不是特定值。 这种独特行为源于 `NaN` 需要在计算*内部*充当错误标志。 为了避免意外结果(例如 `NaN / NaN` 等于 1),`NaN` 在技术上是一种数字类型,但它不等于自身。 检测 `NaN` 需要小心。 直接比较 (`=== NaN`) 会失败。 全局 `isNaN()` 函数检查一个值*是否可以强制转换为* `NaN`,而 `Number.isNaN()` 专门检查一个值*是否是* `NaN`,不进行强制转换。 在大多数情况下,`isNaN()` 适合于验证一个值是否可以在计算中使用,而 `Number.isNaN()` 最适合于明确识别计算错误。

## NaN:深入探讨“非数字” 这次Hacker News讨论围绕编程中NaN(“非数字”)的特殊行为,尤其是在IEEE 754浮点标准中。一个关键点是`NaN !== NaN`,意味着NaN不等于自身——这是为了防止计算中出现意外错误的设计选择。如果NaN等于自身,`NaN / NaN`可能会错误地解析为1,掩盖错误而不是标记它们。 对话探讨了这种看似违反直觉的行为存在的原因,涉及历史硬件限制以及对“有毒”错误传播系统的需求。不同的语言对NaN的处理方式不同;例如,Julia认为具有相同位表示的NaN相等。 参与者们讨论了替代方案,例如第三种布尔值(“NaB” - 非布尔值)或抛出错误,但最终承认该标准背后的原理。讨论还强调了浮点运算的复杂性、理解NaN在错误处理中的作用的重要性,以及语言设计中固有的权衡。
相关文章

原文

When NaN is included in a arithmetic expression, the result will always be NaN — that tracks, right? Anything math’d against the very concept of “not a number” can’t result in a number:


2 + NaN;


NaN - 50;


NaN / 0;


That means once any part of a calculation includes or results in NaN, the whole thing will result in NaN. As soon as NaN is in play, we can’t possibly end up with a number:


( 2 + 2 ) * 10 / ( "Ten" * 4 ) + 9;


Likewise, any comparison that uses NaN as one of the operands will evaluate to false, which certainly tracks in the same way — no value can be greater than, less than, or equal to what is effectively a placeholder for the concept of being a non-number result:

It follows that any individual value will be unequal to NaN, as those values either are numbers — thus not NaN — or aren’t evaluated as numbers, and thus not NaN.


100 !== NaN;


"String" !== NaN;


Now, here’s where it gets weird: that inequality extends to NaN itself. The way true represents the very essence of trueness, NaN represents a non-specific non-number result. NaN is the only value in the whole of JavaScript that isn’t equal to itself.


NaN == NaN;


NaN === NaN;


NaN !== NaN;


Now, the cheap explanation is “well, that’s because NaN is a number.”

By definition a number can’t be equal to the concept of not-a-number, sure, but NaN !== NaN goes much deeper than that, and well beyond JavaScript itself. Across the whole of computer programming, the concept of NaN is meant to represent a breakdown of calculation — the end result of any mathematical equation that comes to involve a NaN value, no matter how simple or complex it may be, must end in NaN. NaN is, in effect, an error state.

An operation that propagates a NaN operand to its result and has a single NaN as an input should produce a NaN with the payload of the input NaN if representable in the destination format.

— IEEE Std 754-2019, IEEE Standard for Floating-Point Arithmetic

We’re operating strictly in the realm of calculations, here. In order to function like an error in a calculation without itself causing wildly unpredictable results in that calculation, NaN has to behave like a number. That’s why NaN is a number.

That’s also the reason NaN !== NaN. If NaN behaved like a number and had a value equal to itself, well, you could accidentally do math with it: NaN / NaN would result in 1, and that would mean that a calculation containing a NaN result could ultimately result in an incorrect number rather than an easily-spotted “hey, something went wrong in here” NaN flag.

Now, as you might imagine, this makes it tricky to determine whether an expression has evaluated to NaN. Say I want to multiply the value assigned to a given identifier by ten only if that value is a number — I might write the following, thinking “well, if it isn’t NaN, it must be a number, so do math to it; otherwise, do something else:“

Try it out


let theValue = "String";

if ( theValue != NaN ) {
  console.log( theValue * 10 );
} else {
  console.log( "This isn't a number." );
}

No dice, because the simple expression "String" doesn’t evaluate to the concept of a non-number value in a the context of a calculation, it’s a string. It evaluates to a string. We might then think, “okay, fine, we’ll be explicit: do the math, and the result of that is equal to NaN, do this, else do the math:“

Try it out


let theValue = "String";

if ( theValue * 10 === NaN ) {
  console.log( "This isn't a number." );
} else {
  console.log( theValue * 10 );
}

Still no good. theValue * 10 does evaluate to NaN, but NaN isn’t equal to NaN.

Instead, we have a couple of options: there is, of course, using good old-fashioned typeof to see if we’re working with a number:

Try it out


let theValue = "String";

if ( typeof theValue === "number" ) {
  console.log( theValue * 10 );
} else {
  console.log( "This isn't a number." );
}

Reliable, though comparing strings in order to verify that something is a number has always felt a little clunky to me. Luckily, we can also make use of the global method isNaN — which has existed since the very first ECMAScript specification in 1997 — and the Number.isNaN method introduced in ES6.


isNaN( "Two" * 2 );


isNaN( 20 );


Number.isNaN( "Two" * 2 );


There is — I say with a depth of sigh that can only come from experience — a big difference between isNaN() and Number.isNaN(). If you’ve made it this far, though, you’re through the worst NaN has to offer. We’re in the home stretch here.

You can think of the global isNaN method as checking to see whether something is not a number, or perhaps more accurately, “if I tried to make you into a number, would that work, or would you end up being NaN?” An expression supplied to isNaN() will coerce the resulting value to a number, and if the result of that coercion is NaN, the method returns true:


isNaN( "Two" * 2 );


isNaN( 20 );


isNaN( "20" );


isNaN( "A string" );


You can think of the Number.isNaN method as checking to see whether something is the value NaN, just like it says on the tin. It doesn’t perform any coercion — it just checks to see whether NaN is the explicit result of the expression you’ve given it:


Number.isNaN( "Two" * 2 );


Number.isNaN( 20 );


Number.isNaN( "20" );


Number.isNaN( "A string" );


"Two" * 2 results in NaN, no two ways about it; both methods return true.

The number 20, being a number and all, is not NaN, nor does it evaluate to NaN; both methods return false.

The string "20" can be coerced to a number value, so isNaN returns false. Number.isNaN doesn’t try to coerce "20" to a number, but that value still isn’t NaN, it’s a string. false there too.

When isNaN tries to coerce "A string" to a number value, that results in NaN, so isNaN returns true. But once again: a string doesn’t evaluate to NaN in and of itself. Number.isNaN( "A string" ) returns false.

So for purposes of our snippet, the global isNaN is the tool for the job. If this can be evaluated to a number, it will be evaluated to a number when we attempt to multiply it by ten.

Try it out


let theValue = "Ten";

if ( isNaN( theValue ) ) {
  console.log( "This isn't a number." );
} else {
  console.log( theValue * 10 );
}

"Ten" can’t be coerced to a number, so this works as expected; just like the majority of my high school career, no math is attempted whatsoever. The string "10" can be coerced to the number value 10 though, and that works too:

Try it out


let theValue = "10";

if ( isNaN( theValue ) ) {
  console.log( "This isn't a number." );
} else {
  console.log( theValue * 10 );
}

If we ultimately wanted to check against an explicit NaN value without performing any coercion whatsoever — a use case arguably more in-line with the IEEE intent of NaN as a sort of error state — we’d want to use Number.isNaN instead:

Try it out


let theResult = "10" * 10;

if ( Number.isNaN( theResult ) ) {
  console.log( "The calculation hasn't resulted in a number." );
} else {
  console.log( theResult );
}

So there you have it: NaN, the number that means “not a number” is a number, but it isn’t NaN . And they say JavaScript is confusing.

Enjoyed this article? You can support us by leaving a tip via Open Collective

联系我们 contact @ memedata.com