JavaScriptで文字列を数値に変換するとき、文字列全体が数値の形かを厳密に確かめたいなら Number()、"12px" のように先頭の数字だけ取り出したいなら parseInt(文字列, 10) や parseFloat() を使います。 両者のいちばん大きな違いは「数値として読めない文字が混ざったときに、全体を NaN にするか、読めたところまでを返すか」です。

この記事では、Number・parseInt・parseFloat・単項プラス(+)・Math.trunc の5手法に、空文字・null・全角数字・カンマ区切り・16進数表記など23種類の入力を渡した結果を、Node.js 22.22.2 で実際に動かして1枚の表にしました。あわせて、toFixed が文字列を返すこと、(1.005).toFixed(2) が "1.00" になる丸め誤差とその対策、Number.isNaN での判定、フォーム入力を安全に数値化する関数まで、コピーしてそのまま使える形で紹介します。

文字列→数値変換の早見表

まずは「やりたいこと」から選べる早見表です。

やりたいこと 書き方 "42" "3.14" "12px" ""(空文字) 向いている場面
文字列全体を厳密に数値へ Number(str) 42 3.14 NaN 0 入力チェック、API の値
先頭の整数だけ取り出す parseInt(str, 10) 42 3 12 NaN "12px" など単位つき
先頭の小数まで取り出す parseFloat(str) 42 3.14 12 NaN "1.5rem" など単位つき
短く書いて厳密に変換 +str 42 3.14 NaN 0 Number() と同じ結果
変換して小数部を切り捨て Math.trunc(str) 42 3 NaN 0 厳密変換+整数化
数値を小数N桁の文字列に num.toFixed(2) "42.00" "3.14" ― ― 表示用(戻り値は文字列)
変換できたか判定する Number.isNaN(n) false false true(Number の結果に対して) false 変換後のチェック
有限の数値か判定する Number.isFinite(n) true true false true NaN と Infinity をまとめて除外

迷ったら「Number() で変換し、Number.isFinite() で確かめる」が基本形です。空文字が 0 になる点だけは、変換前に自分でチェックします(後半にコピペ用の関数があります)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScript文字列→数値変換|parseIntとNumberの違い一覧表」

5手法×入力23種の実行結果一覧表

次のスクリプトを Node.js 22.22.2 で実行し、出力をそのまま表にしています。parseInt は基数に 10 を指定しています。

// compare.js
const inputs = [
  "42", "3.14", "  42  ", "", " ", null, undefined, "12px", "px12", "1,000",
  "123", "0x10", "1e3", "010", "-5.7", "+5", ".5", "1_000", true,
  "Infinity", [], "0b101", "12.5.3",
];
const methods = [
  (v) => Number(v),
  (v) => parseInt(v, 10),
  (v) => parseFloat(v),
  (v) => +v,
  (v) => Math.trunc(v),
];
for (const v of inputs) {
  console.log(JSON.stringify(v), methods.map((f) => String(f(v))).join(" | "));
}
入力 Number(v) parseInt(v, 10) parseFloat(v) +v Math.trunc(v)
"42" 42 42 42 42 42
"3.14" 3.14 3 3.14 3.14 3
" 42 "(前後に空白) 42 42 42 42 42
""(空文字) 0 NaN NaN 0 0
" "(空白だけ) 0 NaN NaN 0 0
null 0 NaN NaN 0 0
undefined NaN NaN NaN NaN NaN
"12px" NaN 12 12 NaN NaN
"px12" NaN NaN NaN NaN NaN
"1,000"(カンマ区切り) NaN 1 1 NaN NaN
"123"(全角数字) NaN NaN NaN NaN NaN
"0x10"(16進数表記) 16 0 0 16 16
"1e3"(指数表記) 1000 1 1000 1000 1000
"010"(先頭ゼロ) 10 10 10 10 10
"-5.7" -5.7 -5 -5.7 -5.7 -5
"+5" 5 5 5 5 5
".5" 0.5 NaN 0.5 0.5 0
"1_000"(アンダースコア) NaN 1 1 NaN NaN
true 1 NaN NaN 1 1
"Infinity" Infinity NaN Infinity Infinity Infinity
[](空配列) 0 NaN NaN 0 0
"0b101"(2進数表記) 5 0 0 5 5
"12.5.3" NaN 12 12.5 NaN NaN

