ponyfoo.com

ES6 Math Additions in Depth

You’ve made it! Here’s another article in the ES6 – “What? I’d rather develop for IE6” – in Depth series. If you’ve never been around here before, start with A Brief History of ES6 Tooling. Then, make your way through destructuring, template literals, arrow functions, the spread operator and rest parameters, improvements coming to object literals, the new classes sugar on top of prototypes, let, const, and the “Temporal Dead Zone”, iterators, generators, Symbols, Maps, WeakMaps, Sets, and WeakSets, proxies, proxy traps, more proxy traps, reflection, and Number. Today we’ll learn about new Math methods.

Improve this article
Nicolás Bevacqua
| 18 minute read | 1

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
  • Exponentiation and Logarithmic
    • Math.cbrt – cubic root of value, or ∛‾value
    • Math.expm1e to the value minus 1, or evalue - 1
    • Math.log1p – natural logarithm of value + 1, or ln(value + 1)
    • Math.log10 – base 10 logarithm of value, or log10(value)
    • Math.log2 – base 2 logarithm of value, or log2(value)
  • Trigonometry
  • Bitwise
    • Math.clz32 – leading zero bits in the 32-bit representation of a number
  • Compile-to-JavaScript
    • Math.imulC-like 32-bit multiplication
    • Math.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.

'What really grinds my gears', on Family Guy
'What really grinds my gears', on Family Guy

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.

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments (1)

lps wrote

The multiplication of two 32-bit integers is 64-bits wide. Does Math.imul return the lower 32-bit part, the 53-bit float part, or all 64-bits?