16

Does it perform better than * expressions? Why?

This article describes it as being useful for projects like Emscripten. Why is that?

Thanks

Matthew
  • 2,210
  • 1
  • 19
  • 30

3 Answers3

27

Short version

Math.imul(a,b) multiplies a and b as though they were 32 bit signed integers. (a*b)|0 does the same, but may give incorrect results for large numbers.

Long version

You would use Math.imul whenever you want to multiply two numbers, and you want the multiplication to act as though the two numbers were 32 bit signed integers. That is, you want integer multiplication modulo 2^32.

Normally, you can get this effect by using a bitwise operator on the result. For instance, (0xffffffff + 1)|0 quite correctly gives you 0. However, this doesn't work for multiplication. (0x7fffffff * 0x7fffffff)|0 gives us 0, when 32 bit multiplication of the two numbers would give us 1. The reason for this failure is precision. The Number type is a IEEE754 double precision floating point number, which has 53 bits of mantissa. When you try to use numbers larger than 2^53, you start losing precision. You can test this by running Math.pow(2,53) == Math.pow(2,53)+1 in your browser.

When you multiply two 32 bit numbers in Javascript and truncate the result using a bitwise operator the intermediate result may be larger than 2^53 and may thus be incorrect, which means that the end result of course will also be incorrect.

valderman
  • 8,365
  • 4
  • 22
  • 29
  • 3
    One should note that 0xffffffff is not a large 32-bit signed integer, because 0xffffffff is -1. The maximum signed value is 0x7fffffff. The rest of the answer is correct since (0x7fffffff * 0x7fffffff)|0 gives you 0 when it should be 1 (which is why you should use Math.imul). (0xffffffff|0)*(0xffffffff|0)|0 evaluates correctly to 1. – Joa Ebert Mar 18 '15 at 11:42
  • 4
    Oops, fixed. Curious that it took a whole year for anyone to notice. – valderman Mar 18 '15 at 21:14
  • why there is no "Math.iadd" for integer addition? – neoexpert Jun 15 '20 at 06:50
  • 1
    Probably because `Math.iadd(a, b)` would be equivalent to `(a + b)|0`. The largest number you can obtain by adding two 32 bit integers is ~2^32, which is well below the 2^53 bound. – valderman Jun 15 '20 at 18:36
-1

I just did a quick benchmark in Chrome, on a Windows 10 computer.

First, I ran this code a bunch of times :

var result;
for(var i = 0; i < 100000000; i++) {
    result = 2 * 4;
}

(see also this Fiddle)

Then, I ran this code a bunch of times :

var result;
for(var i = 0; i < 100000000; i++) {
    result = Math.imul(2, 4);
}

(see also this Fiddle)

In both cases, execution time averages around 60ms. So, at least in Chrome, there's no performance gained or lost from using just Math.imul().

So why use it? Well, there's probably few to no use cases where you'll ever want to use it in handwritten JavaScript. You will, however, see it a lot in asm.js code that's generated by emscripten.

Asm.js is a very strict subset of JavaScript that's practically impossible to handcode by any human developer, but that can be generated relatively easily when compiling from C/C++ to JavaScript. Asm.js code is much closer to machine code than ordinary JavaScript code, which allowed browsers to heavily optimise for any code written in asm.js. In browsers that implemented those optimizations, your code will typically run about 50% of the speed of a C/C++ program that is compiled to machine code. That may seem slow, but it's a hell of a lot faster than any ordinary JavaScript.

Anyway, what I'm saying, is that there is literally no reason to use Math.imul() in your handwritten code unless, perhaps, if you're a C/C++ programmer still trying to get used to JavaScript. Using Math.imul() really has no benefits, except when you're writing asm.js code, and that's something you're not supposed to do as a human developer in the first place, because asm.js is both very hard to read and has very strict syntax rules!

For more info on asm.js, see eg. John Resig's article from 2013 or the official specs.

John Slegers
  • 45,213
  • 22
  • 199
  • 169
  • 1
    `Math.imul` is not a replacement for JS's native multiplication. `Math.imul(0xDEADBEEF, 0xCAFEBABE) !== 0xDEADBEEF * 0xCAFEBABE` It is essentially a **native** multiplication with overflow, achieving the same result as C on 32-bit registers. `Math.imul` is much faster than any JS shim that simulates it. This is useful when porting math-heavy C code to JS that expects this behavior. – bryc Feb 10 '18 at 18:37
  • 1
    You might notice your benchmarks are invariant of whether you actually do any multiplication. That is simply not how to benchmark things, you are not going to get useful results. The rest of your answer is also wrong. – Veedrac May 12 '18 at 15:07
  • @Veedrac : You lost me there. So what exactly is it that's wrong with my benchmarks in your opinion? And why do you believe the rest of my answer is wrong? Can you give me one good reason to ever use 32-bit integer multiplication in handwritten JavaScript code?! Please do enlighten me on whichever use cases I'm overlooking here... – John Slegers May 13 '18 at 12:37
  • [Can we continue this in chat?](https://chat.stackoverflow.com/rooms/170957/discussion-between-veedrac-and-john-slegers) – Veedrac May 13 '18 at 14:43
-4

I think it performs better because it's more low level, its implementation may go "down to the metal". With the 32-bit restriction the multiplication could be done directly with the processor with less intermediate abstractions/conversions.

Cheers

Edgar Villegas Alvarado
  • 18,204
  • 2
  • 42
  • 61
  • 1
    On the contrary, _it's worse, because it's a function; you have to call it._ – Sapphire_Brick Feb 11 '20 at 23:32
  • 1
    @Sapphire_Brick A Browser can optimize that. It could automatically compile "Math.imul" to native multiplication instruction. – neoexpert Jun 15 '20 at 06:49
  • @neoexpert A browser can optimize the standard multiplication operator in the same way. What's your point? – Sapphire_Brick Jun 15 '20 at 19:54
  • 2
    No it can't optimise standard multiplication "in the same way". imul multiplies an integer. So it just need to cast the operands to integer types. "*" would be a multiplication between two doubles or maybe integers. This depends on the context. – neoexpert Jul 14 '20 at 19:34