Skip to content

Commit 13771c0

Browse files
committed
types-grammar, ch1: added discussion of -0 and NaN
1 parent c2d2aef commit 13771c0

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

types-grammar/ch1.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,86 @@ Depending on how you interpret "smallest", you could either answer `0` or... `Nu
618618
Number.MIN_SAFE_INTEGER; // -9007199254740991
619619
```
620620
621+
#### Double Zeros
622+
623+
It may surprise you to learn that JS has two zeros: `0`, and `-0` (negative zero). But what on earth is a "negative zero"? A mathematician would surely balk at such a notion.
624+
625+
This isn't just a funny JS quirk; it's mandated by the IEEE-754[^IEEE754] specification. All floating point numbers are signed, including zero. And though JS does kind of hide the existence of `-0`, it's entirely possible to produce it and to detect it:
626+
627+
```js
628+
function isNegZero(v) {
629+
return v == 0 && (1 / v) == -Infinity;
630+
}
631+
632+
regZero = 0 / 1;
633+
negZero = 0 / -1;
634+
635+
regZero === negZero; // true -- oops!
636+
Object.is(-0,regZero); // false -- phew!
637+
Object.is(-0,negZero); // true
638+
639+
isNegZero(regZero); // false
640+
isNegZero(negZero); // true
641+
```
642+
643+
You may wonder why we'd ever need such a thing as `-0`. It can be useful when using numbers to represent both the magnitude of movement (speed) of some item (like a game character or an animation) and also its direction (e.g., negative = left, positive = right).
644+
645+
Without having a signed zero value, you couldn't tell which direction such an item was pointing at the moment it came to rest.
646+
647+
#### Invalid Number
648+
649+
Mathematical operations can sometimes produce an invalid result. For example, if you try to divide a number by a string (`42 / "Kyle"`), that's an invalid mathematical operation.
650+
651+
Another type of invalid numeric operation is trying to convert/coerce a non-numeric type of value to a `number`. We can do so with either the `Number(..)` function (no `new` keyword) or with the unary `+` operator in front of the value:
652+
653+
```js
654+
myAge = Number("just a number");
655+
656+
myAge; // NaN
657+
658+
+undefined; // NaN
659+
```
660+
661+
All such invalid operations (mathematical or numeric) produce the special `number` value called `NaN`.
662+
663+
The historical root of "NaN" (including from the IEEE-754[^IEEE754] specification) is as an acronym for "Not a Number". Unfortunately, that meaning produces confusion, since `NaN` is *absolutely* a `number`.
664+
665+
| TIP: |
666+
| :--- |
667+
| Why is `NaN` a `number`?!? Think of the opposite: what if a mathematical/numeric operation, like `+` or `/`, produced a non-`number` value (like `null`, `undefined`, etc)? Wouldn't that be really strange and unexpected? What if they threw exceptions, so that you had to `try..catch` all your math? The only sensible behavior is, numeric/mathematical operations should *always* produce a `number`, even if that value is invalid because it came from an invalid operation. |
668+
669+
To avoid such confusion, I strongly prefer to define "NaN" as any of the following instead:
670+
671+
* "iNvalid Number"
672+
* "Not actual Number"
673+
* "Not available Number"
674+
* "Not applicable Number"
675+
676+
`NaN` is a special value in JS, in that it's the only value in the language that lacks the *identity property* -- it's never equal to itself.
677+
678+
```js
679+
NaN === NaN; // false
680+
```
681+
682+
So unfortunately, the `===` operator cannot check a value to see if it's `NaN`. But there are some ways to do so:
683+
684+
```js
685+
politicianIQ = "nothing" / Infinity;
686+
687+
Number.isNaN(politicianIQ); // true
688+
689+
Object.is(NaN,politicianIQ); // true
690+
[ NaN ].includes(politicianIQ); // true
691+
```
692+
693+
Here's a fact of virtually all JS programs, whether you realize it or not: `NaN` happens. Seriously, almost all programs that do any math or numeric conversions are subject to `NaN` showing up.
694+
695+
If you're not properly checking for `NaN` in your programs where you do math or numeric conversions, I can say with some degree of certainty: you probably have a number bug in your program somewhere, and it just hasn't bitten you yet (that you know of!).
696+
697+
| WARNING: |
698+
| :--- |
699+
| JS originally provided a global function called `isNaN(..)` for `NaN` checking, but it unfortunately has a long-standing coercion bug. `isNaN("Kyle")` returns `true`, even though the string value `"Kyle"` is most definitely *not* the `NaN` value. This is because the global `isNaN(..)` function forces any non-`number` argument to coerce to a `number` first, before checking for `NaN`. Coercing `"Kyle"` to a `number` produces `NaN`, so now the function sees a `NaN` and returns `true`! This buggy global `isNaN(..)` still exists in JS, but should never be used. When `NaN` checking, always use `Number.isNaN(..)`, `Object.is(..)`, etc. |
700+
621701
### BigInteger Values
622702
623703
As the maximum safe integer in JS `number`s is `9007199254740991`, such a relatively low limit can present a problem if a JS program needs to do larger integer math, or even just hold values like 64-bit integer IDs (e.g., Twitter Tweet IDs).

0 commit comments

Comments
 (0)