62

NaN, the not-a-number number that isn't NaN

> 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.

While I'm not really against the concept of NaN not equaling itself, this reasoning makes no sense. Even if the standard was "NaN == NaN evaluates to true" there would be no reason why NaN/Nan should necessarily evaluate to 1.

3 hours agoKalMann

Maybe the result of NaN === NaN should be neither true nor false but NaB (not a bool).

3 hours agoema

In R, NA (which is almost, but not quite like NaN) actually has separate types for each result, so you can have NA_boolean, NA_integer etc. Its super confusing.

3 hours agodisgruntledphd2

It is a minor nuisance, but I think there's ultimately a pretty good reason for it.

Old-school base R is less type-sensitive and more "do what I mean", but that leads to slowness and bugs. Now we have the tidyverse, which among many other things provides a new generation of much faster functions with vectorized C implementations under the hood, but this requires them to be more rigid and type-sensitive.

When I want to stick a NA into one of these, I often have to give it the right type of NA, or it'll default to NA_boolean and I'll get type errors.

2 hours agogetnormality

> When I want to stick a NA into one of these, I often have to give it the right type of NA, or it'll default to NA_boolean and I'll get type errors.

Yeah, I know. I hit this when I was building S4 classes, which are similarly type-strict.

Again, I think this was the right decision (pandas's decision was definitely not), but it was pretty confusing the first time.

an hour agodisgruntledphd2

NaN is, by definition, not equal to NaN because they’re not comparable, it does have a definitive Boolean representation - false is correct

an hour agomonkpit

The concept of NaN long predates the language that uses ===, and is part of a language-agnostic standard that doesn't consider other data types. Any language choosing to treat the equality (regardless of the operator symbol) of NaN differently would be deviating from the spec.

an hour agozahlman

Because NaN is defined as a number and two equal numbers divided by themselves equal 1

3 hours agoJohnMakin

It doesn't really matter if NaN is technically a number or not. I find the standard "NaN == NaN is true" to be potentially reasonable (though I do prefer the standard "NaN == Nan is false"). Regardless of what you choose NaN/NaN = 1 is entirely unacceptable.

an hour agoKalMann

> two equal numbers divided by themselves equal 1

That's not true. For example: 0 == 0, but 0/0 != 1.

(See also +Infinity, -Infinity, and -0.)

3 hours agombrubeck

If you're going to nitpick this comment, you should note that infinity isn't on the number line and infinity != infinity, and dividing by zero is undefined

2 hours agoJohnMakin

We're commenting on an article about IEEE 754 floating point values. Following the IEEE 754 standard, we have:

    >> isNaN(Infinity)
    false
    >> Infinity == Infinity
    true
    >> Infinity / Infinity == 1
    false

    >> isNaN(0)
    false
    >> 0 == 0
    true
    >> 0 / 0 == 1
    false
Also, you say NaN ("not a number") is "defined as a number" but Infinity is not. I would think every IEEE 754 value is either "a number" or "not a number". But apparently you believe NaN is both and Infinity is neither?

And you say 0 / 0 is "undefined" but the standard requires it to be NaN, which you say is "defined".

2 hours agombrubeck

Shouldn't an operator on incompatible types return undefined? ;)

Equality on things that it doesn't make sense to compare returning false seems wrong to me. That operation isn't defined to begin with.

By shipping with undefined, JavaScript could have been there only language whose type system makes sense... alas!

4 hours agoandai

My understanding is that the reasoning behind all this is:

- In 1985 there were a ton of different hardware floating-point implementations with incompatible instructions, making it a nightmare to write floating-point code once that worked on multiple machines

- To address the compatibility problem, IEEE came up with a hardware standard that could do error handling using only CPU registers (no software, since it's a hardware standard) - With that design constraint, they (reasonably imo) chose to handle errors by making them "poisonous" - once you have a NaN, all operations on it fail, including equality, so the error state propagates rather than potentially accidentally "un-erroring" if you do another operation, leading you into undefined behavior territory

- The standard solved the problem when hardware manufacturers adopted it

- The upstream consequence on software is that if your programming language does anything other than these exact floating-point semantics, the cost is losing hardware acceleration, which makes your floating-point operations way slower

4 hours agortfeldman

This is a matter of choice, not something with an objectively correct answer. Every possible answer has trade offs. I think consistency with the underlying standard defining NaN probably has better tradeoffs in general, and more specific answers can always be built on top of that.

That said, I don’t think undefined in JS has the colloquial meaning you’re using here. The tradeoffs would be potentially much more confusing and error prone for that reason alone.

It might be more “correct” (logically; standard aside) to throw, as others suggest. But that would have considerable ergonomic tradeoffs that might make code implementing simple math incredibly hard to understand in practice.

A language with better error handling ergonomics overall might fare better though.

3 hours agoeyelidlessness

>A language with better error handling ergonomics overall might fare better though.

So what always trips me up about JavaScript is that if you make a mistake, it silently propagates nonsense through the program. There's no way to configure it to even warn you about it. (There's "use strict", and there should be "use stricter!")

And this aspect of the language is somehow considered sacred, load-bearing infrastructure that may never be altered. (Even though, with "use strict" we already demonstrated that have a mechanism for fixing things without breaking them!)

I think the existence of TS might unfortunately be an unhelpful influence on JS's soundness, because now there's even less pressure to fix it than there was before.

3 hours agoandai

> And this aspect of the language is somehow considered sacred, load-bearing infrastructure that may never be altered. (Even though, with "use strict" we already demonstrated that have a mechanism for fixing things without breaking them!)

There are many things we could do which wouldn't break the web but which we choose not to do because they would be costly to implement/maintain and would expand the attack surface of JS engines.

39 minutes agorimunroe

NaN is just an encoding for "undefined operation".

As specified by the standard since its beginning, there are 2 methods for handling undefined operations:

1. Generate a dedicated exception.

2. Return the special value NaN.

The default is to return NaN because this means less work for the programmer, who does not have to write an exception handler, and also because on older CPUs it was expensive to add enough hardware to ensure that exceptions could be handled without slowing down all programs, regardless whether they generated exceptions or not. On modern CPUs with speculative execution this is not really a problem, because they must be able to discard any executed instruction anyway, while running at full speed. Therefore enabling additional reasons for discarding the previously executed instructions, e.g. because of exceptional conditions, just reuses the speculative execution mechanism.

Whoever does not want to handle NaNs must enable the exception for undefined operations and handle that. In that case no NaNs will ever be generated. Enabling this exception may be needed in any case when one sees unexpected NaNs, for debugging the program.

3 hours agoadrian_b

> Shouldn't an operator on incompatible types return undefined? ;)

NaN is a value of the Number type; I think there are some problems with deciding that Number is not compatible with Number for equality.

We just need another value in the boolean type called NaB, and then NaN == NaN can return NaB.

To complement this, also if/then/else should get a new branch called otherwise that is taken when the if clause evaluates to NaB.

3 hours agodragonwriter

"return undefined" is incoherent in almost every language, and IEEE754 predates JavaScript by a decade.

an hour agozahlman

JavaScript has also TypeError which would be more appropriate here. unfortunately undefined has never been used well and it's caused much more pain than it has brought interesting use cases

4 hours agoagos

It should return false, right? They are different types of thing, so they can’t be the same thing.

Or, maybe we could say that our variables just represent some ideal things, and if the ideal things they represent are equal, it is reasonable to call the variables equal. 1.0d0, 1.0, 1, and maybe “1” could be equal.

3 hours agobee_rider

>Shouldn't an operator on incompatible types return undefined? ;)

Please no, js devs rely too much on boolean collapse for that. Undefined would pass as falsy in many places, causing hard to debug issues.

Besides, conceptually speaking if two things are too different to be compared, doesn’t that tell you that they’re very unequal?

4 hours agokace91

Interesting. So, having a comparison between incomparable types result in false -- what we have now -- is functionally equivalent, in an if-statement, to having the undefined evaluate to false... with the difference that the type coercion is currently one level lower (inside the == operator itself).

It kind of sounds like we need more type coercion because we already have too much type coercion!

I'm not sure what an ergonomic solution would look like though.

Lately I'm more in favour of "makes sense but is a little awkward to read and write" (but becomes effortless once you internalize it because it actually makes sense) over "convenient but not really designed so falls apart once you leave the happy path, and requires you to memorize a long list of exceptions and gotchas."

3 hours agoandai

NaNs aren't always equal to each other in their bit representation either, most of the bits are kept as a "payload" which is not defined in the spec it can be anything. I believe the payload is actually used in V8 to encode more information in NaNs (NaN-boxing).

3 hours agojasonthorsness

You’ve opened a rabbit hole for me

3 hours agolovich

> 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,

So, by that logic, if 0 behaved like a number and had a value equal to itself, well, you could accidentally do math with it: 0 / 0 would result in 1...

But as it turns out, 0 behaves like a number, has a value equal to itself, you can do math with it, and 0/0 results in NaN.

2 hours agodragonwriter

I'm sometimes wondering if a floating point format really needs to have inf, -inf and nan, or if a single "non finite" value capturing all of those would be sufficient

39 minutes agoAardwolf

NaN comes from parsing results or Infinity occurring in operations. I personally ends up more to use Number.isFinite(), which will be false on both occurrences when I need a real (haha) numeric answer.

4 hours agotuyiown

Also remember that NaN is represented in multiple ways bitwise:

https://en.wikipedia.org/wiki/NaN

Also you even have different kinds of NaN (signalling vs quiet)

3 hours agonhatcher

console.log(new Array(16).join("wat"-1) + " Batman!")

5 hours agoZealotux

Opened Web Inspector in Safari and pasted the above. (I knew what to expect but did not know how it would work … me trying to figure out what subtracting 1 from a string (ASCII?) would give you. But very related to this post.)

4 hours agoJKCalhoun
[deleted]
4 hours ago

My gut reaction is that both NaN == NaN and NaN != NaN should be false, it to put it another way, NaN != NaN returns True was a surprise to me.

Does Numpy do the same? That’s where I usually meet NaN.

3 hours agowodenokoto

In a perfect world, in my opinion is that they are incompatibles, and the equality operation should return False in both cases.

But equality is a very complicated concept.

I guess if the values are incomparable != preserves PEM

But Boolean algebra is a lattice, with two+ binary operators were two of those are meet and join with a shared absorbing property.

X == not not X being PEM, we lose that in NP vs co-NP and in vector clocks etc…

But that is just my view.

2 hours agonyrikki

Yes, in numpy we also have that `np.float64(nan) != np.float64(nan)` evaluates to true.

3 hours agoiNic
[deleted]
3 hours ago

tl;dr:

- NaN is a floating point number, and NaN != NaN by definition in the IEEE 754-2019 floating point number standard, regardless of the programming language, there's nothing JavaScript-specific here.

- In JS Number.isNaN(v) returns true for NaN and anything that's not a number. And in JS, s * n and n * s return NaN for any non empty string s and any number n ("" * n returns 0). (EDIT: WRONG, sée below)

4 hours agojraph

> And in JS, s * n and n * s return NaN for any non empty string s and any number n ("" * n returns 0).

No? It is easy to verify that `"3" * 4` evaluates to 12. The full answer is that * converts its operands into primitives (with a hint of being number), and any string that can be parsed as a number converts to that number. Otherwise it converts to NaN.

4 hours agolifthrasiir

Ah indeed, thanks for the correction, I edited message.

2 hours agojraph

I always thought of NaN as more of the concept of not-a-number the way that infinity in math is not a specific value but the concept of some unbounded largest possible value.

Therefore, trying to do math with either (for example: NaN/NaN or inf./inf.) was to try to pin them down to something tangible and no longer conceptual — therefore disallowed.

4 hours agoJKCalhoun

You can use some form of real extensions, e.g. the extended real line (+inf, -inf is often useful for programmers) or the projectively extended real line (+inf = -inf).

This is not about infinity in math not being a _specific_ value, it can certainly be (the actual infinite instead of potential).

It's simply about design and foresight, in my humble opinion.

2 hours agobadosu

Tangentially related: one of my favourite things about JavaScript is that it has so many different ways for the computer to “say no” (in the sense of “computer says no”): false, null, undefined, NaN, boolean coercion of 0/“”, throwing errors, ...

While it’s common to see groaning about double-equal vs triple-equal comparison and eye-rolling directed at absurdly large tables like in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid... but I think it’s genuinely great that we have the ability to distinguish between concepts like “explicitly not present” and “absent”.

3 hours agofwlr

random thought: To see if something equals NaN,can't you just check for the stringified form of the number equaling "NaN"?

after all, the usual WTF lists for JS usually have a stringified NaN somewhere as part of the fun.

4 hours agokace91

It can usually be implemented like this. No need for strings.

fun isNan(n) = n != n

4 hours agospand

Ah, clever!

4 hours agokace91

well there is also one weird quirk I I assumed will be also included in this article:

because a <= b is defined as !(a > b)

then:

5 < NaN // false

5 == NaN // false

5 <= NaN // true

Edit: my bad, this does not work with NaN, but you can try `0 <= null`

4 hours agoSzpadel

IEEE 754 specifically prohibits that definition, and JavaScript indeed evaluates `5 <= NaN` to false.

4 hours agolifthrasiir

Yep, my memory was incorrect here and I didn't had access to computer, but it is true with `0 <= null`

4 hours agoSzpadel

This is because null coerces to 0 in JS so this is effectively 0 <= 0. NaN is already a `number` so no coercion happens.

Note that == has special rules, so 0 == null does NOT coerce to 0 == 0. If using == null, it only equals undefined and itself.

3 hours agokaoD

Slightly off topic, I hate that typescript bundles NaN under the number type and the signature for parseInt is number.

an hour agocandiddevmike

Imagine that society calls the people who have to work with these toys during office hours, engineers

4 hours agolnxg33k1

> type(NaN) -> "number"

NaN should have been NaVN, not a valid number.

4 hours agoamelius

What's the difference between something that's "not a number" and something that's "a number but not a valid one"?

4 hours agomort96

I'm now remembering the differences between "games" and "numbers" in Surreal numbers. :D

3 hours agotaeric
[deleted]
4 hours ago

A string (like "20") that can be coerced to a number?

4 hours agoJKCalhoun

Isn't that just not a number? It's a text string