Like I did in previous articles on the series, I would love to point out that you should probably set up Babel and follow along the examples with either a REPL or the
babel-node
CLI and a file. That’ll make it so much easier for you to internalize the concepts discussed in the series. If you aren’t the “install things on my computer” kind of human, you might prefer to hop on CodePen and then click on the gear icon for JavaScript – they have a Babel preprocessor which makes trying out ES6 a breeze. Another alternative that’s also quite useful is to use Babel’s online REPL – it’ll show you compiled ES5 code to the right of your ES6 code for quick comparison.
Before getting into it, let me shamelessly ask for your support if you’re enjoying my ES6 in Depth series. Your contributions will go towards helping me keep up with the schedule, server bills, keeping me fed, and maintaining Pony Foo as a veritable source of JavaScript goodies.
Thanks for reading that, and let’s go into Math
improvements. For a bit of context you may want to look at the extensive article on Number
improvements from last week. Time to dig into Math
.
Math
Additions in ES6
There’s heaps of additions to Math
in ES6. Just like you’re used to, these are static methods on the Math
built-in. Some of these methods were specifically engineered towards making it easier to compile C into JavaScript, and you may never come across a need for them in day-to-day development – particularly not when it comes to front-end development. Other methods are complements to the existing rounding, exponentiation, and trigonometry API surface.
Below is a full list of methods added to Math
. They are grouped by functionality and sorted by relevance.
- Utility
Math.sign
– sign function of a numberMath.trunc
– integer part of a number
- Exponentiation and Logarithmic
Math.cbrt
– cubic root of value, or∛‾value
Math.expm1
–e
to thevalue
minus1
, orevalue - 1
Math.log1p
– natural logarithm ofvalue + 1
, orln(value + 1)
Math.log10
– base 10 logarithm ofvalue
, orlog10(value)
Math.log2
– base 2 logarithm ofvalue
, orlog2(value)
- Trigonometry
Math.sinh
– hyperbolic sine of a numberMath.cosh
– hyperbolic cosine of a numberMath.tanh
– hyperbolic tangent of a numberMath.asinh
– hyperbolic arc-sine of a numberMath.acosh
– hyperbolic arc-cosine of a numberMath.atanh
– hyperbolic arc-tangent of a numberMath.hypot
– square root of the sum of squares
- Bitwise
Math.clz32
– leading zero bits in the 32-bit representation of a number
- Compile-to-JavaScript
Math.imul
– C-like 32-bit multiplicationMath.fround
– nearest single-precision float representation of a number
Let’s get right into it.
Math.sign
Many languages have a Math.Sign
method (or equivalent) that returns a vector like -1
, 0
, or 1
, depending on the sign of the provided input. Surely then, you would think JavaScript’s Math.sign
method does the same. Well, sort of. The JavaScript flavor of this method has two more alternatives: -0
, and NaN
.
Math.sign(1)
// <- 1
Math.sign(0)
// <- 0
Math.sign(-0)
// <- -0
Math.sign(-30)
// <- -1
Math.sign(NaN)
// <- NaN
Math.sign('foo')
// <- NaN, because Number('foo') is NaN
Math.sign('0')
// <- 0, because Number('0') is 0
Math.sign('-1')
// <- -1, because Number('-1') is -1
This is just one of those methods. It grinds my gears. After all the trouble we went through to document how methods ported over to Number
, such as Number.isNaN
, don’t indulge in unnecessary type coercion, why is it that Math.sign
does coerce its input? I have no idea. Most of the methods in Math
share this trait, though. The methods that were added to Number
don’t.
Sure, we’re not a statically typed language, we dislike throwing exceptions, and we’re fault tolerant – after all, this is one of the founding languages of the web. But was not coercing everything into a Number
too much to ask? Couldn’t we just return NaN
for non-numeric values?
I’d love for us to get over implicit casting, but it seems we’re not quite there yet for the time being.
Math.trunc
One of the oddities in Math
methods is how abruptly they were named. It’s like they were trying to save keystrokes or something. After all, it’s not like we stopped adding super-precise method names like Object.getOwnPropertySymbols()
. Why trunc
instead of truncate
, then? Who knows.
Anyways, Math.trunc
is a simple alternative to Math.floor
and Math.ceil
where we simply discard the decimal part of a number. Once again, the input is coerced into a numeric value through Number(value)
.
Math.trunc(12.34567)
// <- 12
Math.trunc(-13.58)
// <- -13
Math.trunc(-0.1234)
// <- -0
Math.trunc(NaN)
// <- NaN
Math.trunc('foo')
// <- NaN, because Number('foo') is NaN
Math.trunc('123.456')
// <- 123, because Number('123.456') is 123.456
While it still coerces any values into numbers, at least it stayed consistent with Math.floor
and Math.ceil
, enough that you could use them to create a simple polyfill for Math.trunc
.
Math.trunc = function truncate (value) {
return value > 0 ? Math.floor(value) : Math.ceil(value)
}
Another apt example of how “succintly” Math
methods have been named in ES6 can be found in Math.cbrt
– although this one matches the pre-existing Math.sqrt
method, to be fair.
Math.cbrt
As hinted above, Math.cbrt
is short for “cubic root”. The examples below show the sorts of output it produces.
Math.cbrt(-1)
// <- -1
Math.cbrt(3)
// <- 1.4422495703074083
Math.cbrt(8)
// <- 2
Math.cbrt(27)
// <- 3
Not much explaining to do here. Note that this method also coerces non-numerical values into numbers.
Math.cbrt('8')
// <- 2, because Number('8') is 8
Math.cbrt('ponyfoo')
// <- NaN, because Number('ponyfoo') is NaN
Let’s move onto something else.
Math.expm1
This operation is the result of computing e
to the value
minus 1
. In JavaScript, the e
constant is defined as Math.E
. The method below is a rough equivalent of Math.expm1
.
function expm1 (value) {
return Math.pow(Math.E, value) - 1
}
The evalue
operation can be expressed as Math.exp(value)
as well.
function expm1 (value) {
return Math.exp(value) - 1
}
Note that this method has higher precision than merely doing Math.exp(value) - 1
, and should be the preferred alternative.
expm1(1e-20)
// <- 0
Math.expm1(1e-20)
// <- 1e-20
expm1(1e-10)
// <- 1.000000082740371e-10
Math.expm1(1e-10)
// <- 1.00000000005e-10
The inverse function of Math.expm1
is Math.log1p
.
Math.log1p
This is the natural logarithm of value
plus 1
, – ln(value + 1)
– and the inverse function of Math.expm1
. The base e
logarithm of a number can be expressed as Math.log
in JavaScript.
function log1p (value) {
return Math.log(value + 1)
}
This method is more precise than executing the Math.log(value + 1)
operation by hand, just like the Math.expm1
case.
log1p(1.00000000005e-10)
// <- 1.000000082690371e-10
Math.log1p(1.00000000005e-10)
// <- 1e-10, exactly the inverse of Math.expm1(1e-10)
Next up is Math.log10
.
Math.log10
Base ten logarithm of a number – log10(value)
.
Math.log10(1000)
// <- 3
You could polyfill Math.log10
using the Math.LN10
constant.
function log10 (value) {
return Math.log(x) / Math.LN10
}
And then there’s Math.log2
.
Math.log2
Base two logarithm of a number – log2(value)
.
Math.log2(1024)
// <- 10
You could polyfill Math.log2
using the Math.LN2
constant.
function log2 (value) {
return Math.log(x) / Math.LN2
}
Note that the polyfilled version won’t be as precise as Math.log2
in some cases. Remember that the <<
operator means “bitwise left shift”.
Math.log2(1 << 29)
// <- 29
log2(1 << 29)
// <- 29.000000000000004
Naturally, you could use Math.round
or Number.EPSILON
to get around rounding issues.
Math.sinh
Returns the hyperbolic sine of value
.
Math.cosh
Returns the hyperbolic cosine of value
.
Math.tanh
Returns the hyperbolic tangent of value
.
Math.asinh
Returns the hyperbolic arc-sine of value
.
Math.acosh
Returns the hyperbolic arc-cosine of value
.
Math.atanh
Returns the hyperbolic arc-tangent of value
.
Math.hypot
Returns the square root of the sum of the squares of the arguments.
Math.hypot(1, 2, 3)
// <- 3.741657386773941
We could polyfill Math.hypot
by doing the operations manually. We can use Math.sqrt
to compute the square root and Array.prototype.reduce
combined with the spread operator to sum the squares. I’ll throw in an arrow function for good measure!
function hypot (...values) {
return Math.sqrt(values.reduce((sum, value) => sum + value * value, 0))
}
Surprisingly, our handmade method is more precise than the native one (at least on Chrome 45) for this case in particular.
Math.hypot(1, 2, 3)
// <- 3.741657386773941
hypot(1, 2, 3)
// <- 3.7416573867739413
And now for the “really fun” methods!
Math.clz32
Definitely not immediately obvious, but the name for this method is an acronym for “count leading zero bits in 32-bit binary representations of a number”. Remember that the <<
operator means “bitwise left shift”, and thus…
Math.clz32(0)
// <- 32
Math.clz32(1)
// <- 31
Math.clz32(1 << 1)
// <- 30
Math.clz32(1 << 2)
// <- 29
Math.clz32(1 << 29)
// <- 2
Cool, and also probably the last time you’re going to see that method in use for the foreseeable future. For completeness’ sake, I’ll add a sentence about the pair of methods that were added mostly to aid with asm.js
compilation of C programs. I doubt you’ll be using these directly, ever.
Math.imul
Returns the result of a C-like 32-bit multiplication.
Math.fround
Rounds value
to the nearest 32-bit float representation of a number.
Conclusions
Some nice methods rounding out the Math
API. It would’ve been nice to see additions to the tune of more flavors of Math.random
and similar utilities that end up being implemented by libraries in almost every large-enough application, such as Lodash’es _.random
and _.shuffle
.
That being said, any help towards making asm.js
faster and more of a reality are desperately welcome additions to the language.
Comments