174

I had to write a routine that increments the value of a variable by 1 if its type is number and assigns 0 to the variable if not, where the variable is initially null or undefined.

The first implementation was v >= 0 ? v += 1 : v = 0 because I thought anything not a number would make an arithmetic expression false, but it was wrong since null >= 0 is evaluated to true. Then I learned null behaves like 0 and the following expressions are all evaluated to true.

  • null >= 0 && null <= 0
  • !(null < 0 || null > 0)
  • null + 1 === 1
  • 1 / null === Infinity
  • Math.pow(42, null) === 1

Of course, null is not 0. null == 0 is evaluated to false. This makes the seemingly tautological expression (v >= 0 && v <= 0) === (v == 0) false.

Why is null like 0, although it is not actually 0?

S. Hesam
  • 5,266
  • 3
  • 37
  • 59
Chungmin Lee
  • 2,320
  • 2
  • 18
  • 19
  • 3
    He is talking about Javascript. Your example is in PHP. In PHP operator== compares values in a special way. You can make some really crazy comparisons like "10" == "1e1" (which is true). If you used operator===, you would get a whole different result because it checks whether the type matches as well as the value. Check this link out: http://www.php.net/manual/en/language.operators.comparison.php – Pijusn Apr 06 '12 at 05:59
  • The PHP '==' operator really does work in a "special" way. – Two-Bit Alchemist Mar 19 '14 at 03:26
  • If your requirement was to start counting at 1 instead of 0, there's a really terse way to increment counters that are initially either `null` or `undefined`: `c = -~c // Results in 1 for null/undefined; increments if already a number` – Ates Goral Sep 12 '17 at 16:24
  • 1
    `undefined` is a variable value, for variables that have not been initialized. `null`, on the other hand, is an empty object value, and should not be mixed with numbers. `null` should not be combined with numbers, so null should not have to behave like numbers. – Matthew Jan 04 '18 at 03:24
  • 1
    @AtesGoral - terse, but non-obvious. Worth reminding people that whenever doing something non-obvious, to please add a comment explaining what the code does. In most situations, I would consider it a "premature optimization", given that it trades clarity for a miniscule performance gain. – ToolmakerSteve Nov 01 '19 at 12:13

6 Answers6

245

Your real question seem to be:

Why:

null >= 0; // true

But:

null == 0; // false

What really happens is that the Greater-than-or-equal Operator (>=), performs type coercion (ToPrimitive), with a hint type of Number, actually all the relational operators have this behavior.

null is treated in a special way by the Equals Operator (==). In a brief, it only coerces to undefined:

null == null; // true
null == undefined; // true

Value such as false, '', '0', and [] are subject to numeric type coercion, all of them coerce to zero.

You can see the inner details of this process in the The Abstract Equality Comparison Algorithm and The Abstract Relational Comparison Algorithm.

In Summary:

  • Relational Comparison: if both values are not type String, ToNumber is called on both. This is the same as adding a + in front, which for null coerces to 0.

  • Equality Comparison: only calls ToNumber on Strings, Numbers, and Booleans.

Michael Liquori
  • 619
  • 10
  • 16
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • 2
    Hi CMS, as per your explanation null primitive is 0,so 0>=0 returns true and == is returning false.but as per the ecma algorithm If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.then in this it should return true.please explain me – bharath muppa Jun 07 '16 at 06:34
  • to me the answer does not provide an *answer* - `null is treated in a special way by the Equals Operator (==). In a brief, it only coerces to undefined:` - and what? Can you explain, why `null >= 0`? :) – Andrey Deineko Jun 26 '16 at 14:01
  • 1
    @bharathmuppa @andrey-deineko: The rest of CMS's answer is here: [The Abstract Relational Comparison Algorithm](http://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5) which explains in point 3. that if both values are not type String, ToNumber is called on both. This is the same as adding a `+` in front, which for null coerces to `0`. Equality only calls ToNumber on Strings, Numbers, and Booleans. – Michael Liquori Aug 23 '16 at 15:32
  • 9
    Good description, but I don't like it. In any language (x == 0 || x > 0) should be equivalent to (x >= 0). javascript is a stupid language. – John Henckel Nov 29 '16 at 16:14
  • 4
    It's simply a bug in the spec really (because mathematically it's wrong) and there is nothing to do about it since millions of websites relies on null comparisons ^^' – mahieddine Aug 08 '18 at 13:43
  • Shouldn't it be, if both values are not number, ToNumber is called on them, you have written if both values are not string, ToNumber is called on them. – Suraj Jain Feb 24 '19 at 07:27
23

I'd like to extend the question to further improve visibility of the problem:

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

It just makes no sense. Like human languages, these things need be learned by heart.

estani
  • 24,254
  • 2
  • 93
  • 76
  • 4
    As described above it can be explained with just an exception as how == treats null, otherwise in all cases null is being converted into 0 by using Number(nulll) – Sourabh Jain 543 Mar 22 '18 at 21:10
10

JavaScript has both strict and type–converting comparisons

null >= 0; is true but (null==0)||(null>0) is false

null <= 0; is true but (null==0)||(null<0) is false

"" >= 0 is also true

For relational abstract comparisons (<= , >=), the operands are first converted to primitives, then to the same type, before comparison.

typeof null returns "object"

When type is object javascript tries to stringify the object (i.e null) the following steps are taken (ECMAScript 2015):

  1. If PreferredType was not passed, let hint be "default".
  2. Else if PreferredType is hint String, let hint be "string".
  3. Else PreferredType is hint Number, let hint be "number".
  4. Let exoticToPrim be GetMethod(input, @@toPrimitive).
  5. ReturnIfAbrupt(exoticToPrim).
  6. If exoticToPrim is not undefined, then
    a) Let result be Call(exoticToPrim, input, «hint»).
    b) ReturnIfAbrupt(result).
    c) If Type(result) is not Object, return result.
    d) Throw a TypeError exception.
  7. If hint is "default", let hint be "number".
  8. Return OrdinaryToPrimitive(input,hint).

The allowed values for hint are "default", "number", and "string". Date objects, are unique among built-in ECMAScript object in that they treat "default" as being equivalent to "string". All other built-in ECMAScript objects treat "default" as being equivalent to "number". (ECMAScript 20.3.4.45)

So I think null converts to 0.

Panos Kalatzantonakis
  • 12,525
  • 8
  • 64
  • 85
10
console.log( null > 0 );  // (1) false
console.log( null == 0 ); // (2) false
console.log( null >= 0 ); // (3) true

Mathematically, that’s strange. The last result states that "null is greater than or equal to zero", so in one of the comparisons above it must be true, but they are both false.

The reason is that an equality check == and comparisons > < >= <= work differently. Comparisons convert null to a number, treating it as 0. That’s why (3) null >= 0 is true and (1) null > 0 is false.

On the other hand, the equality check == for undefined and null is defined such that, without any conversions, they equal each other and don’t equal anything else. That’s why (2) null == 0 is false.

S. Hesam
  • 5,266
  • 3
  • 37
  • 59
2

I had the same problem !!. Currently my only solution is to separate.

var a = null;
var b = undefined;

if (a===0||a>0){ } //return false  !work!
if (b===0||b>0){ } //return false  !work!

//but 
if (a>=0){ } //return true !
jon
  • 1,494
  • 3
  • 16
  • 29
  • 1
    It might be clearer to instead do: `if (a!=null && a>=0)`. This clarifies the reason for not simply doing `>=` by itself: "a might be null (or undefined, which is also '== null')". – ToolmakerSteve Nov 01 '19 at 12:07
0

It looks like the way to check x >= 0 is !(x < 0) In that way make sense the response.