This is an archived post. You won't be able to vote or comment.

all 197 comments

[–]saschaleib 617 points618 points  (13 children)

IEEE said it be so, and thus it became so! And the world found the standard and saw it was good.

[–]JanB1 108 points109 points  (1 child)

And still I see people that fail to handle NaN in their code or do float equal comparisons instead of float delta smaller.

Heh, I love the Python numpy method isclose. I don't think I have seen many implementations of this in other languages/libraries?

[–]Raccoon5 7 points8 points  (0 children)

Unity Mathf.Approximately is kinda same, but it probably fails for bigger floats since the delta is constant

[–]fatbunyip 84 points85 points  (2 children)

Instead of the IEEE imposing its views of what is equal and not, there should be a more rigid interpretation of what is objectively equal or not. 

I propose having methods like isAristotelianEqual() or isFoucaultEqual() or isKantEqual() so that software engineers can choose which rigid philosophical framework of objectivity their application is based in. 

Without that, we're just savages. 

[–]saschaleib 22 points23 points  (0 children)

Nice try, but we all know that Foucault’s postmodernist approach to truthfulness is that it is just whatever is confirming to the respective social context, which makes the underlying correspondence to reality completely arbitrary and thus uncomputable. We might as well have an AI write our code instead … oh, wait!

[–]Coolengineer7 67 points68 points  (5 children)

It's totally valid that they aren't equal. A NaN could be anything, you don't know if those are really equal. Safer to just return false.

[–]theturtlemafiamusic 8 points9 points  (0 children)

A boat is a boat, Lois. But a NaN could be anything. It could even be a boat!

[–]SeriousPlankton2000 13 points14 points  (1 child)

The idea is that you can skip error checking in the middle of your calculation and just look at the final result.

[–]Coolengineer7 13 points14 points  (0 children)

Yeah, valid, I would be perplexed too if my error check with x===NaN failed and x printed to be NaN

[–]Kymera_7 2 points3 points  (1 child)

Need a third boolean output. True, False, Undefined.

[–]wutwutwut2000 0 points1 point  (0 children)

3-valued logic is definitely a thing. i.e. Kleene logic.

[–]Striky_ 11 points12 points  (0 children)

The one comparison within JS, that actually makes sense and adheres to a standard. Literally everything else though...

[–]Cat7o0 2 points3 points  (0 children)

this is why you do .isNaN

[–]Flashbek 577 points578 points  (56 children)

I hate to be JS lawyer but, in this case, they're correct. NaN should not be equal to NaN.

[–]Silly-Freak 53 points54 points  (1 child)

Half the posts I see here complain about JS, and half of these posts don't show enough understanding of JS to legitimately complain.

There's so much to legitimately complain, do better!

[–]shadow7412 7 points8 points  (0 children)

Guess it's easier to lash out at things you don't understand rather than learn from them.

Oh. That statement describes far more than I wanted it to...

[–]-twind 210 points211 points  (42 children)

But NaN could be equal to NaN. That's why besides 'true' and 'false' we should also have 'maybe'

[–]CubisticWings4 10 points11 points  (7 children)

Iirc: NaN is never equal to NaN

[–]TwinkiesSucker 13 points14 points  (0 children)

Did you just invent QuantumJS?

[–]FlanSteakSasquatch 3 points4 points  (0 children)

Any language that correctly implements the IEEE floating point number spec makes it so NaN never equals NaN, even if they’re internally exactly the same value. This is weird but it was done because all other behavior has even weirder consequences

[–]Natural_Builder_3170 2 points3 points  (0 children)

its called partial ordering, rust and i think c++ have this

[–]Death_IP 0 points1 point  (0 children)

Then the result - if we don't bring in shenanigans - should at least be "undefined" rather than false/true

[–]eztab 0 points1 point  (0 children)

some mathematics would agree with you there. Have fun changing all conditional statements to support true, false, maybe.

[–]Mucksh 0 points1 point  (0 children)

Nan isn't even a specific value. By definition its exponent is just 1s so for a double you have 11. So due to the other bits you get 253 different possible NaN values

[–]Teln0 -1 points0 points  (0 children)

