New Number and Math Features in ES6

Number

You can specify integers in binary and octal notation:

    > 0xFF // ES5 - hexadecimal
    255
    
    > 0b11 // ES6 - binary
    3
    
    > 0o10 // ES6 - octal
    8
    

Number.prototype.toString(radix) can be used to convert numbers from decimal.

    > 255.toString(16)
    'ff'
    
    > 4.toString(2)
    '100'
    
    > 8.toString(8)
    '10'
    

For parseInt(string, radix?), if radix is missing, 0, or 16, parseInt parses hexadecimal literal notation to base 10.

    > parseInt('0xFF')
    255
    
    > parseInt('0xFF', 0)
    255
    
    > parseInt('0xFF', 16)
    255
    

Otherwise, digits are parsed until the first non-digit.

    > parseInt('0xFF', 10)
    0
    
    > parseInt('0xFF', 17)
    0
    

parseInt does not have support for binary or octal literal notation. Number() does, though.

    > Number('0b111')
    7
    
    > Number('0o10')
    8
    

To parseInt() base 2 or 8 numbers, indicate the radix as the second parameter.

The Number() constructor has new methods in ES6: isFinite, isNaN, parseFloat() and parseInt().

    > Number.isFinite(NaN)
    false
    

The constructor method does not automatically coerce its parameter automatically to type 'number,' unlike the global method.

    > Number.isFinite('123')
    false
    
    > isFinite('123')
    true
    
    > Number.isNaN('???')
    false
    
    > isNaN('???')
    true
    

NaN is the only value that doesn't equal itself.

    > const x = NaN
    > x === NaN
    false
    

You can check if a value is NaN using:

    > x !== x
    true
    

Using Number.isNaN is more descriptive:

    > Number.isNaN(x)
    true
    

Number.parseFloat(string) and Number.parseInt(string, radix) work exactly like their global counterparts.

Rounding errors can become a problem in JavaScript. Number.EPSILON specifies a reasonable margin of error when comparing floating point numbers.

    > 0.1 + 0.2 === 0.3
    false
    
    > function epsEqu(x, y) { return Math.abs(x - y) < Number.EPSILON; }
    > console.log(epsEqu(0.1+0.2, 0.3));
    true
    

JavaScript only has floating point numbers (doubles). Integers are just floating point numbers without a decimal fraction.

Number.isInteger(number) returns true if its parameter is a 'number' and does not have a decimal fraction.

    > Number.isInteger(-17)
    true
    > Number.isInteger(33)
    true
    > Number.isInteger(33.1)
    false
    > Number.isInteger('33')
    false
    > Number.isInteger(NaN)
    false
    > Number.isInteger(Infinity)
    false
    

JavaScript numbers only have enough storage space to represent 53 bit signed integers. That is, for all integers i in the range -2^53 < i < 2^53 are safe. The following methods determine if an integer is safe:

                    Number.MAX_SAFE_INTEGER = Math.pow(2, 53)-1;
                    Number.MIN_SAFE_INTEGER = -Number.MAX_SAFE_INTEGER;
                    Number.isSafeInteger(number)
            

JavaScript Safe integers have a one-to-one mapping between them and the mathematical integers they represent. Unsafe integers do not:

    > Math.pow(2, 53)
    9007199254740992
    
    > 9007199254740992
    9007199254740992
    > 9007199254740993
    9007199254740992
    > 9007199254740994
    9007199254740994
    > 9007199254740995
    9007199254740996
    > 9007199254740996
    9007199254740996
    > 9007199254740997
    9007199254740996
    

When performing arithmetic in JavaScript,

isSafeInteger(a) && isSafeInteger(b) && isSafeInteger(a op b)

validates that the result of the operation and its operands are safe integers.

JavaScript's 53-bit integers are a problem if 64-bit integers are needed. The only way around this is to use a library for higher-precision numbers (bigints or bigfloats). Decimal.js is an example library.

Math

Math.sign(x) returns the sign of x as +1 or -1. For 0 and NaN, the parameter is returned.

Math.trunc(x) removes the decimal fraction of x.

Math.cbrt(x) returns the cube root of x.

A good case practice for floating point number is to store them using a 0 instead of a 1. This is because floating point numbers with base 10 are internally represented as mantissa x 10^exponent. Use cases include exponentiation and logarithms.

(A) 0.000000234 = 2.34 * 10-7. Significant digits: 234
(B) 1.000000234 = 1.000000234 * 100. Significant digits: 1000000234

In this example, A has higher precision than B. We can see this applied in the following example:

    > 1e-16 === 0
    false
    > 1 + 1e-16 === 1
    true
    

Math.expml(x) returns Math.exp(x) - 1. This is the inverse of Math.loglp().

This method provides higher precision when Math.exp() has results close to 1. For example:

    > Math.expm1(1e-10)
    1.00000000005e-10
    > Math.exp(1e-10)-1
    1.000000082740371e-10
    

Math.loglp(x) returns Math.log(1 + x).

This method also lets you specify parameters that are close to 1 with higher precision.

    > Math.log(1 + 1e-16)
    0
    > Math.log(1 + 0)
    0
    

In contrast, log1p() produces different results:

    > Math.log1p(1e-16)
    1e-16
    > Math.log1p(0)
    0
    

Math.log2(x) computes the logarithm to base 2.

    > Math.log2(8)
    3
    

Math.log10(x) computes the logarithm to base 10.

    > Math.log10(100)
    2
    

Trigonometric functions include:

    Math.sinh(x)
    Math.cosh(x)
    Math.tanh(x)
    Math.asinh(x)
    Math.acosh(x)
    Math.atanh(x)
    

Math.hypot(...values) computes the square root of the sum of the squares of its arguments.

Fun facts: Bitwise operations

Math.clz32(x) counts the leading zero bits in the 32-bit integer x.

    > Math.clz32(0b01000000000000000000000000000000)
    1
    > Math.clz32(0b00100000000000000000000000000000)
    2
    > Math.clz32(2)
    30
    > Math.clz32(1)
    31
    

Counting leading zeros in an integer number is a critical operation in many DSP algorithms, such as normalization of samples in sound or video processing, as well as in real-time schedulers to quickly find the highest-priority task ready-to-run.


Source: Exploring ES6 by Dr. Axel Rauschmayer.

Published March 14, 2016