表から読み取れる5つのポイント

  1. Number()・単項プラス・Math.trunc() は同じ規則で文字列を読みます。 3つとも内部では同じ「数値への変換」を行うため、NaN になる入力が完全に一致しています。Math.trunc() はその結果から小数部を取り除くだけです。
  2. parseInt() と parseFloat() は「読めるところまで読む」方式です。 "12px" は 12、"1,000" は 1、"12.5.3" は 12 や 12.5 になります。エラーにも NaN にもならないので、カンマ区切りの "1,000" が 1 として通ってしまう点は知っておくと安心です。
  3. 空文字・空白だけ・null は、Number() 系では 0 になります。 未入力の欄が 0 として扱われる原因の多くはここにあります。parseInt 系は NaN を返すため、未入力に気づきやすい反面、"12px" も通します。
  4. 全角数字はどの手法でも NaN です。 日本語入力のまま打たれた "123" は、変換前に半角へそろえる必要があります(normalize("NFKC") を後述します)。
  5. "0x10" と "1e3" は手法によって結果が割れます。 Number("0x10") は 16、parseInt("0x10", 10) は 0 です。"1e3" は Number と parseFloat が 1000、parseInt が 1 になります。

NumberとparseIntの違いを整理する

上位の解説でよく比べられる2つを、観点ごとに並べます。右端に parseFloat も添えました。

観点 Number(str) parseInt(str, 10) parseFloat(str)
読み方 文字列全体が数値の形かを確かめる 先頭から読める整数部分だけ読む 先頭から読める小数まで読む
余計な文字が後ろにある NaN 読めたところまで返す 読めたところまで返す
小数 そのまま保持 小数点以下を読まない(切り捨てと同じ結果) そのまま保持
空文字・空白だけ 0 NaN NaN
null 0 NaN NaN
基数(進数)の指定 できない(0x・0o・0b の接頭辞は解釈) 第2引数で 2〜36 を指定 できない(10進数のみ)
指数表記 "1e3" 1000 1 1000
引数が文字列でないとき 型ごとの規則で数値化 いったん文字列にしてから読む いったん文字列にしてから読む
主な用途 入力値の厳密な変換 単位つき文字列、進数変換 単位つき文字列(小数)

「入力が正しい数値かを確かめたい」なら Number()、「文字列の先頭から数値を取り出したい」なら parseInt()/parseFloat() と覚えると選びやすくなります。

Number():文字列全体が数値のときだけ変換する

console.log(Number("42"));      // 42
console.log(Number("12.50"));   // 12.5
console.log(Number("\n42\t"));  // 42(前後の空白・改行・タブは無視)
console.log(Number("12 34"));   // NaN(途中の空白は不可)
console.log(Number("1."));      // 1
console.log(Number("-"));       // NaN
console.log(Number("1e1000"));  // Infinity(表現できる範囲を超える)

Number() は前後の空白を取り除いたうえで、残り全体が数値リテラルの形になっているかを確かめます。1文字でも読めない文字があれば NaN です。「怪しい入力は NaN にしてくれる」ので、入力チェックと相性がよい変換です。

文字列以外を渡したときの結果も一度見ておきましょう(Node.js 22.22.2 の実行結果)。

式 結果 理由
Number(true) / Number(false) 1 / 0 真偽値は 1 と 0
Number(null) 0 null は 0
Number(undefined) NaN undefined は NaN
Number([]) 0 空配列 → 空文字 → 0
Number([5]) 5 "5" として読まれる
Number([1, 2]) NaN "1,2" として読まれる
Number({}) NaN "[object Object]" として読まれる
Number(new Date(0)) 0 日付はミリ秒のタイムスタンプ
Number(1n) 1 BigInt は Number() なら変換できる

parseInt():先頭の整数を取り出す。基数は必ず書く

console.log(parseInt("12px", 10));     // 12
console.log(parseInt("  -42abc", 10)); // -42
console.log(parseInt("3.99", 10));     // 3(小数点で読み取りが止まる)
console.log(parseInt("abc", 10));      // NaN(先頭が数字でない)