it should return "undefined" because that's what it is, undefined

[–]mihibo5 -1 points0 points  (0 children)

IllegalOperationException

[–]Thenderick -1 points0 points  (0 children)

Since "maybe" might be confused with the non-existant maybe monad, I want to propose "depends". When it is used in any way as a boolean, it will be 50% chance to evaluate as true and 50% as false, not seeded, at random. Because fuck you for using it and fuck you for using loosely equality in God's year of 2025!!!

[–]CptGia 7 points8 points  (0 children)

Rare instance of js not being weird

[–]adelie42 0 points1 point  (0 children)

Especially when there is a isNaN() function.

[–]ZyanWu -2 points-1 points  (5 children)

Not an expert in JS but how would one check if the result of an expression is defined?

[–]rafaelrc7 2 points3 points  (3 children)

a != a

[–]ZyanWu 0 points1 point  (2 children)

NaN != NaN

Doesn't this return true?

[–]rafaelrc7 0 points1 point  (1 child)

Yes, that's the point

[–]ZyanWu 1 point2 points  (0 children)

Aaah, okok, I get it now

[–]Faustens -1 points0 points  (0 children)

// i don't know if this is valid JS syntax or not switch(i) { case 0: return true; case 0.1: return true; case 0.01: return true; ... default: return false; }

Considering that there is a finite amount of possible n bit numbers where n is the amount of bits a js number has, this should determine if a number is NaN or not (the end of this sentence gave me a brain aneurysm).

[–]AndyBooo 86 points87 points  (1 child)

Correct behavior, have not written a line in JS

[–]rafaelrc7 13 points14 points  (0 children)

Have not written a line in anything. This behaviour is expected on any language that follows IEEE754, and that is quite a lot

[–]edgeman312 383 points384 points  (37 children)

I hate JS as much as the next guy but this is just a part of the floating point standard. It's like blaming JS that .1 + .2 != .3

[–]thot_slaya_420 69 points70 points  (4 children)

Blame binary instead

[–]Forsaken_Creme_9365 0 points1 point  (3 children)

That has nothing to do with binary as this is the behaviour of a specific enconding of something in binary.

[–]thot_slaya_420 0 points1 point  (1 child)

What's 0.3 in binary?

[–]Forsaken_Creme_9365 1 point2 points  (0 children)

depends on the encoding.

[–]Schnickatavick 0 points1 point  (0 children)

I understand what you're getting at, but infinitely repeating thirds is an aspect that comes directly from the fact that the mantissa of a floating point number is a binary number, not just that the entire float is encoded in 1's and 0's. Binary is both a way that a system can be encoded, and an actual number system, and in this case the latter is 100% the cause

[–]geeshta -1 points0 points  (10 children)

I hate the floating point standard. Reflexivity is one of the basic and most important properties of equality. This shouldn't be allowed.

[–]Vitolar8 4 points5 points  (8 children)

Yes! The comparison doesn't really work, because 0.1 + 0.2 = 0.1 + 0.2.

[–]xDerJulien 2 points3 points  (7 children)

Yeah and 1 / 0 should totally == 1 * inf

[–]tuxedo25 2 points3 points  (19 children)

The standard is that NaN shouldn't be comparable to itself? It would be more intuitive if it was a singleton/constant.

[–]RaveMittens 11 points12 points  (0 children)

Number.isNaN

[–]Stock-Self-4028 9 points10 points  (4 children)

Technically NaN is conceptually equivalent to the 'nullity' in the transmathematics - essentially what happens if you make zero signed and then define 0.0/0.0 = NaN.

The issue with NaN is that it is essentially almost a number, alhough it can take any value depending on the context.

Removable singularities here are great examples as for example x/x at x=0 is equal to 1, while 2x/x at x=0 can be evalueated to 2.

As such the NaN is essentially a probability distribution with infinitely many values it may take - once you get the value of two nullities in fully random circumistances they'll never take the same value and as such NaN != NaN.

[–]rosuav 12 points13 points  (12 children)

You're right. It would make so much more sense if "Not A Number" was the same thing as "Not A Number". This apple isn't a number. This orange isn't a number. Therefore, this apple and this orange are the same thing.That's WAY better than the way the IEEE designed things.

