JavaScript

JavaScriptのMath.imul():32ビット整数の乗算で安定した振り分けを作る

この記事でわかること

Math.imul()で32ビット整数の乗算を行う方法を解説。通常の掛け算との違い、文字列を使った振り分け例、ブラウザーとNode.jsの実行手順、期待出力と入力の制約を学べます。

文字列から一定の数値を作り、同じ項目を毎回同じグループに振り分けたい場合があります。たとえば、管理画面で名前に応じて表示色を決めたり、テスト用データを複数の処理先へ割り当てたりする場面です。

このような処理で整数を繰り返し掛け合わせるときに使えるのが、Math.imul()です。この記事では、32ビット整数として掛け算する仕組みを確認し、文字列を決まった番号へ振り分ける小さな例を作ります。

Math.imul()の目的と基本仕様

Math.imul()は、2つの値について32ビット整数の乗算を行い、符号付き32ビット整数として結果を返す静的メソッドです。Mathのインスタンスを作らず、直接呼び出します。MDNのMath.imul()

JavaScript
Math.imul(a, b);
  • aは掛け算する1つ目の値です。
  • bは掛け算する2つ目の値です。
  • 戻り値の型はnumberです。
  • 結果の範囲は-2147483648から2147483647です。

通常の掛け算では、3 * 4は12です。Math.imul(3, 4)も12になります。ただし、大きな値を掛けた場合の扱いが異なります。

桁あふれはエラーにならない

32ビットでは、すべての大きな整数を保持できません。Math.imul()は積の下位32ビットを残し、そのビット列を符号付き整数として返します。範囲を超えたことを知らせる例外は発生しません。

たとえば、2147483647を2倍すると、通常の計算では4294967294になります。同じ積を32ビットの符号付き整数として読むと-2になります。負数になっても、APIの計算失敗ではありません。

この性質は、一定のビット幅で値を更新する処理に適しています。一方、商品の合計金額や数量など、積の大きさをそのまま保持したい計算には適しません。

動作環境とバージョン

確認日は2026年10月14日です。MDNでは、Math.imul()は2015年7月以降、主要ブラウザーで広く利用できる機能とされています。本記事は現行のChrome、Edge、Firefox、Safariを対象にします。古いブラウザーや組み込みWebViewでは、利用前に機能の有無を確認してください。MDNの互換性説明

Node.jsでも利用できます。本記事の実行環境はNode.js 22系または24系とし、追加パッケージや実験的フラグは使いません。この指定は記事の対象バージョンであり、APIが初めて追加されたバージョンを意味しません。Node.jsはV8を通じてJavaScriptの言語機能を提供します。Node.jsのECMAScript対応説明

JavaScript
console.log(typeof Math.imul === "function");

利用できる環境ではtrueが表示されます。falseの場合は、この記事のコードをそのまま実行せず、対応する環境へ切り替えてください。

ブラウザーとNode.jsで共有できるように、以降のコードはDOM、通信、ファイル読み込みを使いません。

基本コードを実行する

次のコードは、そのままコピーして実行できます。全体を即時実行関数で囲んでいるため、ブラウザーのコンソールで繰り返し実行しても、外側に変数を残しません。

JavaScript
(() => {
  "use strict";

  if (typeof Math.imul !== "function") {
    throw new Error("この環境はMath.imul()に対応していません");
  }

  console.log(`通常の積: ${2147483647 * 2}`);
  console.log(`32ビットの積: ${Math.imul(2147483647, 2)}`);
  console.log(`小数の入力: ${Math.imul(3.9, 2)}`);
  console.log(`範囲を超える入力: ${Math.imul(4294967296, 7)}`);
})();

ブラウザーでの実行方法

対応ブラウザーで通常のWebページを開き、開発者ツールのConsoleを選びます。上のJavaScript全体を貼り付けて実行してください。HTMLファイルやサーバーを用意する必要はありません。

Node.jsでの実行方法

Node.jsをインストールした環境で、上のコードをimul-basic.cjsとして保存します。ターミナルで保存先のフォルダーへ移動し、次のコマンドを実行してください。

Shell
node --version
node imul-basic.cjs

最初のコマンドで22系または24系であることを確認し、次のコマンドでコードを実行します。ファイル名をnodeへ渡す方法は、公式の実行手順に沿っています。Node.jsのスクリプト実行方法

期待出力と読み方

CODE
通常の積: 4294967294
32ビットの積: -2
小数の入力: 6
範囲を超える入力: 0

小数部分は整数への変換時に取り除かれます。そのため、3.9と2の例は7.8ではなく6になります。また、4294967296は2の32乗なので、32ビットへの変換後は0として扱われます。

これらは仕様に基づいて導いた期待出力です。本記事の作成時にコードを実行した結果ではありません。

活用例:文字列を4つのグループへ振り分ける