第2引数の基数(何進数として読むか)は、毎回 10 と書くのがおすすめです。 省略すると、文字列が 0x で始まる場合に16進数として読まれます。

console.log(parseInt("0x1f"));      // 31(基数を省略すると 0x を16進数と解釈)
console.log(parseInt("0x1f", 10));  // 0 (10進数として読むと "0" で止まる)
console.log(parseInt("08"));        // 8 (現在の仕様では10進数として読まれる)

基数を指定すると、進数変換にも使えます。

式 結果 説明
parseInt("101", 2) 5 2進数として読む
parseInt("ff", 16) 255 16進数として読む
parseInt("z", 36) 35 36進数(0〜9とa〜z)
parseInt("9", 8) NaN 8進数に 9 は存在しない
parseInt("10", 1) NaN 基数は 2〜36 の範囲
parseInt("10", 37) NaN 同上
parseInt("-0x10", 16) -16 符号と 0x 接頭辞を解釈

逆に、数値を各進数の文字列にするには toString(基数) を使います。

console.log((255).toString(16)); // "ff"
console.log((5).toString(2));    // "101"

parseInt()に数値を渡すと起きること

parseInt は引数をまず文字列に変換します。そのため、とても小さい数や大きい数を直接渡すと、指数表記の文字列を経由して思わぬ値になります。

console.log(parseInt(0.0000005)); // 5  ("5e-7" として読まれる)
console.log(parseInt(1e21));      // 1  ("1e+21" として読まれる)
console.log(parseInt(null, 36));  // 1112745("null" を36進数として読む)

数値の小数部を落としたいときは、parseInt ではなく Math.trunc() を使うと意図どおりになります。

map(parseInt)が [1, NaN, NaN] になる理由

配列の文字列をまとめて数値にするときの定番のつまずきです。

console.log(["1", "2", "3"].map(parseInt));
// [ 1, NaN, NaN ]

map はコールバックに「要素・インデックス・配列」を渡します。parseInt は第2引数を基数として受け取るので、実際には parseInt("1", 0)、parseInt("2", 1)、parseInt("3", 2) が実行されています。基数 1 は範囲外、2進数に 3 は存在しないため NaN になります。

// 直したあと
console.log(["1", "2", "3"].map(Number));                  // [ 1, 2, 3 ]
console.log(["1", "2", "3"].map((s) => parseInt(s, 10)));  // [ 1, 2, 3 ]

引数を1つだけ受け取る Number を渡すか、アロー関数で包むと解決します。

parseFloat():先頭の小数を取り出す

console.log(parseFloat("1.5rem"));   // 1.5
console.log(parseFloat("1.5e3px"));  // 1500(指数表記も読む)
console.log(parseFloat("-.5"));      // -0.5
console.log(parseFloat("12.5.3"));   // 12.5(2つ目の小数点で止まる)
console.log(parseFloat("0x10"));     // 0   (16進数表記は読まない)

parseFloat には基数の引数がなく、常に10進数として読みます。なお、Number.parseInt と Number.parseFloat はグローバルの parseInt/parseFloat と同じ関数です(Number.parseInt === parseInt は true)。

単項プラス(+):Number()と同じ結果を短く書く

const str = "42";
console.log(+str);        // 42
console.log(+"");         // 0
console.log(+"12px");     // NaN
console.log(typeof +str); // "number"

単項プラスは Number() と同じ変換を行います。短く書ける一方で、a + +b のように足し算と並ぶと読み取りにくくなるため、読む人にやさしいのは Number() です。違いが出るのは BigInt で、Number(1n) は 1 を返しますが、+1n は次のエラーになります。

TypeError: Cannot convert a BigInt value to a number

Math.trunc():変換と整数化を同時に行う

console.log(Math.trunc("12.7"));  // 12
console.log(Math.trunc("-5.7"));  // -5(0に近づく方向へ切り捨て)
console.log(Math.floor("-5.7"));  // -6(小さい方向へ切り捨て)
console.log(Math.trunc("12px"));  // NaN(Number と同じ厳密さ)