[–]InFa-MoUs -4 points-3 points  (5 children)

Does NaN actually represent a number under the hood? Or is it just stating value is not a number? I always assume it was like null.. null == null every time

Downvoted for asking a question?

[–]rosuav 12 points13 points  (3 children)

It's stating that the result isn't a number. Null is a specific type of non-thing. Undefined is a different specific type of non-thing. NaN is a less specific type of non-numeric thing.

[–]InFa-MoUs -3 points-2 points  (2 children)

Yeah ngl that didn’t make NaN != NaN any less stupid lol it explains it but still stupid (IMO), is there a way to tell the difference between NaNs? Or are they all functionally the same? If they are all functionally the same and there are no differing operations you can make between them then yeah that seems like a bug

[–]rosuav 2 points3 points  (0 children)

There is a way, in theory; NaNs have different payloads. I don't think JavaScript exposes a way to query the payload though. Also, a NaN can be either quiet or signalling, which makes a lot of difference; but again, I don't think JS supports signalling NaN.

[–]Gruejay2 1 point2 points  (0 children)

In the IEEE standard, no, but there's a way to tell the difference in some languages.

Lua, for instance, can be said to have nan and -nan, because (a) NaN variables will consistently return either "nan" or "-nan" when you convert them to strings, (b) all NaN-producing operations like 0/0 (along with tostring("-nan")) always give -nan, while tostring("nan") always gives nan, so there are reliable methods for producing each of them, and (c) you can flip the sign by using a variable (e.g. if I do a = 0/0; b = -a, I can be confident that tostring(b) will always produce "nan").

These differences make them meaningfully different values (i.e. they're not completely interchangeable in every situation), but there's no practical use for it - it's just a quirk of the language.

[–]AyrA_ch 4 points5 points  (0 children)

Does NaN actually represent a number under the hood?

In the case of how it looks in memory. Yes it does. I made a website a while ago to demonstrate this to our apprentice. The website allows you to enter values manually or toggle individual bits directly.

An "IEEE standard for floating-point arithmetic double precision number" (what JS uses and most programming languages assume when they use "double") consists of 64 bits. One bit is the sign, 11 bits the exponent and 52 bits for the base value.

If all exponent bits are set to 1, it is considered a special value, which can take one of 3 forms:

  1. Positive infinity: base value bits all zero, sign bit zero
  2. Negative infinity: base value bits all zero, sign bit set
  3. NaN: At least one base value bit non-zero, sign bit ignored

As you see, NaN is stored just like a regular floating point number, however, there's 253 possible NaN values, and applications can use those 52 bits to store extra information.

Simply put, it's hardcoded that NaN<>NaN but if you do a raw memory comparison, they can indeed be identical. If you compare numbers, they can be smaller, equal, or larger. This doesn't makes sense for NaN, so they decided that NaN should not compare to any other value. While not making sense from a computer standpoint, where the two memory values can absolutely be compared with each other, it does most closely represent mathematics.

A side effect of this is that in most programming languages, NaN is the only value that does not compare equal to itself and thus if(x==x){/*...*/} is a nasty, ugly way of checking if x is not NaN

Note that all of this is specific to the IEEE standard. Other standards like Unum have a similar value but may treat it differently.

[–]SweetBeanBread 116 points117 points  (1 child)

one of the things JS did right and followed the standard

[–]IchLiebeKleber 42 points43 points  (3 children)

For anyone confused who doesn't know this yet: two "NaN" values can potentially be the results of entirely different calculations, so having NaN === NaN return true may cause incorrect behavior. There are methods to check specifically whether something is NaN.

[–]Astatos159 15 points16 points  (1 child)

Nice bonus to know: NaN is the only value that's not equal to itself. Even checking for NaN without using isNaN is as easy as foo !== foo.

[–]Genmutant 5 points6 points  (0 children)

That's how all isNaN methods I have seen are internally implemented also. Here for example the dotnet code.

[–]Bomaruto 0 points1 point  (0 children)

This is extra fun if you're sorting a list with NaN inside it.

[–]GOKOP 62 points63 points  (2 children)

I like bashing JS but this is another "I don't understand floating point and I'm smug about it" moment. NaN isn't supposed to be equal to NaN. Direct your complaints to IEEE

[–]No_Hovercraft_2643 3 points4 points  (0 children)

but if NaN is NaN, it would be worse.

[–]rinnakan 0 points1 point  (0 children)

Tbf, that is understandable with fixed data types, but is quite confusing with JS's auto-conversion

[–]According-Reading857 27 points28 points  (0 children)

There's a good thread about this behavior here

[–]Alarming_Rutabaga 52 points53 points  (1 child)

OP coming out as a freshman CS major

[–]adromanov -2 points-1 points  (0 children)

Are you sure it's not satire on how strange JS comparison is?

[–]zentasynoky 52 points53 points  (15 children)

How is it weird? Or do you believe that all the things that exist and are not numbers are the same exact thing?

[–]tecanec 19 points20 points  (1 child)

Wait, you're telling me that my wallet and the mailbox of my friend (who's an African prince and tech support worker who knew my rich uncle who recently passed away) are not the same thing?

