Javascript : The Curious Case of Null >= 0

Home / Developer Tools / Javascript : The Curious Case of Null >= 0
Javascript : The Curious Case of Null >= 0

 

Why it’s important to read the Javascript Spec

While compiling notes for a crash course on Javascript I was to give to a couple of colleagues, I came across a rather interesting scenario with nulland relational operators –

null > 0; // false
null == 0; // false
null >= 0; // true

Wait…what? That makes no sense!

How can a value not be greater than 0, not be equal to 0, but be greater than and equal to 0?

While I brushed this off initially as just Javascript being Javascript, in all it’s quirkiness, it did intrigue me. Did it have something to do with the type nulland how it is handled, or with how the relational and equality checks are performed?

So I sought out to find the root cause of this and started to sift through the single source of truth for Javascript — The Javascript Spec.

And boy did I go down a rabbit hole!

The Abstract Relational Comparison Algorithm

Let’s take the first check.

null > 0; // false

According to the Spec, the relational operators > and < send the statement through an algorithm called the Abstract Relational Comparison Algorithm to find out if the statement is true or false.

1. Call ToPrimitive(x, hint Number).
2. Call ToPrimitive(y, hint Number).
3. If Type(Result(1)) is String and Type(Result(2)) is String, go to step 16. (Note that this step differs from step 7 in the algorithm for the addition operator + in using 'and' instead of 'or'.)
4. Call ToNumber(Result(1)).
5. Call ToNumber(Result(2)).
6. If Result(4) is NaN, return undefined.
7. If Result(5) is NaN, return undefined.
8. If Result(4) and Result(5) are the same number value, return false.
9. If Result(4) is +0 and Result(5) is -0, return false.
10. If Result(4) is -0 and Result(5) is +0, return false.
11. If Result(4) is +∞, return false.
12. If Result(5) is +∞, return true.
13. If Result(5) is -∞, return false.
14. If Result(4) is -∞, return true.
15. If the mathematical value of Result(4) is less than the mathematical value of Result(5) --- note that these mathematical values are both finite and not both zero --- return true. Otherwise, return false.
16. If Result(2) is a prefix of Result(1), return false. (A string value p is a prefix of string value q if q can be the result of concatenating p and some other string r. Note that any string is a prefix of itself, because r may be the empty string.)
17. If Result(1) is a prefix of Result(2), return true.
18. Let k be the smallest nonnegative integer such that the character at position k within Result(1) is different from the character at position k within Result(2). (There must be such a k, for neither string is a prefix of the other.)
19. Let m be the integer that is the code point value for the character at position k within Result(1).
20. Let n be the integer that is the code point value for the character at position k within Result(2).
21. If m < n, return true. Otherwise, return false.

Let’s walk through this algorithm with our statement – null > 0.

Steps 1 and 2 ask us to call ToPrimitive() on null and 0 respectively to convert these values to their primitive value types (such as Number and String). The ToPrimitive conversion follows this table.

02.JPG

Going by the table, both our LHS, null, and RHS, 0, don’t undergo any conversion.

Now Step 3 does not apply to us, so we can ignore it and move on. At Steps 4 and 5, we need to convert both the LHS and RHS to type Number. The conversion to Number follows this table.

03.JPG

(I’ve omitted String and Object from the table as they have a more elaborate conversion, and the conversions are not relevant to us right now anyway. If you are curious about those, you can find them here.) 

null gets converted to +0 and 0 remains 0. Neither of the values are NaNso we can skip Steps 6 and 7. It’s at Step 8 that we need to stop. +0 is equal to0, and the algorithm returns false. Hence,

null > 0; // false
and 
null < 0; // also false

The Abstract Equality Comparison Algorithm

Let’s tackle the next check.

null == 0; //false

This was rather interesting.

The == operator runs the statement through the Abstract Equality Comparison Algorithm, and returns true or false.

1. If Type(x) is different from Type(y), go to step 14.
2.If Type(x) is Undefined, return true.
3.If Type(x) is Null, return true.
4.If Type(x) is not Number, go to step 11.
5.If x is NaN, return false.
6.If y is NaN, return false.
7.If x is the same number value as y, return true.
8.If x is +0 and y is -0, return true.
9. If x is -0 and y is +0, return true.
10. Return false.
11.If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.
12. If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.
13.Return true if x and y refer to the same object or if they refer to objects joined to each other (see 13.1.2). Otherwise, return false.
14. If x is null and y is undefined, return true.
15. If x is undefined and y is null, return true.
16.If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
17.If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x)== y.
18. If Type(x) is Boolean, return the result of the comparison ToNumber(x)== y.
19. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
20.If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
21.If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x)== y.
22. Return false.

Evaluating the equality of null and 0, we immediately jump from Step 1 to Step 14 as the Types are not the same. Surprisingly, Steps 14 through 21 also do not apply to us, since Type(x) is null. We finally arrive at Step 22 and the value false is returned as a default!

Hence,

null == 0; //false

The Greater-than-or-equal Operator (>= )

And now, we get to our last check.

null >= 0; // true

And this is where the Spec threw me off completely. At a very high level, the relational operator >= is evaluated as

if null < 0 is false, then null >= 0 is true

Hence,

null >= 0; // true

And it makes sense, honestly. Mathematically, if we have two numbers x and y, and if x is not less than y, then x has to be greater than, or equal to y.

I’m assuming it’s done this way to optimise evaluation of the statement. Why check if x is first greater than y, and if that’s not the case, if x is equal to y, when you can perform just one evaluation — is x less than y, and then use the result of that to glean the result for the original evaluation.

(If you’re curious about the actual steps executed for the >= operator, you can find them here.)

As trivial as this problem was, the search for the answer offered up some nice insights to the language. Hope this article did the same for you.

Source: https://medium.com/@CharlesBordet/how-to-extract-and-clean-data-from-pdf-files-in-r-da11964e252e

Leave a Reply

Your email address will not be published.