JavaScript Numbers

JavaScript Numbers

·

6 min read

image.png


Numbers

There are basically two types of numbers in JavaScript:

  • Regular numbers: These are double-precision floating-point numbers stored in 64-bit format IEEE-754. The double-precision floating-point numbers are responsible for things like imprecise calculation.
0.2 + 0.1 // 0.30000000000000004
// caused by double-precision floating-point (64 bit)

These numbers are between -2⁵³ - 1 (minimum safe integer) and 2⁵³ - 1 (maximum safe integer).

Number.MIN_SAFE_INTEGER // -9007199254740991 (-2⁵³ - 1)

Number.MAX_SAFE_INTEGER // 9007199254740991 (2⁵³ - 1)
  • BigInt numbers: These are numbers below -2⁵³ - 1 or above 2⁵³ - 1 (B ≤ -2⁵³ - 1 or B ≥ 2⁵³ - 1)

Numbers in underscore

Digits can be separated with underscores. For example, 18,054,020 can be represented as 18_054_020 in JavaScript.


Numbers in exponential

Let's assume we want to represent the number 2,000,000,000 in exponential. In Maths, it's 2x10⁹. In JavaScript, it's 2e9 or 2E9.

See the examples below:

2e9 === 2000000000; // true
1e-3 === 1 / 1000; // true
1.43e-6 === 1.43 / 1000000; // true

In the example above, 2e-9 is the same as 0.000000002 (9 zeros to the left).


Hex, binary and octal numbers

Hexadecimal numbers

Hexadecimal can be used to represent colors, encode characters, etc.

Normally one or two characters (aA-fF or numbers 0 - 9) appends to 0x.

See the example below:

0x4a; // 74
0xf9; // 249

console.log(`Hexadecimal numbers are between ${0x00} and ${0xff}.`)
// Hexadecimal numbers are between 0 and 255.

Binary and octal numbers

The prefix 0b represents a binary number while the prefix 0o represents an octal number.

See the example below:

const a = 0b11111111; // binary form of 255
const b = 0o377; // octal form of 255

console.log( a === b ); // true

toString(base)

To convert a whole num to base a the syntax is:

num.toString(a)

See the example below:

const num = 255;

num.toString(16);  // ff
num.toString(2);   // 11111111

The base can vary from 2 to 36. By default, it's 10.

Note: If you do not want to use the variable (num) to store a number but want to call a method directly on a number, the .. can be used.

The example above is the same as below:

255..toString(16);  // ff
255..toString(2);   // 11111111

Alternative to the example above, it can be rewritten as shown below:

(255).toString(16);  // ff
(255).toString(2);   // 11111111

Rounding

There are several built-in functions for rounding:

  • Math.floor(num) and Math.round(num): They round down numbers to the nearest whole number. 3.24 becomes 3.

The difference between Math.floor(num) and Math.round(num) is shown in the example below:

console.log( Math.floor(3.4), Math.round(3.4) ) // 3 3
console.log( Math.floor(3.5), Math.round(3.5) ) // 3 4
console.log( Math.floor(3.6), Math.round(3.6) ) // 3 4

For negative decimal numbers (-num), Math.floor(-num) becomes -Math.abs(num + 1) to the nearest whole number; while Math.round(-num) becomes -Math.abs(num) to the nearest whole number.

See the example below:

console.log( Math.floor(-3.3), Math.round(-3.3) ); // -4 -3

For positive decimal numbers, Math.round(num) round-up numbers (num) exactly at or after .5 position but round-down numbers (num) before .5 position, but Math.floor(num) always round-down.

  • Math.ceil(num) always round up numbers (num).
Math.ceil(3.4) // 4
Math.ceil(3.2) // 4
Math.ceil(3.6) // 4
  • Math.trunc(num): It is like Math.floor for positive numbers but like Math.round(num) for negative numbers.

See the example below:

Math.trunc(3.4); // 3
Math.trunc(3.5); // 3
Math.trunc(3.6); // 3
Math.trunc(-3.6); // 3
Math.trunc(-3.4); // 3