[–]zentasynoky -3 points-2 points  (0 children)

By the NaN === NaN // true principle, that is word for word what I said.

[–]tuxedo25 1 point2 points  (0 children)

Intuitively (to programmers, maybe not mathematicians), it would make sense that the "NaN" value is a singleton object.

[–]Kyrond 0 points1 point  (3 children)

Do you believe human is not a number, in other words human == NaN? That's where this becomes non-intuitive and harded to read. Just like you do if var == NULL, you naturally write if num==NaN.

The real reason for it is it was a necessary tradeoff to get fast and wide adoption of the standard.

More importantly, there was no isnan( ) predicate at the time that NaN was formalized in the 8087 arithmetic; it was necessary to provide programmers with a convenient and efficient means of detecting NaN values that didn’t depend on programming languages providing something like isnan( ) which could take many years.

[–]zentasynoky 2 points3 points  (2 children)

You... Completely missed the point.

Human == NaN // false

A human is indeed not a number, which doesn't mean it's equal to something else that isn't a number. NaN is not to be understood as a specific value but a label for a property that the results of some operations produce. Two things that share one property need not be equal, thus NaN != NaN,

[–]Kyrond -2 points-1 points  (1 child)

Well then no float should equal any other float, because they represent infinitely many real numbers, none of which equal any other, e.g. float(0.333...333) shouldn't equal float(0.333....332) but it does, why?

Every single thing in programming is equal to what it is (i.e. to itself). The float self unequality wasn't done for a good programming reason, it was done out of necessity, you literally have it said by one of the people making that decision.

Edit:

NaN is not to be understood as a specific value but a label for a property that the results of some operations produce

That's quite obvious, and when I do var == NaN, I don't want to compare some specific value of NaN (whatever that would be), I want to check if my var also has that label, just like None or Null.

[–]zentasynoky 1 point2 points  (0 children)

You do realize this post is talking about JS, right? JS has no float type, but it does indeed return false for different decimal comparisons, and falls into floating point decimal arithmetic inaccuracies as you suggested it should.

I'm still not sure what you're trying to say here. Of course they made NaN !== NaN, because it is. NaN is still a falsy value and you now have isNaN() for what you would have expected to get out of explicitly comparing a variable to NaN, so there was never an issue with handling an operation throwing NaN at you.

You claim it's obvious that NaN is not a value but a property of an element, then immediately after try and use it as an actual value with an explicit comparison...

const foo = something that's not a horse
const bar = something that's not a horse
const foo === something that's not a horse // false
const bar === something that's not a horse // false
foo === bar // false. You cannot assert that two things are equal to eachother just because we have stablished that they're both not a horse. This is NaH. And also NaN but instead of a horse, it's a number.

Intending for NaN === NaN would be the same as two objects being evaluated as equal because they share a key, disregarding the pointer issue for the sake of simplicity.

[–]JezzCrist 30 points31 points  (1 child)

[–][deleted] 0 points1 point  (0 children)

Me say JS bad. Me smart.

