Hacker News new | past | comments | ask | show | jobs | submit login
WTFJS? (wtfjs.com)
237 points by mcantelon on Feb 12, 2010 | hide | past | web | favorite | 70 comments

NaN === NaN being false is extremely handy. If you are trying to track down a rogue NaN in some code, equating a variable against itself finds it immediately. This is also the same in most other languages (I use it all the time in C++).

Is there an advantage to doing that as opposed to using isNaN()?

IIRC, it's a speed hack in the IEEE floating point standard.

NaN can happen for a lot of different reasons. You might have divided by zero because your code is broken, or you might have divided by epsilon because your algorithm isn't stable on this problem. [1]

This test checks to make sure none of {f, g, x, y, intermediate_values} were ever NaN, wihtout having to test each one every step of the way.

    if (  f(x) == g(y)  )
Which is a big deal if it's the guard on an inner loop in some numerical code.

[1] I'm fuzzy on the details. Corrections solicited.

If it's just a debugging trick, though, then speed hacks seem less useful.

isNaN() will return true for anything that can't be coerced into a number while !(x == x) will only be true for the NaN value itself.

How do you know isNaN() doesn't use this fact? In Java, the body of isNaN(n) is simply 'return n!=n;'

It depends on the implementation. In C, it is quite easy to have n != n optimized away when compilers have their compiler flags set on. That's why you should use isnan to test nan instead of any other techniques when using C or C++.

I didn't know that! very useful, thank you.

No problem. At least once per game I end up having to track rogue NaNs. Learned this trick a while back and it has saved my bacon many a time.

NaN === NaN is guaranteed to be false by IEEE 754. Hardly a wtf.

Actually most of these posts seem to be the author misunderstanding floating point numbers. Perhaps he should learn what he's doing before he blames the language.

Yup; Number.MIN_VALUE has the minimum possible mantissa and exponent, for example. If you want the most negative possible number, you don't need a separate constant, you can just use -(Number.MAX_VALUE), as 754 reals are signed-magnitude.

This suggests that you don't actually know what the === operator is. To summarize:

== - 'values are equal' - in this case, are these two numbers equal? The IEEE spec would say no. I agree!

=== - 'both sides are exactly the same' - or, in more technical terms, are both the values and types of each argument exactly identical. In this case, the answer is yes, absolutely. === is not a numerical comparison and should not be.

=== means value and type are equal. In this case, the types are equal, but the values are not. I'd argue that NaN === NaN being true would imply a broken implementation of IEEE 754, and the designers of JS seem to agree with me.

What benefit is created by having a hierarchy of comparison operators in which there is no actual way to compare two numbers for semantic (not numeric) equivalence?

If (NaN == NaN) == false, and (NaN === NaN) == false, how would you implement an isNaN() function? If your answer is 'depend on the VM', I find that a bit strange.

typeof(NaN) should still be 'number', which would identify it uniquely. The irony of the value "Not a Number" having type "Number" is not lost on me, however.

function isNaN(x) { return x != x; };

new String("foo") === new String("foo") is false. === does not mean value and type are equal, it means that the left is the same object as on the right OR that both the left and right are primitives with the same value.

The behavior there is a result of the 'new' operator. typeof new String("foo") is specified to be "object", and it is extensible (String("foo") is not). Both == and === on two object-types return true if both refer to the same thing. More generally, when two types are the same, == has the same behavior as ===.

> In this case, the answer is yes, absolutely.

No, it isn't, absolutely.

`===` means that javascript does a normal equality test, whether it be string comparison or numerical comparison, but without any type conversion. It means that while `"3" == 3` is `true` when you execute `"3" === 3"` it is false.

Given two `NaN`s, it would see they're both floats, do a floating point comparison, and return `false`. As it should.

"== - 'values are equal' - in this case, are these two numbers equal? The IEEE spec would say no." "=== - ...are both the values and types..."

If the values are not equal, how can both the values and types be?

Looking at the ECMAScript 5 standard even if you take out the explicit handling of NaN in the === (Strict Equality) definition it should still evaluate to false since the next subrule compares the numeric value.

4.If Type(x) is Number, then a.If x is NaN, return false. b.If y is NaN, return false. c.If x is the same Number value as y, return true.

