# Double the Beyoncé: Navigating Numbers in JavaScript

What happens when the numbers you’re working with are too big for JavaScript? How do numbers behave beyond JavaScript’s integer limit?

In a talk which promises to be more interesting than your typical high school math class, Meggan takes you down the rabbit hole that is numbers in JavaScript. We’ll explore how they’re implemented, how we can best write, format, convert & calculate them, and what to do when we’re forced to handle really big (or really small) numbers.

@megganeturner delighting and teaching us JavaScript math #code19 pic.twitter.com/u1bbTtG0I7

— Diana MacDonald (@didoesdigital) June 20, 2019

Lots of Beyonce gifs ahead `#sorrynotsorry`

We use numbers for a lot of things, but generally to…

- Count
- Measure
- Label
- Identify

There are many, *many* different types of number…

- Natural numbers (positive integers)
- Integers (negative and positive whole numbers)
- Rational numbers
- Irrational numbers
- Complex numbers
- Transcendental numbers
- Infinity and negative infinity
- Real numbers – a superset of all possible numbers
- …and many more

We can communicate numbers in many different ways. We can say eight and people may think of `8`

, `八`

, `आठ`

…

Different ways to represent numbers in #javascript form @megganeturner at @webdirections #code19 pic.twitter.com/MvnbkaMz5v

— Klee Thomas (@kleeut) June 20, 2019

We have many notations: base 10, binary, octal, hex, scientific…

JavaScript follows the IEE-754 standard for floating point arithmetic. This is why 0.1 + 0.2 !== 0.3 in JS (and many other languages that do unexpected things due to floating point maths).

This limits the *range* we can display, although it’s in the quadrillions so generally not a big issue. We also consider *precision*, JS goes to 17 decimal points.

Everything on computers ends up in binary, which can’t do decimals. Floating point arithmetic has to work around this by encoding numbers. You can break down a 64 bit binary number into its components, the Sign (1 bit), Exponent (11 bits) and Significand precision (53 bits, 52 explicitly stored).

Fractions are tricky. 1/3 is a recurring decimal 0.33333…… so we round it. And you know this is painful when you are trying to create a three-column layout…

How big an issue is this? Depends what the number relates to. The rounding error if you were measuring a light year with 0.00000000000000004ly margin for error, that’s about as big as a bowling pin. In most applications it just isn’t big enough to be an issue; but in something like banking it can become a really big problem very quickly.

Range: size does matter. We make a trade off between range and precision.

If you type `1.7e308`

into your JS console, you get `1.7e+308`

; but if you type in `1.8e308`

you get infinity.

`Number.MAX_VALUE`

`Number.MIN_VALUE`

We have names for big numbers going up to centillion (a number followed by 303 zeros), and JS goes beyond that. It’s a massive range but we can’t display them all safely.

If you try…

`Number.MAX_SAFE_INTEGER`

`Number.MAX_SAFE_INTEGER + 1`

`Number.MAX_SAFE_INTEGER + 2`

`Number.MAX_SAFE_INTEGER + 3`

`Number.MAX_SAFE_INTEGER + 4`

…this will not do what you would expect! You get:

9007199254740991 9007199254740992 9007199254740992 9007199254740994 9007199254740996

Meggan ran into this problem while working on a very large music database at Jaxta. There was an error where the main artist on a listing was coming up as the featured artist. Which wasn’t right, even if Meggan does contend that Double Beyonce isn’t a bug!

The core of the problem was that two artist resources were coming back with the same ID. How was this possible? The actual IDs were returned as different numbers from the back end; but since **they were beyond MAX_SAFE_INTEGER**, JS was turning them into the same number. Solution was to move IDs to a 128-bit UUID strings.

So, long term what can JS do about this? Enter `BigInt`

. A new numeric primitive for JS, allowing representation of numbers beyond `MAX_SAFE_INTEGER`

. This can be expressed with `n`

or by explicit cast:

`100n`

`BigInt(100)`

We can do arithmetic with BigInts, noting it will still round to whole integers. Can’t do mixed type operations, although we can compare `0n == 0`

and sort mixed numbers.

Plus if you try this…

`let bigNumber = BigInt(Number.MAX_SAFE_INTEGER)`

`bigNumber + 1n`

`bigNumber + 2n`

`bigNumber + 3n`

`bigNumber + 4n`

`bigNumber + 5n`

…you get the numbers you expect! Yay! This is a really important new feature for JavaScript. Sadly though, browser support is some way off.

Numbers in JS can be confusing, but when you understand why they were implemented that way it’s easier to figure out the bugs.

Things to look out for:

- we only have 64 bits, so round numbers
- remember MAX_SAFE_INTEGER
`BigInt`

is coming!