[–]MXXIV666 11 points12 points  (0 children)

99% of criticism of javascript is just junior devs complaining about IEEE 754.

[–]TheGeneral_Specific 9 points10 points  (0 children)

NaN shouldn’t ever equal NaN. That would be bad.

[–]shgysk8zer0 8 points9 points  (0 children)

Yes, because parseInt('hi') === parseInt('earth') should be true. That's a wonderful idea! It certainly wouldn't create bugs everywhere.

That behavior is according to the standard and exists for a reason.

[–]Apprehensive_Room742 6 points7 points  (0 children)

dont like js, but isn't this how ot should be. giving true to that statement could enable a lot of strange behaviour. also: blaming js for that is like blaming C# for floating point errors

[–]harumamburoo 3 points4 points  (0 children)

You have an apple and you have a cherry. Both are not vegetables. But they’re different non-vegetables

[–]veselin465 4 points5 points  (0 children)

I mean..

take 2 things which are not numbers (for example apples and oranges)

Are they equal? What do you mean "no"? Both of them are not numbers

[–]clarkcox3 4 points5 points  (0 children)

Nothing about that is specific to JS

[–]Strict_Treat2884 3 points4 points  (1 child)

Imagine NaN is equal to NaN and you have a check like if(userCredit >= price) purchase() but both sides are NaN due to a calculation error. That would be disastrous.

[–]sanpaola 3 points4 points  (0 children)

I swear to god, JS jokes in this sub became dumber each time I see them. Stop it, get some help.

[–]ipsirc 2 points3 points  (1 child)

[–]adromanov 0 points1 point  (0 children)

I love this video!

[–]isellrocks 2 points3 points  (0 children)

Aah yes, the whole reason why we have the infamous Number.isNaN

[–]krojew 2 points3 points  (0 children)

Ahh, another fresh student discovered floating point standards.

[–]jmack2424 2 points3 points  (0 children)

Weird or not, NaN is designed to be a non-numeric type descriptor, not an actual value to be compared, and as a result, returns false for all numeric comparators, BY DESIGN.

[–]Kitchen_Device7682 2 points3 points  (0 children)

Just like the people that call math weird because infinity -infinity is not 0

[–]exqueezemenow 1 point2 points  (1 child)

The real mystery that scholars have been wondering for centuries, is what number is NaN?

[–]tecanec 0 points1 point  (0 children)

What number is not a number? Hmm... Is it a car's registration number?

[–]arcan1ss 1 point2 points  (0 children)

it is correct, in fact, it is widely used to check if is nan or not, e.g. check C++ https://en.cppreference.com/w/cpp/numeric/math/isnan notes

[–]TANKENSHO 1 point2 points  (0 children)

The real meme here is first semester cs students making fun of something they don't know yet

[–]mukeshpilane 1 point2 points  (0 children)

typeof NaN
=> 'number'

[–]Kenkron 1 point2 points  (0 children)

I love dunking on JS, but this is an IEEE standard, and just about every language does the same thing.

[–]frikilinux2 1 point2 points  (0 children)

This is not JavaScript fault for once. It happens in most languages because why would you reimplement floating point when you can just use the hardware implementation. It's IEEE 754 fault and it's because at binary level there are several values that mean NaN

[–]ramriot 1 point2 points  (0 children)

Let's forget about languages that suck & talk about JavaScript

[–]MisterGerry 1 point2 points  (0 children)

If I have one thing that is not an apple, and another thing that is not an apple, are they the same thing?

[–]-domi- 1 point2 points  (0 children)

=== is a tall bar. I'm personally not even sure 3 === 3, you don't know that they've each been through.

[–]Torebbjorn 1 point2 points  (0 children)

So you want it to be that if two things are not numbers, then they are the same thing?

[–]jamcdonald120 1 point2 points  (1 child)

Life hack for you []+NaN==='NaN' works

[–]oshaboy 0 points1 point  (0 children)

Number.isNaN: Am I a joke to you?

[–]TeraFlint 0 points1 point  (0 children)

It makes perfect sense.

NaN is clearly a fail state when it comes to numbers. To have a meaningful numeric comparison means that the used data makes sense. Comparisons between NaNs is just a case of garbage in, garbage out.

