JavaScript WTFs explained - Number type and floating point
Tags: javascript, math
Published: 2022-07-28 23:44:59

---
title: JavaScript WTFs explained - Number type and floating point
published: true
description:
tags: javascript, math
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgtuyr17yikr560tmg3f.jpg
series: JavaScript WTFs explained
---

_Cover Photo by <a href="https://unsplash.com/@tonny_tran?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Tony Tran</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>_

![A screenshot of the Firefox developer console with several confusing results output - this will be the subject of today's discussion](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r2r1f3n0kggl717hto2p.png)

Source:

{% embed https://www.reddit.com/r/ProgrammerHumor/comments/w8m9em/just_started_learning_javascript_and_have_a_lot/ %}

## About binary and numeric representation

Computers don't have ten fingers to count on - they use a system called binary, which functions very similarly to our base-ten number system, but with only two digits (0 and 1) instead of ten (0 through 9).

Some resources if you need a quick lesson on how this works:

*Trevor Storr - How to count in binary*

{% embed https://www.youtube.com/watch?v=puaaRoWL-Ec %}

*Khan Academy - Adding in binary*

{% embed https://www.youtube.com/watch?v=RgklPQ8rbkg %}

*Low Level JavaScript - Floating point explained*

{% embed https://www.youtube.com/watch?v=wPBjd-vb9eI %}


---

## `9999999999999999` evaluates to `10000000000000000`

This is not specific to JavaScript by any means! It's another consequence of how computers handle numbers and math.

> 💡 **About how computers represent numbers**
> 
> When you think of a big number, your brain has an advantage over computers. You don't need to reserve a certain amount of space in order to think about that number; you can be as precise or imprecise as you want, without your brain crashing. Probably. 
>
> In order for a *computer* to think about a number, it first needs to know roughly how large and how precise it is, so it can reserve the right amount of space for it in its memory.

Because of how computers represent numbers, if you have a number with a lot of "information" in it (very large, very precise, or both), the computer might run out of "space" for that number. In some circumstances, this just throws an error (the computer gives up). In JavaScript **and many other languages**, the number is reduced to a version that has less "information" to store.

JavaScript has a maximum number, over which it cannot guarantee precision for integers: `Number.MAX_SAFE_INTEGER`. You can get a value for this in the console:

    > Number.MAX_SAFE_INTEGER
    > 9007199254740991 

And as you can see, our value of `9999999999999999` is over that threshold, so it will not be guaranteed to be accurate.

---

## `0.5 + 0.1 === 0.6`, but `0.1 + 0.2 !== 0.3`


In JavaScript, when you add `0.1` and `0.2` together in the console, here's what you get:

    > 0.30000000000000004

Almost - but not *exactly* - 0.3.

This is another consequence of how computers handle numbers, and is again **not specific to JavaScript** - in fact, *any* language that uses floating point decimals will have this problem.

When you tell a computer about a base-10 number, like `0.5` (one half), it internally is thinking about this number in binary as `0.1` (**not** one-tenth, since in binary that's the *"halves place"* and not the *"tenths place"*). 

Let's convert all these numbers to binary:

- `0.1` in base ten => `0.00011[0011...]` in binary
- `0.2` in base ten => `0.00110[0110...]` in binary
- `0.3` in base ten => `0.01001[1001...]` in binary
- `0.5` in base ten => `0.1` in binary
- `0.6` in base ten => `0.10011[0011...]` in binary

You may notice that a lot of those contain **infinitely repeating decimals**. Computers aren't so great with infinite things, so they just write down however much they can, and stop when they run out of space to store the number.

These infinitely repeating decimals are the core of this particular issue.

Let's look at adding `0.1` and `0.5`, which is easy even for us humans, since `0.5` has such a tidy representation in binary.

(The logic for addition in binary is the same in base ten, except you "carry" when you get to two rather than ten. Luckily, this problem doesn't involve any carrying at all!)

```
  0.00011[0011...] // 0.1
+ 0.1              // 0.5
= 0.10011[0011...] // 0.6
```

For this one, you could cut off the repeating decimal at *any point* and the value of `0.1 + 0.2` would have the same representation as `0.3`. We can tell this easily because one of the numbers does not infinitely repeat. Therefore, the infinite part of the result must come from the infinite part of the `0.1`.

Now let's try `0.1` plus `0.2` (You do need to learn how to carry for this one, sorry):

```
  0.00011[0011...] // 0.1
+ 0.00110[0110...] // 0.2
= 0.10011[0011...] // 0.3
```

These are the same *if* you have the ability to think about infinitely long numbers. But what about a computer? They need to cut off the repeating decimals at some point.

Let's take another look at what happens if we cut it off at, say, **seven** decimal places:

```
  0.0001100 // approximately 0.1
+ 0.0011001 // approximately 0.2
= 0.1001101 // close to 0.3, but...
  0.1001100 // this is what 0.3 actually looks like when approximated to 7 decimal places!
```



> 💡 **All languages that use floating point numbers will have some form of this problem.**

Those languages may not face this *exact* issue - it depends on where exactly the program gives up keeping track of infinitely repeating decimals.

---

## `typeof NaN` is `"number"`

This is partially a language decision, but also partially due to how computers don't do math quite like humans do.

`NaN` (Not a Number) is typically seen when a numeric system encounters non-numeric data (for example, `parseInt("a")`). However, it is defined in the [IEEE764 floating point spec](http://www.hlam.ece.ufl.edu/EEL4712/Labs/Lab6/IEEEStandard754FP.pdf) as any numeric concept or value that doesn't represent a single real number, such as:

* A number with a complex component (as in 2 + 3 *i*, where *i* is the square root of -1)
* A result of a numeric operation of numbers that has an indeterminate result
* A result of an invalid mathematical operation
* The result of a mathematical operation where one of the operands is `NaN`

JavaScript's `Number` type is based on this floating point specification, so it makes sense that the type also includes `NaN`. `Number` also includes non-numbers that are more conceptual, such as `Infinity`.

If you think of `Number` as **"math stuff"**, it probably makes more sense.

...I do agree that it's hilarious, though.


---
## `Math.max()` is `-Infinity` and `Math.min()` is `Infinity`

These functions both typically take list of arguments, such as:

```js
Math.max(2, 3, 4); // returns 4
Math.min(2, 3, 4); // returns 2
```

So these are basically the functions being called with an "empty array" of arguments.

If you take the maximum or minimum value of an empty list, what would you expect the result to be? The question itself - *what is the smallest value in an empty list of things?* - sounds like a question a Zen master would ask. The answer might not have any meaning at all, so it falls to us to make sure it's at least practical.

Let's go over this one by example - if you had an array of numbers (in any language), what sort of algorithm would you use to find the smallest or largest? A bit of pseudo-code for the most common algorithm to replicate `Math.max`:

```
given LIST OF ARGUMENTS
variable RESULT
for (each ITEM in the LIST OF ARGUMENTS):
  is the ITEM greater than the current RESULT?
    if so, replace RESULT with the ITEM's value
    otherwise, do nothing; continue on to the next ITEM.
return RESULT
```
The question here is - **what should the initial value of RESULT be**? There are two intuitive answers that work well:

1. It should be an **empty value** such as `null` or `undefined`. This means that you have to update the algorithm a little bit to account for the empty value, and people who use this will need to note that the return value may be empty or zero, and they will need to be careful to not confuse the two.\*
2. It should be something that is **always "less than" any finite number**. in JavaScript, `-Infinity` satisfies that logic. The logical consequence to this is that if there is nothing in the list of arguments, the `-Infinity` will be returned.

\* *If you've ever coded in PHP, this likely brought back traumatic memories. I'm sorry.*

Both of these work just fine, and the only thing that's affected is the (probably rare) case of being called with an empty list of things to compare, and some *extremely* minor performance differences. Not an important choice in the long run, so I don't blame JavaScript for this one. At least we don't have to check the return value for `null` every time.

---

You made it to the end! Thanks for sticking around. Here's a picture of a kitten in a bowtie to show my appreciation (via [reddit](https://www.reddit.com/r/aww/comments/w9kskf/my_friend_bought_little_bowties_for_some_kittens/)).

![A picture of a very small kitten wearing a bowtie](https://preview.redd.it/ld5tkt53c5e91.jpg?width=640&crop=smart&auto=webp&s=9b49b67fa16e2cb0a0c3256893e8ce98977c60a0)

[The other half of the Reddit post is discussed here](https://dev.to/tchaflich/javascript-wtfs-explained-type-coercion-4i8o).