Going BIG with JavaScript: Numbers

Like all things, JavaScript has limits. Interestingly there are artificial limits and then there are physical limits. I’m going to be exploring these limits and try and push them as much as possible to their true limits.
In JavaScript, a Number is defined by w3schools.com as:
Unlike many other programming languages, JavaScript does not define different types of numbers, like integers, short, long, floating-point etc.
JavaScript numbers are always stored as double precision floating point numbers, following the international IEEE 754 standard.
This format stores numbers in 64 bits, where the number (the fraction) is stored in bits 0 to 51, the exponent in bits 52 to 62, and the sign in bit 63
What does this mean practically? In JavaScript, the largest possible number that can be stored is 1.7976931348623157e+308, or using Number.MAX_VALUE;
Things start to get weird when we try to go beyond this point. I tried running the below code expecting “Infinity”. That did not happen.
console.log(Number.MAX_VALUE+1);1.7976931348623157e+308
This doesn’t line up with the documentation that states “Numbers larger than MAX_VALUE are represented as infinity.”
Side note: The below tests were done using the Google Chrome console, so other JS interpreters might yield different results.
I then found a maximum number that was not Infinity.
console.log(Number.MAX_VALUE+999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999);1.7976931348623157e+308
If we add an extra “1” as below:
console.log(Number.MAX_VALUE+9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999991);Infinity
We now get Infinity. So what happened?
Let’s now talk about Number.MAX_SAFE_INTEGER, which is defined as:
The
Number.MAX_SAFE_INTEGER
constant represents the maximum safe integer in JavaScript (2⁵³-1).
A practical explanation is that anything beyond this limit, the JavaScript interpreter is no long accountable for operations.
Take a look at the code below:
console.log(Number.MAX_SAFE_INTEGER);
9007199254740991console.log(Number.MAX_SAFE_INTEGER+1);
9007199254740992let a = Number.MAX_SAFE_INTEGER+1;
let b = Number.MAX_SAFE_INTEGER+2;
console.log(a === b);
true
From the results above, we see that we can definitely go above the “MAX_SAFE_INTEGER”, but anything we want to do from a practical point of view no longer works. Both “a” and “b” are considered equal, when we know they’re not.
What if we want to go beyond MAX_SAFE_INTEGER?
JavaScript has a native function called BigInt. As its name describes, it handles big numbers. It’s also extremely simple to activate. Add a “n” to the end of the number and it’s instantly converted.
const theBiggestInt = 9007199254740991n;
typeof theBiggestInt;
"bigint"
So why not just use “BigInt” all the time? You can easily do “1n” and it’s now a BigInt and you have a much larger number limit. The catch? BigInt can’t handle decimals.
This means that if you convert from a Number to a BigInt and backwards again, you can lose precision. There’s also a compatibility catch in that only modern browsers support it. This isn’t really an issue as it’s most likely only going to be needed in NodeJS, which is supported.
We will now try our MAX_VALUE that we were having issues with earlier and wrap the result inside BigInt:
console.log(BigInt(Number.MAX_VALUE));
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368nconsole.log(BigInt(Number.MAX_VALUE+1));
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368n
The above example shows what’s going on. When we add more to Number.MAX_VALUE, it is simply ignored. Both equations result in the same answer. Why? I don’t know, that’s up to the JavaScript interpreter.
What we can do now is use BigInt to go beyond Number.MAX_VALUE. This time, we will use the increment against the BigInt value of MAX_VALUE instead of the result of MAX_VALUE. Below, notice the position of the “+1n” in the bracketing compared to the above code.
console.log(BigInt(Number.MAX_VALUE)+1n);
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858369n
As we can see above, our final digit has increased to a “9” from the “8”. So BigInt does work as described. Let’s try our comparison:
let ab = BigInt(Number.MAX_VALUE);
let bb = BigInt(Number.MAX_VALUE)+1n;
console.log(ab === bb);
false
As we can see, we our able to do comparisons above the Number.MAX_VALUE limit, with the downside of not being able to use decimal places.
BigInt does have limits, however there isn’t any upper “hard limit” or even any “soft limit”. The documentation makes no mention of it. From other peoples experience on StackOverflow, it seems that the limits are governed by the JavaScript interpreter and also the devices memory limits. There’s also a noticeable performance hit when reaching really big numbers.
In summary, it is possible to go beyond JavaScripts native number limits on the rare occasion that you need to. One example would be if you want to find prime numbers since these don’t require decimal places.
In most cases though you will not need to use BigInt, but you will want to make sure you don’t go beyond the native limit by checking against Number.MAX_SAFE_INTEGER if you know you might reach that number.