It is advisable to use math.trunc(num) more often than Math.floor(num) to round-down numbers. Math.floor(num) is mostly used on positive decimal numbers.

numberMath.ceilMath.floorMath.roundMath.trunc
3.14333
3.64343
-3.1-3-4-3-3
-3.6-3-4-4-3

Math.trunc is not supported by Internet Explorer



Imprecise calculation

A number internally is represented in 64-bit format IEEE-754 - 52 bits to store digits; 11 bits to store decimal numbers position (zero for integer numbers); 1 bit for signs.

console.log(1e550); // Infinity
0.2 + 0.1 === 0.3 // false 

// The above is false because
0.2 + 0.1 // 0.30000000000000004

// ☝ the operation on 0.1, 0.2 causes 
// unending fractions in their binary form.

the operation on 0.1, 0.2 causes unending fractions in their binary form

To solve the issue, we can use the method num.toFixed(n)

num.toFixed(n)

See the example below:

const num = 0.1 + 0.2;
const result = num.toFixed(1); // 0.3

console.log(result, typeof result); // 0.3 string
console.log(typeof +result) // number

num.toFixed(n) is in string.

n=1 specifies the number of digits after 0.

toFixed always returns a string

See the example below

const num = 0.2 + 0.1;
const result = num.toFixed(2);

console.log(result, typeof result); // 0.30 string

To convert the result above to a number, use the unary operator (+) or Number(...)

const num = 0.2 + 0.1;
const result = num.toFixed(2);
const toNum = +result; // Number(result)

console.log(toNum, typeof toNum); // 0.30 number

Alternatively, you can change the decimal numbers in an expression to fractional numbers and multiply both numerator and denominator by 10.

// const num = ((0.1 * 10) + (0.2 * 10)) / 10;
const num = ((1/10 * 10) + (2/10 * 10)) / 10;
num;

The trick above doesn't always work. This means the formal, num.toFixed(n) is the preferred way to fix numbers.

0.28 + 0.14; // 0.42000000000000004 

// ( (28 / 100 * 100) + (14 / 100 * 100) ) / 100
( (0.28 * 100) + (0.14 * 100) ) / 100 ; // 0.4200000000000001 

// num.toFixed(n)
+(0.28 + 0.14).toFixed(2); // 0.42

Integers (non-decimal) numbers are accurate up to 15 digits.

const a = 999999999999999;   // 15 9s
const b = 9999999999999999;  // 16 9s will lead to wrong interpretation

console.log(a, b); // 999999999999999 10000000000000000

Tests - isFinite and isNaN

  • isNaN(value) converts its argument to a number and then tests it for being NaN: Non-integer values are NaN.

See the example below:

console.log( isNaN(NaN) ); // true
console.log( isNaN("str") ); // true
console.log( isNaN(10) ); // false

The value NaN is unique in that it does not equal anything.

NaN === NaN // false

In JavaScript, Object.is(a, b) is used when an internal algorithm needs to compare two values for being exactly the same.

Object.is(NaN, NaN); // true
Object.is(0, 0); // true
Object.is(0, -0); //false
  • isFinite(value) converts its argument to a number and returns true - not either NaN Infinity or -Infinity.

See the example below:

console( isFinite("41") ); // true => value number
console( isFinite("str") ); // false => value NaN
console( isFinite(Infinity) ); // false => value Infinity

Sometimes isFinite(value) is used to validate whether a string value is a regular number:

const num = +prompt("Enter a number", ' ');

alert( isFinite(num), typeof num);

ParseInt and ParseFloat

  • The function parseInt(...) returns an integer.
  • The function parseFloat(...) returns a floating-point number.
parseInt(100.45) // 100
parseFloat(100) // 100.00

Both functions only read a number from a string.

parseInt('30$'); // 30

ParseInt base

If a parseInt function has a second argument, the second argument becomes the base to be converted to.

See the syntax below:

parseInt(str[, radix])
parseInt('0xff', 16); // 255
parseInt('ff', 16); // 255, without 0x also works

parseInt('2n9c', 36); // 123456

Built-in Math functions

Check out MDN for other built-in Math functions

Happy coding!


Buy me a Coffee