Math featuresNumber propertiesMath methodsNumber.parseInt() and the new integer literalsNumber propertiesNumber.EPSILONNumber.isInteger(number)Math functionalityYou can now specify integers in binary and octal notation:
> 0xFF // ES5: hexadecimal255> 0b11 // ES6: binary3> 0o10 // ES6: octal8Number propertiesThe global objectNumber gained a few new properties:
Number.EPSILON for comparing floating point numbers with a tolerance for rounding errors.Number.isInteger(num) checks whethernum is an integer (a number without a decimal fraction): > Number.isInteger(1.05) false > Number.isInteger(1) true > Number.isInteger(-3.1) false > Number.isInteger(-3) trueNumber.isSafeInteger(number)Number.MIN_SAFE_INTEGERNumber.MAX_SAFE_INTEGERNumber.isNaN(num) checks whethernum is the valueNaN. In contrast to the global functionisNaN(), it doesn’t coerce its argument to a number and is therefore safer for non-numbers: > isNaN('???') true > Number.isNaN('???') falseNumber are mostly equivalent to the global functions with the same names:Number.isFinite,Number.parseFloat,Number.parseInt.Math methodsThe global objectMath has new methods for numerical, trigonometric and bitwise operations. Let’s look at four examples.
Math.sign() returns the sign of a number:
> Math.sign(-8)-1> Math.sign(0)0> Math.sign(3)1Math.trunc() removes the decimal fraction of a number:
> Math.trunc(3.1)3> Math.trunc(3.9)3> Math.trunc(-3.1)-3> Math.trunc(-3.9)-3Math.log10() computes the logarithm to base 10:
> Math.log10(100)2Math.hypot() Computes the square root of the sum of the squares of its arguments (Pythagoras’ theorem):
> Math.hypot(3, 4)5ECMAScript 5 already has literals for hexadecimal integers:
> 0x99> 0xA10> 0x1016> 0xFF255ECMAScript 6 brings two new kinds of integer literals:
0b or0B: > 0b11 3 > 0b100 40o or0O (that’s a zero followed by the capital letter O; the first variant is safer): > 0o7 7 > 0o10 8Remember that theNumber methodtoString(radix) can be used to see numbers in a base other than 10:
> 255..toString(16)'ff'> 4..toString(2)'100'> 8..toString(8)'10'(The double dots are necessary so that the dot for property access isn’t confused with a decimal dot.)
In the Node.jsfile system module, several functions have the parametermode. Its value is used to specify file permissions, via an encoding that is a holdover from Unix:
That means that permissions can be represented by 9 bits (3 categories with 3 permissions each):
| User | Group | All | |
|---|---|---|---|
| Permissions | r, w, x | r, w, x | r, w, x |
| Bit | 8, 7, 6 | 5, 4, 3 | 2, 1, 0 |
The permissions of a single category of users are stored in 3 bits:
| Bits | Permissions | Octal digit |
|---|---|---|
| 000 | ––– | 0 |
| 001 | ––x | 1 |
| 010 | –w– | 2 |
| 011 | –wx | 3 |
| 100 | r–– | 4 |
| 101 | r–x | 5 |
| 110 | rw– | 6 |
| 111 | rwx | 7 |
That means that octal numbers are a compact representation of all permissions, you only need 3 digits, one digit per category of users. Two examples:
Number.parseInt() and the new integer literalsNumber.parseInt() (which does the same as the global functionparseInt()) has the following signature:
Number.parseInt(string,radix?)
Number.parseInt(): hexadecimal number literalsNumber.parseInt() provides special support for the hexadecimal literal notation – the prefix0x (or0X) ofstring is removed if:
radix is missing or 0. Thenradix is set to 16. As a rule, you should never omit theradix.radix is 16.For example:
> Number.parseInt('0xFF')255> Number.parseInt('0xFF', 0)255> Number.parseInt('0xFF', 16)255In all other cases, digits are only parsed until the first non-digit:
> Number.parseInt('0xFF', 10)0> Number.parseInt('0xFF', 17)0Number.parseInt(): binary and octal number literalsHowever,Number.parseInt() does not have special support for binary or octal literals!
> Number.parseInt('0b111')0> Number.parseInt('0b111', 2)0> Number.parseInt('111', 2)7> Number.parseInt('0o10')0> Number.parseInt('0o10', 8)0> Number.parseInt('10', 8)8If you want to parse these kinds of literals, you need to useNumber():
> Number('0b111')7> Number('0o10')8Number.parseInt() works fine with numbers that have a different base, as long as there is no special prefix and the parameterradix is provided:
> Number.parseInt('111', 2)7> Number.parseInt('10', 8)8Number propertiesThis section describes new properties that the constructorNumber has picked up in ECMAScript 6.
Four number-related functions are already available as global functions and have been added toNumber, as methods:isFinite andisNaN,parseFloat andparseInt. All of them work almost the same as their global counterparts, butisFinite andisNaN don’t coerce their arguments to numbers, anymore, which is especially important forisNaN. The following subsections explain all the details.
Number.isFinite(number)Number.isFinite(number) determines whethernumber is an actual number (neitherInfinity nor-Infinity norNaN):
> Number.isFinite(Infinity)false> Number.isFinite(-Infinity)false> Number.isFinite(NaN)false> Number.isFinite(123)trueThe advantage of this method is that it does not coerce its parameter to number (whereas the global function does):
> Number.isFinite('123')false> isFinite('123')trueNumber.isNaN(number)Number.isNaN(number) checks whethernumber is the valueNaN.
One ES5 way of making this check is via!==:
> const x = NaN;> x !== xtrueA more descriptive way is via the global functionisNaN():
> const x = NaN;> isNaN(x)trueHowever, this function coerces non-numbers to numbers and returnstrue if the result isNaN (which is usually not what you want):
> isNaN('???')trueThe new methodNumber.isNaN() does not exhibit this problem, because it does not coerce its arguments to numbers:
> Number.isNaN('???')falseNumber.parseFloat andNumber.parseIntThe following two methods work exactly like the global functions with the same names. They were added toNumber for completeness sake; now all number-related functions are available there.
Number.EPSILONEspecially with decimal fractions, rounding errors can become a problem in JavaScript3. For example, 0.1 and 0.2 can’t be represented precisely, which you notice if you add them and compare them to 0.3 (which can’t be represented precisely, either).
> 0.1 + 0.2 === 0.3falseNumber.EPSILON specifies a reasonable margin of error when comparing floating point numbers. It provides a better way to compare floating point values, as demonstrated by the following function.
functionepsEqu(x,y){returnMath.abs(x-y)<Number.EPSILON;}console.log(epsEqu(0.1+0.2,0.3));// true
Number.isInteger(number)JavaScript has only floating point numbers (doubles). Accordingly, integers are simply floating point numbers without a decimal fraction.
Number.isInteger(number) returnstrue ifnumber 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)falseJavaScript numbers have only enough storage space to represent 53 bit signed integers. That is, integersi in the range −253 <i < 253 aresafe. What exactly that means is explained momentarily. The following properties help determine whether a JavaScript integer is safe:
Number.isSafeInteger(number)Number.MIN_SAFE_INTEGERNumber.MAX_SAFE_INTEGERThe notion ofsafe integers centers on how mathematical integers are represented in JavaScript. In the range (−253, 253) (excluding the lower and upper bounds), JavaScript integers aresafe: there is a one-to-one mapping between them and the mathematical integers they represent.
Beyond this range, JavaScript integers areunsafe: two or more mathematical integers are represented as the same JavaScript integer. For example, starting at 253, JavaScript can represent only every second mathematical integer:
> Math.pow(2, 53)9007199254740992> 90071992547409929007199254740992> 90071992547409939007199254740992> 90071992547409949007199254740994> 90071992547409959007199254740996> 90071992547409969007199254740996> 90071992547409979007199254740996Therefore, a safe JavaScript integer is one that unambiguously represents a single mathematical integer.
Number properties related to safe integersThe two staticNumber properties specifying the lower and upper bound of safe integers could be defined as follows:
Number.MAX_SAFE_INTEGER=Math.pow(2,53)-1;Number.MIN_SAFE_INTEGER=-Number.MAX_SAFE_INTEGER;
Number.isSafeInteger() determines whether a JavaScript number is a safe integer and could be defined as follows:
Number.isSafeInteger=function(n){return(typeofn==='number'&&Math.round(n)===n&&Number.MIN_SAFE_INTEGER<=n&&n<=Number.MAX_SAFE_INTEGER);}
For a given valuen, this function first checks whethern is a number and an integer. If both checks succeed,n is safe if it is greater than or equal toMIN_SAFE_INTEGER and less than or equal toMAX_SAFE_INTEGER.
How can we make sure that results of computations with integers are correct? For example, the following result is clearly not correct:
> 9007199254740990 + 39007199254740992We have two safe operands, but an unsafe result:
> Number.isSafeInteger(9007199254740990)true> Number.isSafeInteger(3)true> Number.isSafeInteger(9007199254740992)falseThe following result is also incorrect:
> 9007199254740995 - 109007199254740986This time, the result is safe, but one of the operands isn’t:
> Number.isSafeInteger(9007199254740995)false> Number.isSafeInteger(10)true> Number.isSafeInteger(9007199254740986)trueTherefore, the result of applying an integer operatorop is guaranteed to be correct only if all operands and the result are safe. More formally:
isSafeInteger(a)&&isSafeInteger(b)&&isSafeInteger(aopb)
implies thata op b is a correct result.
“Clarify integer and safe integer resolution”, email by Mark S. Miller to the es-discuss mailing list.
Math functionalityThe global objectMath has several new methods in ECMAScript 6.
Math.sign(x)Math.sign(x) returns:
-1 ifx is a negative number (including-Infinity).0 ifx is zero4.+1 ifx is a positive number (includingInfinity).NaN ifx isNaN or not a number.Examples:
> Math.sign(-8)-1> Math.sign(3)1> Math.sign(0)0> Math.sign(NaN)NaN> Math.sign(-Infinity)-1> Math.sign(Infinity)1Math.trunc(x)Math.trunc(x) removes the decimal fraction ofx. Complements the other rounding methodsMath.floor(),Math.ceil() andMath.round().
> Math.trunc(3.1)3> Math.trunc(3.9)3> Math.trunc(-3.1)-3> Math.trunc(-3.9)-3You could implementMath.trunc() like this:
functiontrunc(x){returnMath.sign(x)*Math.floor(Math.abs(x));}
Math.cbrt(x)Math.cbrt(x) returns the cube root ofx (∛x).
> Math.cbrt(8)2A small fraction can be represented more precisely if it comes after zero. I’ll demonstrate this with decimal fractions (JavaScript’s numbers are internally stored with base 2, but the same reasoning applies).
Floating point numbers with base 10 are internally represented asmantissa × 10exponent. Themantissa has a single digit before the decimal dot and theexponent “moves” the dot as necessary. That means if you convert a small fraction to the internal representation, a zero before the dot leads to a smaller mantissa than a one before the dot. For example:
Precision-wise, the important quantity here is the capacity of the mantissa, as measured in significant digits. That’s why (A) gives you higher precision than (B).
Additionally, JavaScript represents numbers close to zero (e.g. small fractions) with higher precision.
Math.expm1(x)Math.expm1(x) returnsMath.exp(x)-1. The inverse ofMath.log1p().
Therefore, this method provides higher precision wheneverMath.exp() has results close to 1. You can see the difference between the two in the following interaction:
> Math.expm1(1e-10)1.00000000005e-10> Math.exp(1e-10)-11.000000082740371e-10The former is the better result, which you can verify by using a library (such asdecimal.js) for floating point numbers with arbitrary precision (“bigfloats”):
>varDecimal=require('decimal.js').config({precision:50});>newDecimal(1e-10).exp().minus(1).toString()'1.000000000050000000001666666666708333333e-10'
Math.log1p(x)Math.log1p(x) returnsMath.log(1 + x). The inverse ofMath.expm1().
Therefore, this method lets you specify parameters that are close to 1 with a higher precision. The following examples demonstrate why that is.
The following two calls oflog() produce the same result:
> Math.log(1 + 1e-16)0> Math.log(1 + 0)0In contrast,log1p() produces different results:
> Math.log1p(1e-16)1e-16> Math.log1p(0)0The reason for the higher precision ofMath.log1p() is that the correct result for1 + 1e-16 has more significant digits than1e-16:
> 1 + 1e-16 === 1true> 1e-16 === 0falseMath.log2(x)Math.log2(x) computes the logarithm to base 2.
> Math.log2(8)3Math.log10(x)Math.log10(x) computes the logarithm to base 10.
> Math.log10(100)2Emscripten pioneered a coding style that was later picked up byasm.js: The operations of a virtual machine (think bytecode) are expressed in static subset of JavaScript. That subset can be executed efficiently by JavaScript engines: If it is the result of a compilation from C++, it runs at about 70% of native speed.
The followingMath methods were mainly added to support asm.js and similar compilation strategies, they are not that useful for other applications.
Math.fround(x)Math.fround(x) roundsx to a 32 bit floating point value (float). Used by asm.js to tell an engine to internally use afloat value.
Math.imul(x, y)Math.imul(x, y) multiplies the two 32 bit integersx andy and returns the lower 32 bits of the result. This is the only 32 bit basic math operation that can’t be simulated by using a JavaScript operator and coercing the result back to 32 bits. For example,idiv could be implemented as follows:
functionidiv(x,y){return(x/y)|0;}
In contrast, multiplying two large 32 bit integers may produce a double that is so large that lower bits are lost.
Math.clz32(x)x. > Math.clz32(0b01000000000000000000000000000000) 1 > Math.clz32(0b00100000000000000000000000000000) 2 > Math.clz32(2) 30 > Math.clz32(1) 31Why is this interesting? Quoting “Fast, Deterministic, and Portable Counting Leading Zeros” by Miro Samek:
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.
Math.sinh(x)x.Math.cosh(x)x.Math.tanh(x)x.Math.asinh(x)x.Math.acosh(x)x.Math.atanh(x)x.Math.hypot(...values) > Math.hypot(3, 4) 5JavaScript’s integers have a range of 53 bits. That is a problem whenever 64 bit integers are needed. For example: In its JSON API, Twitter had to switch from integers to strings when tweet IDs became too large.
At the moment, the only way around that limitation is to use a library for higher-precision numbers (bigints or bigfloats). One such library isdecimal.js.
Plans to support larger integers in JavaScript exist, but may take a while to come to fruition.