A reasonable number of these "wtf"s are just more examples of people failing to understand floating point. sigh

The question is who failed to understand floating point: The authors of the JS specification, the authors of the JS runtime, or the people writing the javascript that demonstrates the 'wtf'? Is the answer "all three"?

Here's a "wtf" from the site:

0.1 + 0.2 === 0.3 // false

I'd say that whoever posted that needs a lesson in how floating point works.

The real WTF is, why does a person writing some Javascript code need to understand floating point? What high-performance calculations are you doing in you Javascript wouldn't be easier, more accurate, and not that much slower using fixed point math?

With fixed point or arbitrary-precision math, you either need to (a) set the level of precision explicitly, which is just as bad about forcing the user to understand the underlying model, or (b) have the language choose the level of precision heuristically, and have even more unpredictable performance and rounding characteristics.

There's no language that lets you do real-world work with numbers like sqrt(2) or PI without understanding precision and representation to some degree.

JavaScript's brilliant solution is to just not have integers. At all. That's better, somehow?

No, I think JavaScript's "floats everywhere" is worse than most languages. I prefer languages like Python/Ruby that have one arbitrary-precision integer type and one floating point type, or languages like Haskell or Lisp that have a rich selection of floating/fixed/abitrary/rational types with well-defined performance. But some (not all) of the WTFs here exist in all of those languages too. Eventually, programmers working with numbers really do need to learn how to use different numerical representations.

Could you please grace us with a code sample for all of these? I'm not intimately familiar with Haskell, Lisp or Ruby.

  >>> from decimal import *
  >>> Decimal('0.3') == Decimal('0.1') + Decimal('0.2')
  >>> Decimal('NaN') == Decimal('NaN')

You only get that result in python by importing the decimal library. The standard behavior in python is (.1 + .2 == .3) == False

However, your code example makes the excellent point that python has such a library available, whereas Javascript does not. At least, not as easily as an import.

The people writing the JavaScript that demonstrates the wtf.

I can see that some of the design decisions in IEEE-754, like NaN not beeing equal to NaN might seem unintuitive, but it has nothing to do with JavaScript - the standard is implemented by numerous languages and hardware.

That large numbers are rounded to a limited precision should not even be wtf if you think about it for a moment. Otherwise a single use of PI would fill up all the computer memory.

Psht, floating point WTF's are for noobs. == is where the money is at. Straight from my V8 console:

    >>> x = new Boolean;
    >>> b = (x || !x);
    >>> Boolean(b == false) && Boolean(x == false) && Boolean(b) && Boolean(x)

Eh, msg me on twitter (same username) if you have better wtfs or want to contribute. The idea isn't to prove how awesome knowledgeable you are but rather to have a good laugh and MAYBE point out some of the less intuitive aspects of the language we all love to hate on.

Just pick up JavsaScript: The Good Parts by Douglas Crockford. He has a chapter called "The bad parts".

"(x=[].reverse)() === window // true"

Didn't understand this one at first, but I guess it is easy: first x becomes the reverse function of arrays, then it is called with this === window. So it amounts to window.reverse(). Just looked it up, and reverse() works in place, so window.reverse() === window - although it is potentially different from before.

No, it amonts to Array.prototype.reverse.call(window) - window doesn't have a reverse method AFAIK, so window.reverse() will raise a TypeError (since you can't call something "undefined").

are you sure? i thought in js functions are not really tied to "their" object, instead they receive an implicit this argument. wouldn't x belong to window here? that is, window doesn't have reverse, but now it has x

I smell a potential DOM injection XSS here.

How? The only reaon this works is that 'this' in a function that is not called on an object will default to the global object (window for browser js).

So just saying 'this' in an arbitrary function has the same effect.

but `this` is filtered by most js clean up libs.

