Math.imul()

Функція Math.imul() повертає результат C-подібного 32-бітного множення двох аргументів.

Спробуйте його в дії

Синтаксис

Math.imul(a, b)

Параметри

a

Перше число.

b

Друге число.

Повернене значення

Результат C-подібного 32-бітного множення переданих двох аргументів.

Опис

Math.imul() дає змогу множити 32-бітні цілі числа з традиційною для C семантикою. Цей функціонал корисний для таких проєктів, як Emscripten.

Оскільки imul() — це статичний метод об'єкта Math, його потрібно завжди використовувати через Math.imul(). Не слід звертатись до нього як до методу власноруч створеного екземпляра Math (Math не є конструктором).

Якщо використати звичайні для JavaScript числа з рухомою комою в imul, ви помітите суттєву деградацію продуктивності коду. Це відбувається у зв'язку з дорогим перетворенням чисел з рухомою комою на цілі числа для множення, і наступною конвертацією цілочисельного результату множення назад у число з рухомою комою. Причина існування imul полягає в тому, що ця функція швидша в одному (поки що) разі, а саме — AsmJS. AsmJS дозволяє JIT-оптимізаторам простіше реалізовувати цілі числа всередині JavaScript. Множення двох чисел, внутрішньо збережених як цілі числа (що можливо лише з AsmJS) через imul — це єдина ситуація, де Math.imul може бути ефективним в сучасних браузерах.

Приклади

Застосування Math.imul()

Math.imul(2, 4);          // 8
Math.imul(-1, 8);         // -8
Math.imul(-2, -2);        // 4
Math.imul(0xffffffff, 5); // -5
Math.imul(0xfffffffe, 5); // -10

Поліфіл

Цей функціонал можна відтворити за допомогою наступної функції:

if (!Math.imul) Math.imul = function(a, b) {
  var aHi = (a >>> 16) & 0xffff;
  var aLo = a & 0xffff;
  var bHi = (b >>> 16) & 0xffff;
  var bLo = b & 0xffff;
  // зміщення на 0 фіксує знак у старшій частині
  // кінцеве |0 перетворює беззнакове значення на число зі знаком
  return ((aLo * bLo) + (((aHi * bLo + aLo * bHi) << 16) >>> 0) | 0);
};

Однак, наступна функція — більш продуктивна, оскільки є ймовірність, що ті браузери, всередині яких цей поліфіл може використовуватись, не застосовують цілочисельний тип для оптимізації в JavaScript, натомість використовуючи числа з рухомою комою для всіх значень.

if (!Math.imul) Math.imul = function(opA, opB) {
  opB |= 0; // впевнімося, що opB — ціле число. opA буде приведено автоматично.
  // Числа з рухомою комою дають нам 53 бітів точності, в межах яким ми можемо працювати,
  // плюс 1 знаковий біт, який, зручно для користувача, обробляється автоматично:
  // 1. 0x003fffff /*opA & 0x000fffff*/ * 0x7fffffff /*opB*/ = 0x1fffff7fc00001
  //    0x1fffff7fc00001 < Number.MAX_SAFE_INTEGER /*0x1fffffffffffff*/
  var result = (opA & 0x003fffff) * opB;
  // 2. Ми можемо прибрати зведення до цілого з наведеної вище інструкції, оскільки:
  //    0x1fffff7fc00001 + 0xffc00000 = 0x1fffffff800001
  //    0x1fffffff800001 < Number.MAX_SAFE_INTEGER /*0x1fffffffffffff*/
  if (opA & 0xffc00000 /*!== 0*/) result += (opA & 0xffc00000) * opB |0;
  return result |0;
};

Специфікації

Сумісність із браузерами

desktop mobile server
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
imul
Chrome Full support 28
Edge Full support 12
Firefox Full support 20
Internet Explorer No support No
Opera Full support 16
Safari Full support 7
WebView Android Full support 4.4
Chrome Android Full support 28
Firefox for Android Full support 20
Opera Android Full support 15
Safari on iOS Full support 7
Samsung Internet Full support 1.5
Deno Full support 1.0
Node.js Full support 0.12.0

Дивіться також