Number
Whenever you are handling numbers in any programming language you need to be aware of the idiosyncrasies of how the language handles numbers. Here are a few critical pieces of information about numbers in JavaScript that you should be aware of.
Core Type
JavaScript has only one number type. It is a double-precision 64-bit Number
. Below we discuss its limitations along with a recommended solution.
Decimal
For those familiar with doubles / float in other languages, you would know that binary floating point numbers do not map correctly to Decimal numbers. A trivial (and famous) example with JavaScript’s built in numbers is shown below:
console.log(.1 + .2); // 0.30000000000000004
For true decimal math use
big.js
mentioned below.
Integer
The integer limits represented by the built in number type are Number.MAX_SAFE_INTEGER
and Number.MIN_SAFE_INTEGER
.
console.log({max: Number.MAX_SAFE_INTEGER, min: Number.MIN_SAFE_INTEGER});
// {max: 9007199254740991, min: -9007199254740991}
Safe in this context refers to the fact that the value cannot be the result of a rounding error.
The unsafe values are +1 / -1
away from these safe values and any amount of addition / subtraction will round the result.
console.log(Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2); // true!
console.log(Number.MIN_SAFE_INTEGER - 1 === Number.MIN_SAFE_INTEGER - 2); // true!
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992 - Correct
console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 - Rounded!
console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994 - Rounded - correct by luck
console.log(Number.MAX_SAFE_INTEGER + 4); // 9007199254740996 - Rounded!
To check safety you can use ES6 Number.isSafeInteger
:
// Safe value
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER)); // true
// Unsafe value
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1)); // false
// Because it might have been rounded to it due to overflow
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 10)); // false
For arbitrary precision integer math use
big.js
mentioned below.
big.js
Whenever you use math for financial calculations (e.g. GST calculation, money with cents, addition etc) use a library like big.js which is designed for
- Perfect decimal math
- Safe out of bound integer values
Installation is simple:
npm install big.js @types/big.js
Quick Usage example:
import { Big } from 'big.js';
export const foo = new Big('111.11111111111111111111');
export const bar = foo.plus(new Big('0.00000000000000000001'));
// To get a number:
const x: number = Number(bar.toString()); // Loses the precision
Do not use this library for math used for UI / performance intensive purposes e.g charts, canvas drawing etc.
NaN
When some number calculation is not representable by a valid number, JavaScript returns a special NaN
value. A classic example is imaginary numbers:
console.log(Math.sqrt(-1)); // NaN
Note: Equality checks don’t work on NaN
values. Instead use Number.isNaN
instead:
// Don't do this
console.log(NaN === NaN); // false!!
// Do this
console.log(Number.isNaN(NaN)); // true
Infinity
The outer bounds of values representable in Number are available as static Number.MAX_VALUE
and -Number.MAX_VALUE
values.
console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
console.log(-Number.MAX_VALUE); // -1.7976931348623157e+308
Values outside the range where precision isn’t changed are clamped to these limits e.g.
console.log(Number.MAX_VALUE + 1 == Number.MAX_VALUE); // true!
console.log(-Number.MAX_VALUE - 1 == -Number.MAX_VALUE); // true!
Values outside the range where precision is changed resolve to special values Infinity
/-Infinity
e.g.
console.log(Number.MAX_VALUE + 10**1000); // Infinity
console.log(-Number.MAX_VALUE - 10**1000); // -Infinity
Of-course, these special infinity values also show up with arithmetic that requires it e.g.
console.log( 1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
You can use these Infinity
values manually or using static members of the Number
class as shown below:
console.log(Number.POSITIVE_INFINITY === Infinity); // true
console.log(Number.NEGATIVE_INFINITY === -Infinity); // true
Fortunately comparison operators (<
/ >
) work reliably on infinity values:
console.log( Infinity > 1); // true
console.log(-Infinity < -1); // true
Infinitesimal
The smallest non-zero value representable in Number is available as static Number.MIN_VALUE
console.log(Number.MIN_VALUE); // 5e-324
Values smaller than MIN_VALUE
(“underflow values”) are converted to 0.
console.log(Number.MIN_VALUE / 10); // 0
Further intuition: Just like values bigger than
Number.MAX_VALUE
get clamped to INFINITY, values smaller thanNumber.MIN_VALUE
get clamped to0
.