【JavaScript】数値を3桁カンマ区切りで表示する方法|toLocaleString・Intl.NumberFormat・通貨・小数桁・パフォーマンス

【JavaScript】数値を3桁カンマ区切りで表示する方法|toLocaleString・Intl.NumberFormat・通貨・小数桁・パフォーマンス JavaScript

金額や件数のような大きな数値は、1234567のように並んでいると桁が読み取りにくく、表示の質を下げます。1,234,567のように3桁ごとにカンマを入れるだけで、ぐっと読みやすくなります。

JavaScriptには、この3桁区切りを正しく行うための標準機能が用意されています。自分で正規表現を書く前に、まずtoLocaleString()Intl.NumberFormatを押さえておけば、通貨表記や小数桁の制御まで安全にこなせます。

この記事では、最短の書き方から、通貨・小数・パーセント・コンパクト表記、入力フォームへのリアルタイム適用、そして浮動小数点やBigIntの注意点までを順番に解説します。

先に結論

  • 最短はnum.toLocaleString("ja-JP")です。引数なしでも動きますが、区切り文字は環境のロケールに依存します。
  • 同じ書式を繰り返し使うならIntl.NumberFormatのインスタンスを作って使い回すほうが高速です。
  • 通貨はstyle: "currency"で表示します。円は小数なし、ドルは小数2桁が既定です。
  • 小数桁はminimumFractionDigitsmaximumFractionDigitsでそろえます。
  • 正規表現でも書けますが、小数や負数、ロケールの考慮が増えるため、標準APIを第一候補にします。
  • フォームから受け取った値は文字列です。カンマを除去して数値へ変換してからフォーマットします。

ロケールを使った書式の全体像はIntl APIを使った日付・数値の国際化フォーマット、数値と文字列の相互変換は数値を文字列に変換する方法でも解説しています。

スポンサーリンク

toLocaleStringで3桁区切りにする(最短)

数値にはtoLocaleString()メソッドがあり、呼び出すだけで桁区切りを付けられます。最も手軽な方法です。

to-locale-string-basic.js
const price = 1234567;

console.log(price.toLocaleString("ja-JP")); // "1,234,567"
console.log(price.toLocaleString("en-US")); // "1,234,567"

// 変数に入った数値リテラルから直接呼ぶ場合は丸括弧が必要
console.log((1234567).toLocaleString("ja-JP")); // "1,234,567"
console.log(12345.678.toLocaleString("ja-JP")); // "12,345.678" 
引数なしの toLocaleString は環境のロケール依存

引数を省略したnum.toLocaleString()は、実行環境(ブラウザやサーバー)の既定ロケールに従います。日本語環境ではカンマ区切りでも、ドイツ語環境(de-DE)では1.234.567のように区切り文字が変わります。表示を固定したいなら、"ja-JP"のようにロケールを明示してください。

Intl.NumberFormatでより細かく制御する

Intl.NumberFormatは、桁区切りに加えて通貨・小数・パーセントなどの書式をまとめて指定できる仕組みです。toLocaleString()は内部的にこの機能を使っており、できることは基本的に同じです。

intl-number-format-basic.js
const formatter = new Intl.NumberFormat("ja-JP");

console.log(formatter.format(1234567));   // "1,234,567"
console.log(formatter.format(98765.4));   // "98,765.4"

// 一度作ったフォーマッタは何度でも使い回せる
const numbers = [1000, 25000, 3400000];
console.log(numbers.map((n) => formatter.format(n)));
// ["1,000", "25,000", "3,400,000"]
使い分けの目安

単発で1つの数値を整形するだけならtoLocaleString()が手軽です。同じ書式を繰り返し使う、配列をまとめて整形する、といった場合はIntl.NumberFormatのインスタンスを一度だけ作って使い回すほうが効率的です。理由は後半のパフォーマンスの項で説明します。

通貨として表示する(円・ドル)

