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