Math.trunc() は「Number() で変換してから小数部を取り除く」動きです。parseInt("12.7", 10) と同じ 12 を返しつつ、"12px" や "1,000" を通さないので、「整数がほしい、でも余計な文字は受け付けたくない」ときに向いています。

整数化の方法を並べると次のようになります(実行結果)。

式 "12.7" "-5.7" 補足
Math.trunc(v) 12 -5 小数部を取り除く
Math.floor(v) 12 -6 小さい方向へ
Math.ceil(v) 13 -5 大きい方向へ
Math.round(v) 13 -6 四捨五入(.5 は大きい方向へ)
parseInt(v, 10) 12 -5 文字列の先頭だけ読む
~~v 12 -5 32ビット整数に収まる範囲のみ

~~v や v | 0 はビット演算を利用した短い書き方ですが、32ビット整数に変換されるため、~~"3000000000" は -1294967296 になります。大きな値を扱う可能性があるなら Math.trunc() を選ぶと安心です。

暗黙の型変換:「+」だけは文字列連結になる

<input> の value は必ず文字列です。そのまま計算すると、演算子によって結果が変わります。

const quantity = "3";
const unitPrice = "250";

console.log(quantity + unitPrice);                 // "3250"(文字列連結)
console.log(Number(quantity) + Number(unitPrice)); // 253
console.log(quantity * unitPrice);                 // 750 (* は数値に変換される)
console.log(quantity - unitPrice);                 // -247(- も数値に変換される)
console.log("5" + 2);                              // "52"
console.log("5" - 2);                              // 3

+ は片方が文字列なら連結、-・*・/ は数値に変換して計算します。足し算だけ結果が違うので、計算の前に Number() で明示的に変換しておくと、どの演算子でも同じ考え方で読めるコードになります。

比較や並べ替えでも同じことが起きます。

console.log("10" < "9");                    // true(文字列どうしは辞書順)
console.log(["10", "9", "100"].sort());     // [ '10', '100', '9' ]
console.log(["10", "9", "100"].map(Number).sort((a, b) => a - b)); // [ 9, 10, 100 ]

NaNの判定はNumber.isNaNとNumber.isFiniteで行う

変換に成功したかどうかは NaN かどうかで確かめます。ただし NaN は === で比較できません。

const n = Number("abc");
console.log(n === NaN);       // false(NaN は自分自身とも等しくない)
console.log(Number.isNaN(n)); // true

判定用の関数は4つあり、グローバル版と Number. 版で動きが違います。

式 isNaN()(グローバル) Number.isNaN()
NaN true true
"abc" true(数値に変換してから判定) false(数値型の NaN ではない)
undefined true false
"" / " " / null false(0 に変換される) false
式 isFinite()(グローバル) Number.isFinite()
42 true true
"42" true(数値に変換してから判定) false(文字列は対象外)
Infinity false false
NaN false false

Number.isNaN() と Number.isFinite() は引数を変換せず、「数値型で、かつ NaN か/有限か」だけを判定します。 変換と判定を分けて書けるので、結果を読み違えにくくなります。

// 変換 → 判定の基本形
const n = Number(input);
if (Number.isFinite(n)) {
  // n は NaN でも Infinity でもない、計算に使える数値
}

Number.isFinite() は NaN と ±Infinity をまとめて除外できるため、入力チェックでは Number.isNaN() よりも Number.isFinite() のほうが1回で済みます。 整数かどうかは Number.isInteger()、正確に表せる整数の範囲かどうかは Number.isSafeInteger() で確かめます。

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

toFixedは数値ではなく文字列を返す

toFixed(桁数) は、数値を小数点以下N桁の文字列にするメソッドです。MDN の Number.prototype.toFixed() のページにも、戻り値は文字列で、桁数は 0〜100 と記載されています。

const n = 1.5;
console.log(typeof n.toFixed(1)); // "string"
console.log((12.3).toFixed(2));     // "12.30"
console.log((12.3).toFixed(2) + 1); // "12.301"(文字列連結になる)
console.log((25).toFixed(1));       // "25.0"

toFixed の結果にそのまま足し算をすると、文字列連結になります。「計算はすべて数値で済ませ、表示する直前に toFixed を1回だけ使う」という順番にすると、連結の心配がなくなります。数値として使い続けたいときは Number() で戻します。