金額表示では、style: "currency"currencyオプションを指定します。通貨ごとに記号の位置や既定の小数桁が変わり、それらも自動で処理されます。

currency-format.js
const jpy = new Intl.NumberFormat("ja-JP", {
  style: "currency",
  currency: "JPY"
});
console.log(jpy.format(1234567)); // "¥1,234,567"

const usd = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});
console.log(usd.format(1234.5)); // "$1,234.50"

// 記号ではなくコードで表示することもできる
const jpyCode = new Intl.NumberFormat("ja-JP", {
  style: "currency",
  currency: "JPY",
  currencyDisplay: "code"
});
console.log(jpyCode.format(1234567)); // "JPY 1,234,567" 

日本円(JPY)は小数を持たない通貨なので、既定で小数桁が付きません。一方、米ドル(USD)は小数2桁が既定のため、1234.5$1,234.50と表示されます。通貨ごとのルールを自分で覚える必要がないのが、この方法の利点です。

小数点以下の桁数をそろえる

金額や割合では、小数桁を一定にそろえたい場面が多くあります。minimumFractionDigitsで最小桁数、maximumFractionDigitsで最大桁数を指定します。

fraction-digits.js
const format2 = new Intl.NumberFormat("ja-JP", {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
});
console.log(format2.format(1234));     // "1,234.00"
console.log(format2.format(1234.5));   // "1,234.50"
console.log(format2.format(1234.567)); // "1,234.57"(四捨五入される)

// 整数に丸めて表示する
const format0 = new Intl.NumberFormat("ja-JP", {
  maximumFractionDigits: 0
});
console.log(format0.format(1234.6)); // "1,235" 
桁の切り詰めは「表示」だけ

maximumFractionDigitsで桁を減らすと四捨五入されますが、これはあくまで表示上の処理です。元の数値そのものは変わりません。計算に使う値を丸めたい場合は、表示とは別にMath.round()などで処理してください。小数計算そのものの注意点は四則演算と計算の基本で解説しています。

パーセント・コンパクト表記

Intl.NumberFormatは、桁区切り以外の表示にも対応します。割合を表すパーセント表記や、大きな数を短くまとめるコンパクト表記が代表例です。

percent-compact.js
// パーセント表記(0〜1の値を100倍して % を付ける)
const percent = new Intl.NumberFormat("ja-JP", {
  style: "percent",
  minimumFractionDigits: 1
});
console.log(percent.format(0.1234)); // "12.3%"

// コンパクト表記(万・億や K・M でまとめる)
const compactJa = new Intl.NumberFormat("ja-JP", {
  notation: "compact"
});
console.log(compactJa.format(12345));    // "1.2万"
console.log(compactJa.format(123456789)); // "1.2億"

const compactEn = new Intl.NumberFormat("en-US", {
  notation: "compact"
});
console.log(compactEn.format(12345)); // "12K" 

パーセント表記は、入力値を100倍して%を付けます。0.123412.3%になる点に注意してください。すでに12.34のような百分率の値を持っている場合は、style: "percent"を使わず、自分で%を付けます。

正規表現で手動フォーマットする(仕組みの理解)

標準APIが使えない古い環境や、仕組みを理解したい場合のために、正規表現での書き方も紹介します。整数部に対して、3桁ごとの位置にカンマを差し込みます。

regexp-format.js
function addCommas(num) {
  const [intPart, decimalPart] = String(num).split(".");
  const grouped = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  return decimalPart ? `${grouped}.${decimalPart}` : grouped;
}

console.log(addCommas(1234567));     // "1,234,567"
console.log(addCommas(1234567.89));  // "1,234,567.89"
console.log(addCommas(-1234567));    // "-1,234,567" 

ポイントは、小数点で分割して整数部だけに区切りを入れることです。整数部と小数部をまとめて正規表現にかけると、小数側にもカンマが入ってしまいます。負の符号は単語の境界として扱われるため、先頭にカンマは入りません。