次に、文字列を先頭から読み、途中の値を31倍して文字の値を足す処理を作ります。ここでは学習用の簡単なハッシュ計算として扱います。

JavaScript
(() => {
  "use strict";

  if (typeof Math.imul !== "function") {
    throw new Error("この環境はMath.imul()に対応していません");
  }

  function bucketFor(text) {
    if (typeof text !== "string") {
      throw new TypeError("textには文字列を指定してください");
    }

    let hash = 0;

    for (let index = 0; index < text.length; index += 1) {
      hash = (Math.imul(hash, 31) + text.charCodeAt(index)) | 0;
    }

    return (hash >>> 0) % 4;
  }

  const labels = ["", "a", "b", "abc", "abc"];

  for (const label of labels) {
    const display = label === "" ? "(空文字)" : label;
    console.log(`${display}: ${bucketFor(label)}`);
  }

  try {
    bucketFor(123);
  } catch (error) {
    if (error instanceof TypeError) {
      console.log("入力エラー: textには文字列を指定してください");
    } else {
      throw error;
    }
  }
})();

ブラウザーでは基本コードと同様にConsoleで実行します。Node.jsでは、このコードだけをimul-bucket.cjsとして保存して実行してください。

Shell
node imul-bucket.cjs

期待出力は次のとおりです。

CODE
(空文字): 0
a: 1
b: 2
abc: 2
abc: 2
入力エラー: textには文字列を指定してください

計算の流れを追う

bucketFor()の引数textは文字列で、戻り値は0から3までの整数です。不正な型を渡すとTypeErrorを投げます。空文字は受け入れ、0を返す設計です。

abcでは、UTF-16コード単位の値として97、98、99を順番に使います。途中の値は97、3105、96354となり、最後に4で割った余りが2になります。

掛け算にMath.imul()を使い、足し算の後も| 0で32ビットへ収めています。最後の>>> 0は、ビット列を符号なし32ビット整数として扱うための補助です。これにより、途中の値が負数でも振り分け番号を負にしません。MDNの符号なし右シフト

同じ文字列と同じ計算規則なら、同じ番号が得られます。ただし、文字列が異なっていても同じ番号になることがあります。例でも、bとabcは同じグループです。

失敗例と制約

通常の掛け算の後で変換しても同じとは限らない

大きな積では、通常の掛け算の時点で下位の情報が丸められることがあります。後から32ビットへ変換しても、失われた情報は戻りません。

JavaScript
console.log(Math.imul(4294967295, 4294967295));
console.log((4294967295 * 4294967295) | 0);
CODE
1
0

数学的な積は18446744065119617025です。通常のnumberによる掛け算では、この整数を正確に保持できず、丸められます。32ビットの積が必要な処理では、掛け算そのものにMath.imul()を使ってください。

型変換を入力チェックの代わりにしない

Math.imul()は入力が業務上正しい整数かどうかを検証する関数ではありません。小数や文字列が渡されても、自動変換によって計算が進む場合があります。入力ミスを見逃したくない処理では、呼び出す前に型と許容範囲を検証します。

BigIntを引数へ渡すとTypeErrorになります。大きな整数を桁あふれなしで計算する用途では、BigInt同士の通常の乗算を検討してください。

振り分け例の用途を限定する

今回の計算は、暗号学的なハッシュではありません。パスワードの保存、改ざん検知、一意なIDの生成には使えません。また、4つのグループへ均等に分散することも保証しません。

文字列はUTF-16コード単位で処理するため、絵文字を含む場合も人が見た文字数とは異なる単位で計算されます。表記の違う文字列を同一視したい場合は、入力をそろえる規則を別途決める必要があります。

この例は同期処理です。Promiseや通信がないため、awaitは不要です。例外は入力検証で明示的に扱っていますが、大量の長い文字列を連続処理すると、ブラウザーの画面更新やNode.jsのほかの処理を待たせる可能性があります。

実務での使いどころと改善案

名前ごとの色分けや、再現性のあるテストデータの振り分けでは、このような小さな計算が役立ちます。実際に表示色へ結び付ける場合は、4色の配列を用意し、bucketFor()の戻り値を配列の位置として使えます。

改善するなら、振り分け結果を保存する前に、代表的な入力で分布を確認してください。グループ数や計算規則を変えると既存の割り当ても変わるため、永続的な割り当てが必要なら、計算結果を記録する設計も検討します。

公式参考資料

PREFERRED SOURCES

Googleの優先するニュース提供元にSEの部屋を追加

Googleで、いつも読みたい情報源を選べます。登録可否はGoogleの画面で確認できます。

Googleの設定画面で確認する 新しいタブで開きます

候補にSEの部屋が表示されない場合は、まだ追加できません。

y.
WRITTEN BY

y_ymo10

SEの部屋で、HTML・CSSからJavaScript・React・Next.jsまで、Web制作の開発ノートを公開しています。

ほかの開発ノートを読む →