const rounded = Number((0.1 + 0.2).toFixed(2));
console.log(rounded);        // 0.3
console.log(typeof rounded); // "number"

桁数に範囲外の値を渡すと、次のエラーになります(Node.js 22.22.2)。

RangeError: toFixed() digits argument must be between 0 and 100

また、1e21 以上の値は固定小数点にならず、(1e21).toFixed(2) は "1e+21" を返します。

(1.005).toFixed(2) が "1.00" になる丸め誤差を再現する

四捨五入なら "1.01" になりそうな式が、実際には "1.00" を返します。

console.log((1.005).toFixed(2));  // "1.00"
console.log((1.255).toFixed(2));  // "1.25"
console.log((10.235).toFixed(2)); // "10.23"
console.log((8.345).toFixed(2));  // "8.35"(こちらは繰り上がる)

理由は、1.005 という値が2進数の浮動小数点ではぴったり表せず、実際にはわずかに小さい値として保持されているためです。桁数を増やして表示すると確かめられます。

console.log((1.005).toFixed(20));
// "1.00499999999999989342"

保持されている値が 1.00499… なので、小数第3位は「4」として扱われ、切り捨てられます。toFixed 自体は保持されている値を正しく丸めており、誤差は「10進数の小数を2進数で持つ」という数値の仕組みから生まれています。MDN にも (2.55).toFixed(1) が "2.5" になる例が載っています。

Math.round を使った定番の書き方でも、同じ理由で同じ結果になります。

console.log(1.005 * 100);                    // 100.49999999999999
console.log(Math.round(1.005 * 100) / 100);  // 1

対策1:指数表記の文字列で桁をずらしてから丸める

掛け算で桁をずらすと誤差が入るので、"1.005e2" という文字列を作って数値に読み直すことで、誤差を入れずに桁をずらします。

// round.js
function roundHalfUp(value, digits = 0) {
  if (!Number.isFinite(value)) return NaN;
  const sign = value < 0 ? -1 : 1;
  const shifted = Number(`${Math.abs(value)}e${digits}`); // 文字列の指数表記で桁をずらす
  const rounded = Number(`${Math.round(shifted)}e${-digits}`);
  return sign * rounded;
}

function toFixedSafe(value, digits = 0) {
  return roundHalfUp(value, digits).toFixed(digits);
}

for (const v of [1.005, 1.255, 8.345, 10.235, -1.005, 2.5, 1.1, 0.1 + 0.2, 1234.5678]) {
  console.log(v, v.toFixed(2), roundHalfUp(v, 2), toFixedSafe(v, 2));
}

実行結果(Node.js 22.22.2)を表にまとめます。

値 v.toFixed(2) roundHalfUp(v, 2) toFixedSafe(v, 2)
1.005 "1.00" 1.01 "1.01"
1.255 "1.25" 1.26 "1.26"
8.345 "8.35" 8.35 "8.35"
10.235 "10.23" 10.24 "10.24"
-1.005 "-1.00" -1.01 "-1.01"
2.5 "2.50" 2.5 "2.50"
1.1 "1.10" 1.1 "1.10"
0.1 + 0.2 "0.30" 0.3 "0.30"
1234.5678 "1234.57" 1234.57 "1234.57"

この関数のポイントは3つあります。

  • 符号を分けて絶対値を丸めているため、-1.005 が -1.01 になります(Math.round(-2.5) は -2 になるので、符号を分けないと正負で丸め方がそろいません)。
  • roundHalfUp は数値、toFixedSafe は表示用の文字列を返します。計算の途中では数値、表示には文字列と使い分けられます。
  • 1.5e-7 のように、文字列にしたときすでに指数表記になる値を渡すと NaN を返します(実行して確認)。通常の桁数の値に使う関数として割り切り、戻り値を Number.isFinite() で確かめる使い方がおすすめです。

なお、Math.round((v + Number.EPSILON) * 100) / 100 という書き方もよく紹介されます。今回試した 1.005・1.255・8.345・10.235 では 1.01・1.26・8.35・10.24 になりましたが、値が大きくなると補正が届かなくなる場合があるため、上の文字列方式のほうが適用範囲を説明しやすい方法です。