正規表現は最後の手段にする

手動の正規表現は、ロケールごとの区切り文字、通貨記号、桁の丸め、指数表記などをすべて自分で考慮する必要があります。toLocaleString()Intl.NumberFormatはこれらを正しく処理してくれるため、特別な理由がない限り標準APIを使ってください。

入力フォームにリアルタイムで3桁区切りを適用する

金額入力欄で、入力しながらカンマを表示したい場合があります。注意点は、表示用のカンマ付き文字列計算に使う数値を分けて管理することです。

form-realtime-format.html
<input type="text" id="amount" inputmode="numeric">
<p>入力された金額: <span id="raw"></span> 円</p>
form-realtime-format.js
const input = document.getElementById("amount");
const raw = document.getElementById("raw");
const formatter = new Intl.NumberFormat("ja-JP");

input.addEventListener("input", () => {
  // カンマと数字以外を取り除いて数値化する
  const digits = input.value.replace(/[^\d]/g, "");

  if (digits === "") {
    input.value = "";
    raw.textContent = "0";
    return;
  }

  const value = Number(digits);
  input.value = formatter.format(value); // 表示はカンマ付き
  raw.textContent = String(value);       // 計算用は数値
});

ここでは整数の金額入力を想定し、数字以外をすべて除去してから整形しています。小数やマイナスを許可する場合は、除去する文字の条件を調整してください。フォームの値は常に文字列で届くため、文字列を数値型に変換する方法もあわせて確認しておくと安全です。

カーソル位置のずれについて

入力途中でカンマを差し込むと、文字数が変わってカーソルが末尾へ飛ぶことがあります。厳密に制御したい場合は、整形後にカーソル位置を計算して戻す処理を加えるか、入力中は素の数値のまま受け取り、フォーカスが外れたblurのタイミングで整形する方法が簡単で確実です。

数値変換とフォーマットの注意点

フォーマット以前に、対象が「正しい数値」であることが前提です。文字列やおかしな値を渡すと、意図しない結果になります。

format-edge-cases.js
// 文字列はそのままでは整形できない
console.log("1234567".toLocaleString()); // "1234567"(区切られない)
console.log(Number("1234567").toLocaleString("ja-JP")); // "1,234,567"

// NaN はそのまま "NaN" になる
console.log(Number("abc").toLocaleString("ja-JP")); // "NaN"

// 安全側に倒すなら数値かどうかを確認する
function formatNumber(value) {
  const num = Number(value);
  return Number.isFinite(num)
    ? num.toLocaleString("ja-JP")
    : "-";
}
console.log(formatNumber("1234567")); // "1,234,567"
console.log(formatNumber("abc"));     // "-" 

渡された値が数値か判断したいときは文字列が数値であるかを判定する方法が参考になります。次に、JavaScript特有の大きな数の扱いにも触れておきます。

large-number-bigint.js
// 安全に扱える整数の上限を超えると精度が失われる
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log((123456789012345678).toLocaleString("ja-JP"));
// "123,456,789,012,345,680"(末尾がずれる)

// 桁の大きい整数は BigInt を使うと正確
console.log(123456789012345678n.toLocaleString("ja-JP"));
// "123,456,789,012,345,678"

// Intl.NumberFormat も BigInt をそのまま整形できる
const formatter = new Intl.NumberFormat("ja-JP");
console.log(formatter.format(9007199254740993n));
// "9,007,199,254,740,993" 

金額の合計やIDなど、9007199254740991(約9000兆)を超える整数を1の位まで正確に表示したい場合は、数値型ではなくBigIntを使います。toLocaleString()Intl.NumberFormatBigIntに対応しています。

パフォーマンス:フォーマッタは使い回す

Intl.NumberFormatのインスタンス生成には、ロケールや書式の準備処理が伴います。ループの中で毎回生成すると、その分だけ無駄が積み重なります。一度作って使い回すのが基本です。