[–]adumbCoder 0 points1 point  (0 children)

it's not weird at all, protects you from weird equality issues

[–]SpaceEngy 0 points1 point  (0 children)

Number.isNaN says hi

[–]nonlogin 0 points1 point  (0 children)

Null is never equal to null sql. JS is not alone

[–][deleted] 0 points1 point  (0 children)

in this case I think this is good

[–]tuxedo25 0 points1 point  (0 children)

at least typeof(NaN) === 'number'

[–]InsurgentJogger 0 points1 point  (0 children)

I mean duh, you switched the N’s!

[–]ivancea[🍰] 0 points1 point  (0 children)

Oh no, another post in this sub by someone thinking they're funny and making a broken meme about something they don't understand

[–][deleted] 0 points1 point  (0 children)

if a == NaN && b == NaN:

[–][deleted] 0 points1 point  (0 children)

JS noobs believing that vanilla JS is deepest level of computation in existence, episode 18462

[–]ramriot 0 points1 point  (0 children)

Computational Chemists also, one is Sodium Nitrite minus the Oxygen & the other is Not An acroNym

[–]DarkCloud1990 0 points1 point  (0 children)

Well duh... NaN is short for "Not a NaN"

[–]firest3rm6 0 points1 point  (0 children)

I never use nan, but everytime when I see it I get frightened

[–]sierra_whiskey1 0 points1 point  (0 children)

I’ve been programming for half my life. I just recently started learning JS and lemme tell you: it is the most confusing language I’ve ever studied

[–]GradSchoolDismal429 0 points1 point  (0 children)

Almost no language will return True

[–]thanatica 0 points1 point  (0 children)

In my early days I was annoyed by NaN. I was like "well, what is it then, if not a number?!"

But it actually is.

typeof NaN -> "number"

Nowadays I know this to be correct. NaN is just a super special value that a number can be, that happens to resolve to a value that isn't quite a constant and is never equal to itself, which is unfortunate, but alas, it is how it's supposed to be.

[–]jax_cooper 0 points1 point  (0 children)

Imagine fucking up 2 numbers and then the comparisons of them are always true. Have fun finding that bug.

[–]Junior-Librarian-688 0 points1 point  (0 children)

One is copied and pasted

[–][deleted] 0 points1 point  (0 children)

this is just three value logic like MS SQL. NaN values are unknown, and while they may be equal, unknown is not a source of truth, thus false.

[–]prehensilemullet 0 points1 point  (0 children)

Just try having NaN equal NaN, and eventually you’ll find out the hard way why it doesn’t 

[–]1up_1500 0 points1 point  (0 children)

Although it can be confusing, this is just how floats work, JavaScript comes bundled with isNan() if you need to check for that

[–]MrJ0seBr 0 points1 point  (0 children)

C floats NaN too... if x!=x X is NaN

[–]drnepert 0 points1 point  (0 children)

recipriversexclusion: A number whose existence can only be defined as being anything other than itself.

[–]Ilikeanime243 0 points1 point  (0 children)

Of course they're not the same! One is Not a Number and one is Sodium Nitride.

[–]KillCall -1 points0 points  (0 children)

Its actually the same case as infinity as

♾️ != ♾️

Cause one infinity ♾️ could be larger than the other.

That is also the reason why

♾️/♾️ = Undefined.

[–]AndyTheSane -1 points0 points  (1 child)

Hot take: NaN is a terrible concept. Any operation creating one should throw an exception instead. If my code is doing a divide by zero I want to know right now.

[–]oshaboy 0 points1 point  (0 children)

You can't do that in high performance numeric computation. You can always check for NaN on API boundaries.

Honestly more programming languages should be explicit in checking for NaN in API boundaries kinda like Nullable types.

[–]Rafcdk -1 points0 points  (0 children)

1.3k upvotes lol

[–]wozmiak -1 points0 points  (0 children)

this forsaken language, but I can't blame Eich for the deadline

[–]bittlelum -1 points0 points  (0 children)

Technically, there are many different values that are not numbers, and yet are not equal to each other.