対策2:最初から整数で計算する

価格や数量のように小数点以下の桁数が決まっている値は、最小単位の整数に直してから計算し、表示のときだけ小数に戻すと、誤差の出る場面そのものを減らせます。

const price = "19.99";
const cents = Math.round(Number(price) * 100); // 1999(整数で保持)
const total = cents * 3;                       // 5997
console.log((total / 100).toFixed(2));         // "59.97"

対策3:表示だけならIntl.NumberFormatも使える

桁区切りつきで表示したいときは Intl.NumberFormat が便利です。

const fmt = new Intl.NumberFormat("ja-JP", {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});
console.log(fmt.format(1.005));                                   // "1.01"
console.log(new Intl.NumberFormat("ja-JP").format(1234567.891));  // "1,234,567.891"
console.log((1234.5).toLocaleString("ja-JP"));                    // "1,234.5"

Node.js 22.22.2 では fmt.format(1.005) は "1.01" になりました。toFixed とは丸めの計算方法が異なるため結果が変わることがあります。同じ画面の中では、どちらか一方に統一すると表示がそろいます。

小数を扱うメソッドの比較表

メソッド 戻り値の型 1234.5678 の例 用途
toFixed(2) 文字列 "1234.57" 小数点以下の桁数をそろえる
toPrecision(6) 文字列 "1234.57" 有効桁数をそろえる
Math.round(n) 数値 1235 整数に四捨五入
Math.trunc(n) 数値 1234 小数部を取り除く
roundHalfUp(n, 2)(本記事) 数値 1234.57 小数N桁で四捨五入
Intl.NumberFormat 文字列 "1,234.568"(既定は小数3桁まで) 桁区切りつきの表示

フォーム入力を安全に数値化する関数(コピペ用)

ここまでの内容をまとめて、全角数字・カンマ区切り・前後の空白に対応し、未入力や形式の違いを理由つきで返す関数にします。

// to-number.js
function toNumber(input, { integer = false, min = -Infinity, max = Infinity } = {}) {
  if (typeof input === "number") {
    return Number.isFinite(input)
      ? { ok: true, value: input }
      : { ok: false, reason: "数値として扱えません" };
  }
  if (typeof input !== "string") return { ok: false, reason: "文字列を渡してください" };

  const s = input
    .normalize("NFKC")          // 全角数字・全角記号を半角へ
    .replace(/[\s,]/g, "")      // 空白とカンマ区切りを除く
    .replace(/[−ー―‐]/g, "-"); // 全角系のマイナス・長音を半角ハイフンへ

  if (s === "") return { ok: false, reason: "未入力です" };
  if (!/^[+-]?(\d+\.?\d*|\.\d+)$/.test(s)) {
    return { ok: false, reason: "数値の形式ではありません" };
  }

  const n = Number(s);
  if (!Number.isFinite(n)) return { ok: false, reason: "数値として扱えません" };
  if (integer && !Number.isInteger(n)) return { ok: false, reason: "整数で入力してください" };
  if (integer && !Number.isSafeInteger(n)) return { ok: false, reason: "桁数が大きすぎます" };
  if (n < min || n > max) return { ok: false, reason: `${min}〜${max} の範囲で入力してください` };
  return { ok: true, value: n };
}

Node.js 22.22.2 での実行結果です。

入力 結果
"42" {"ok":true,"value":42}
" 1,000 " {"ok":true,"value":1000}
"123"(全角) {"ok":true,"value":123}
"-5"(全角マイナス) {"ok":true,"value":-5}
"ー5"(長音記号) {"ok":true,"value":-5}
"99.5" {"ok":true,"value":99.5}
".5" {"ok":true,"value":0.5}
"" / " " {"ok":false,"reason":"未入力です"}
"12px" {"ok":false,"reason":"数値の形式ではありません"}
"1e3" {"ok":false,"reason":"数値の形式ではありません"}
"0x10" {"ok":false,"reason":"数値の形式ではありません"}
"1.2.3" {"ok":false,"reason":"数値の形式ではありません"}
toNumber("3.5", { integer: true }) {"ok":false,"reason":"整数で入力してください"}
toNumber("150", { min: 1, max: 100 }) {"ok":false,"reason":"1〜100 の範囲で入力してください"}
toNumber("99999999999999999999", { integer: true }) {"ok":false,"reason":"桁数が大きすぎます"}
toNumber(null) {"ok":false,"reason":"文字列を渡してください"}

この関数の設計ポイント

処理 ねらい
normalize("NFKC") "123" → "123"、"1,000" → "1,000" のように全角を半角へそろえる
空白とカンマの除去 " 1,000 " を "1000" にする
空文字のチェックを Number() の前に行う 空文字が 0 になるのを防ぐ
正規表現で形式を確認 "1e3"・"0x10"・"Infinity" など、Number() が受け付ける別表記を対象外にする
Number.isFinite() NaN と Infinity を除外する
Number.isSafeInteger() 正確に表せる整数の範囲(±9007199254740991)を超える入力を知らせる
{ ok, value } / { ok, reason } を返す 0 という正しい入力と「変換できない」を取り違えない

戻り値を数値と NaN だけにせず、成功・不成功を ok で返すのがコツです。if (!value) と書くと正しい入力の 0 まで弾いてしまうため、ok を見る形にしておくと判定がぶれません。

HTMLフォームでの使い方

<label>数量 <input id="qty" type="text" inputmode="numeric" autocomplete="off"></label>
<p id="msg" role="alert"></p>
<script>
  // toNumber() を上のコードからコピーして、この前に置きます
  const qty = document.getElementById("qty");
  const msg = document.getElementById("msg");
  qty.addEventListener("change", () => {
    const result = toNumber(qty.value, { integer: true, min: 1, max: 99 });
    msg.textContent = result.ok ? `数量: ${result.value}` : result.reason;
  });
</script>

inputmode="numeric" を付けると、スマートフォンで数字キーボードが開きやすくなります。このHTML全体はブラウザでは動かしていません(未実行)。toNumber() 本体は上の表のとおり Node.js で確認しています。

type="number" のinputとvalueAsNumber

<input type="number"> でも value は文字列です。数値で受け取りたいときは valueAsNumber が使えます。Chromium 141(Playwright 1.56)で確かめた結果は次のとおりです。

入力欄の状態 value valueAsNumber validity.badInput
未入力 "" NaN false
12 を入力 "12" 12 false
1.50 を入力 "1.50" 1.5 false
1e3 を入力 "1e3" 1000 false
1e まで入力(途中の状態) "" NaN true

未入力のとき valueAsNumber は NaN、Number(input.value) は 0 になります。未入力と 0 を区別したい場面では valueAsNumber と Number.isNaN() の組み合わせが便利です。なお type="text" の入力欄では valueAsNumber は常に NaN でした。

大きな整数と小数の精度

JavaScript の数値は64ビットの浮動小数点数です。整数として正確に表せるのは Number.MAX_SAFE_INTEGER(9007199254740991)までで、それを超えると隣の値と区別できなくなります。

console.log(Number("9007199254740993"));          // 9007199254740992
console.log(9007199254740992 === 9007199254740993); // true
console.log(BigInt("9007199254740993").toString()); // "9007199254740993"
console.log(Number.isSafeInteger(Number("9007199254740993"))); // false

桁数の多いIDや番号は、数値に変換せず文字列のまま扱うのが確実です。計算が必要な大きな整数には BigInt を使います。BigInt は Math.trunc(1n) や +1n に渡すと TypeError: Cannot convert a BigInt value to a number になるため、通常の数値と混ぜずに扱います。

小数では 0.1 + 0.2 が 0.30000000000000004 になることがよく知られています。これも 1.005 の例と同じ仕組みで、表示するときに toFixed(2)(結果は "0.30")で整えるか、整数に直して計算します。

逆引き:やりたいこと別の書き方

やりたいこと 書き方 結果
文字列を整数にする(厳密に) Math.trunc(Number("12.7")) 12
"12px" から数値を取り出す parseInt("12px", 10) 12
"1.5rem" から数値を取り出す parseFloat("1.5rem") 1.5
カンマ区切りを数値にする Number("1,000".replaceAll(",", "")) 1000
全角数字を数値にする Number("123".normalize("NFKC")) 123
16進数の文字列を数値にする parseInt("ff", 16) 255
2進数の文字列を数値にする parseInt("101", 2) 5
配列の文字列をまとめて数値にする ["1", "2"].map(Number) [1, 2]
小数第2位で四捨五入(数値) roundHalfUp(1.005, 2) 1.01
小数2桁の文字列にする (12.3).toFixed(2) "12.30"
ゼロ埋めの文字列にする String(12).padStart(5, "0") "00012"
数値を文字列にする String(42) / (42).toString() "42"
変換できたか確かめる Number.isFinite(n) true / false
整数か確かめる Number.isInteger(n) true / false

よくあるつまずきと直し方

起きること 原因 直し方
"3" + "250" が "3250" になる + が文字列連結になっている Number() で変換してから足す
未入力なのに 0 として計算される Number("") が 0 変換前に空文字をチェックする
"1,000" が 1 になる parseInt がカンマで読み取りを止める カンマを除いてから Number()
全角で入力すると NaN 全角数字は数値として読まれない normalize("NFKC") で半角へ
map(parseInt) が [1, NaN, NaN] インデックスが基数として渡る map(Number) にする
n === NaN が常に false NaN は自分自身とも等しくない Number.isNaN(n) を使う
toFixed(2) + 1 が "12.301" toFixed の戻り値は文字列 計算後に toFixed を使う
(1.005).toFixed(2) が "1.00" 2進数で 1.005 を正確に持てない roundHalfUp で丸めてから表示
大きな番号の末尾が変わる 安全な整数の範囲を超えている 文字列のまま扱う/BigInt

よくある質問

Q. parseIntとNumberはどちらを使えばよいですか? A. 入力が正しい数値かを確かめたいなら Number()、"12px" のように先頭の数値を取り出したいなら parseInt(str, 10) です。Number() は余計な文字があると NaN を返すので入力チェックに向いています。空文字が 0 になる点だけ、変換前に確認を入れてください。

Q. parseIntの第2引数(基数)は省略してもよいですか? A. 10進数として読みたいなら 10 を書くのがおすすめです。省略すると "0x1f" のような文字列が16進数として読まれ、31 になります。map(parseInt) のように意図しない値が基数として渡る場面もあるため、毎回書いておくと読み手にも意図が伝わります。

Q. toFixedの結果を数値として使うにはどうすればよいですか? A. Number(n.toFixed(2)) のように Number() で戻します。ただし toFixed の丸めは保持されている値に対して行われるため、(1.005).toFixed(2) は "1.00" です。四捨五入の結果を数値でほしいときは、本記事の roundHalfUp() を使ってください。

Q. 小数点以下を切り捨てるにはどれを使いますか? A. 数値なら Math.trunc() です。負の数で小さい方向に切り捨てたいときは Math.floor() を使います(-5.7 は Math.trunc で -5、Math.floor で -6)。parseInt は文字列の先頭を読む関数なので、数値の切り捨てには Math.trunc() が向いています。

まとめ

  • Number()・単項プラス・Math.trunc() は文字列全体を厳密に読み、parseInt()・parseFloat() は先頭から読めるところまで読みます。
  • 空文字・空白だけ・null は Number() で 0、parseInt() で NaN になります。未入力のチェックは変換前に行います。
  • "1,000" は parseInt で 1、全角数字はどの手法でも NaN です。カンマを除き、normalize("NFKC") で半角にそろえてから変換します。
  • parseInt の基数は毎回 10 と書くと、0x の解釈や map(parseInt) のつまずきを避けられます。
  • 変換できたかの判定は Number.isFinite()、NaN だけを見たいときは Number.isNaN() を使います。
  • toFixed の戻り値は文字列です。計算を終えてから、表示の直前に使います。
  • (1.005).toFixed(2) は "1.00" になります。四捨五入したいときは、指数表記の文字列で桁をずらす roundHalfUp() か、整数に直して計算する方法を選びます。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScript文字列→数値変換|parseIntとNumberの違い一覧表」

株式会社RJCでは、JavaScriptの型変換や数値の扱い方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?