reuse-formatter.js
// 非効率:要素ごとにフォーマッタを作っている
const slow = bigArray.map((n) =>
  new Intl.NumberFormat("ja-JP").format(n)
);

// 効率的:フォーマッタは1つだけ作って使い回す
const formatter = new Intl.NumberFormat("ja-JP");
const fast = bigArray.map((n) => formatter.format(n));

少数の数値を1回整形するだけならtoLocaleString()でも体感差はありません。一覧表やグラフなど、何千件もの数値を繰り返し整形する場面では、フォーマッタの使い回しが効いてきます。アプリ全体で使う共通フォーマッタを1か所で定義しておくと、書式の統一にも役立ちます。

よくある失敗

引数なしのtoLocaleStringで区切り文字が環境依存になる

サーバーや利用者の環境によって区切り文字が変わり、表示が崩れることがあります。表示を固定したいなら"ja-JP"のようにロケールを必ず指定します。

文字列のまま整形しようとして区切られない

フォームやAPIから受け取った値は文字列です。"1234567".toLocaleString()は数値のメソッドではないため区切られません。Number()で数値に変換してから整形します。

表示用のカンマ付き文字列をそのまま計算に使う

"1,234,567"Number()に渡すとNaNになります。表示はカンマ付き、計算は素の数値、と役割を分けて保持してください。

大きな整数を数値型のまま扱って末尾がずれる

Number.MAX_SAFE_INTEGERを超える整数は、数値型では正確に保持できません。1の位まで正しく表示する必要があるならBigIntを使います。

小数の丸めを表示だけで済ませて計算とずれる

maximumFractionDigitsは表示を丸めるだけで、元の値は変わりません。合計や検算に使う値は、表示とは別に丸め処理を行ってください。

よくある質問

Q結局どの方法を使えばいいですか?
A単発の整形ならnum.toLocaleString("ja-JP")、通貨や小数桁の制御、繰り返し使う場合はIntl.NumberFormatを使ってください。正規表現は標準APIが使えない特別な状況に限定するのがおすすめです。
QtoLocaleStringとIntl.NumberFormatはどちらが速いですか?
A1回だけの整形ならほぼ差はありません。多数の数値を繰り返し整形する場合は、Intl.NumberFormatのインスタンスを使い回すほうが速くなります。toLocaleStringは内部で都度フォーマッタ相当の処理を行うためです。
Qカンマ区切りを解除して数値に戻すには?
A"1,234,567".replace(/,/g, "")でカンマを取り除き、Number()で数値に変換します。小数点や符号を含む場合は、除去する文字の条件に注意してください。
Q全角の数字が入力されても整形できますか?
Aそのままでは数値に変換できません。先に全角数字を半角へ正規化するか、入力時に半角のみ受け付ける必要があります。フォームではinputmode="numeric"の指定もあわせて検討してください。
Q区切りを入れずに数値だけ表示するには?
AIntl.NumberFormatuseGrouping: falseを指定すると、桁区切りなしで表示できます。状況に応じて区切りの有無を切り替えられます。

まとめ

  • 最短はnum.toLocaleString("ja-JP")です。表示を固定するためロケールを明示します。
  • 通貨・小数・パーセント・コンパクト表記まで含めるならIntl.NumberFormatを使います。
  • 同じ書式を繰り返し使うときは、フォーマッタを1つ作って使い回します。
  • 正規表現でも書けますが、考慮事項が多いため標準APIを第一候補にします。
  • フォームの値は文字列です。カンマ除去と数値変換をしてから整形します。
  • 9000兆を超える整数を正確に表示するにはBigIntを使います。

桁区切りは小さな処理ですが、金額や件数の見やすさを大きく左右します。標準APIを使えば、ロケールや通貨のルールを正しく反映したうえで、表示と計算を安全に分けて扱えます。