In what language is 0.1 + 0.2 == 0.3? (I guess C# with decimal literals, but you'd have to specify that with a suffix)

I was curious and fired up sbcl:

    * (= (+ 0.1 0.2) 0.3)
    * (type-of 0.1)
    * (= 0.3d0 (+ 0.1d0 0.2d0))
    * (type-of 0.1d0)

  Prelude> (0.1 + 0.2) == 0.3
  Prelude Ratio> 1 % 10 + 2 % 10 == 3 % 10


    ghci> 0.1 + 0.2 == (0.3 :: Ratio Int)
Same type, less syntax.

That's cheating. Haskell isn't a language, it's The Word of Simon.

The question of which Simon is left as an exercise to the reader.

In Ruby:

0.1 + 0.2 == 0.3 => false

According to "The Ruby Programming Language", all languages that use IEEE-754 floating-point numbers have this problem.

In Ruby you can get around this by using BigDecimal.

>> require 'bigdecimal'

=> true

>> require 'bigdecimal/util'

=> true

>> 0.1.to_d + 0.2.to_d == 0.3.to_d

=> true

In Python

  >>> decimal.Decimal(".2") + decimal.Decimal(".1") == decimal.Decimal(".3")
  >>> .2 + .1 == .3

`typeof null` is 'object', not Object. (It's a string that contains the word object.)

If the programming can't handle a null object, check that the object is of the required type (by setting a key) or check that the object is not null. It's dumb to expect null to not be an object when EVERYTHING is an object in JS (except undefined, which is special). Furthermore, the programmer would be setting a value to null if they are checking for null (are there any other instances where null occurs without explicitly setting?).

>(function(){ if(false) var window; return window }()) === window

>JavaScript interpreter checks all variable declarations in scope before execution.

This is another way of saying that all variable declarations are scoped at the function level, not the individual block level. This is why generating closures in a loop is broken in javascript (Although in just about every other language as well, closures in a loop give surprising results at first - including, I think, arc?)

To get a new scope, you have to use the anonymous function trick:

       var x;//new scope

This is not a WTF if you just know where a language draws scope boundaries - for most languages, it isn't just along the {}'s.

It's a WTF in the sense that every other language with curly braces scope at the block level.

This is awesome.

<shameless plug>

I feel compelled to mention my WTF JavaScript library, even though it is something completely different: http://github.com/techiferous/wtf-js/blob/master/javascripts...

It's a library for dealing with World Time Format conversions: http://worldtimeformat.com/

</shameless plug>

When you run reverse with a context of anything besides an array you end up getting back that context. This is why assigning [].reverse to x, then running x() returns window. It is an interesting quirk. Not sure if this is the js specified behavior though.

Yes, this is JS specified behavior. If there is no activation object (one referenced by an identifier before the `.'), the value of `this' is set to the global object.

Floating point is hard, let's go shopping.


Some guy registered WTFPHP.com on 2010/02/12

A bit of Computer Science knowledge would go a long way in explaining these: e.g. "Foo" + + "Bar" === "FooNaN" (attempting to do unary + on "Bar"). Tons of other examples as documented here, but some of these are a "bit" strange yes ;).

I am no fan of Javascript, but most of these WTFs make sense to me. The 111111111100 one is unexpected, although there is probably a good explanation. (Like "alert takes a string, you passed a number. nasal demons.")

Wouldn't that have more to do with the fact that javascript uses floats for everything? It's probably exceeding precision at a guess.

Is it? I thought Javascript has integer type.

Say for example Lua (by default) is configured to use only double. It can be configured to use single-float, or even integer - but only one numeric type.

Standard JavaScript uses doubles everywhere. They get casted to 32-bit signed integers for bitwise operations, the results are casted back. It's a little odd, but consistent at least.

A little odd and a lot slow, if you do them a lot (js-protobuf, I'm looking at you).

Execution tracing should eliminate the cast if it is not necessary.

Ah. I don't know the inner workings of Javascript so I'm not surprised I was wrong. Just at a glance, that problem to me screams floating point limitation.

Indeed. I would have expected 415003079.

WTFHN? Why is this voted up? Just because you don't immediately understand something doesn't make it a WTF.

isNaN(null) == false; is the only wtf I can never remember to avoid, because I can't rationalize it no matter how hard I try.

Edit: I get it now. null coverts to 0

it's because of type conversion - null has the same value as 0 (as in `(+ null)`), so it is a number

Yea, what a weird mental block, not realizing null is equivalent to 0, even in math statements. I guess it's because null is not the same as 0 when converted to a String.

Eh, no biggie, a few pages of gotchas. If we were talking about PHP, I could write a few volumes.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact