JavaScript で文字列を数値に変換する方法まとめ(加筆予定)
JavaScript で文字列を数値に変換する方法の例
-
parseInt(x, 10)
=Number.parseInt(x, 10)
-
parseFloat(x)
=Number.parseFloat(x)
Number(x)
+x
~~x
x >>> 0
-
valueAsNumber
(input form の onChange コールバック関数のEventで)
実行結果のまとめ
("parseInt vs unary plus, when to use which? (Stackoverflow)" より)
仕様
parseFloat
parseFloat(x)
の(やや雑な)動作概略:
-
をx で文字列に変換する(\mathrm{ToString} は文字列の場合はそのまま)x -
[1] で先頭の空白文字を除去する。\mathrm{TrimString}( \cdot , \mathrm{START}) -
の構文を満たす("-Infinity", "123_456", "12e+3" などにマッチする)最長の prefix を\mathit{StrDecimalLiteral} とする。もしそのような prefix が無ければ\mathit{trimmedPrefix} NaN
を返す。 -
の\mathit{trimmedPrefix} を返す。\mathit{StringNumericValue}
まとめ
-
parseFloat
は入力文字列の空白を除く先頭部分のみを数値として解釈する(" 123.45foo"
→123.45
) -
null
,undefined
,true
,false
などはいずれも、文字列化した結果の先頭に にマッチする部分が無いため\mathit{StrDecimalLiteral} NaN
になる。 -
parseInt("0x100")
は 256 だがparseFloat("0x100")
は0
になる。
parseInt
parseInt(x, radix)
の(やや雑な)動作概略:
-
をx で文字列に変換する(\mathrm{ToString} は文字列の場合はそのまま)x -
[1:1] で先頭の空白文字を除去する。\mathrm{TrimString}( \cdot , \mathrm{START}) -
"-"
始まりなら 、そうでなければ\mathit{sign} = -1 とする。\mathit{sign} = 1 - 先頭の
"-"
や"+"
文字を除いた部分文字列を とする。S - 基数 radix を
で 32bit 整数にマップする(\mathrm{ToInt32} Infinity
やNaN
は0
にマップされ、小数は0
方向に丸められる)。これを とする。R -
のときR = 0 とする。そうでないとき、R = 10 またはR < 2 のときは36 < R NaN
を返す。 -
に「明示的に」値がセットされなかった場合やR の場合、R = 16 がS "0x"
または"0X"
で始まる文字列だった場合は とし、R = 16 から先頭2文字を取り除く。S - (TODO: 余力があれば追記)
-
をZ の最長の prefix であって "radix-S digit" からなるものとする。R - "radix-
digit" ...R 0-9a-z
のうち、 番目までの文字のこと。例えばR ならR = 3 '0', '1', '2'
、 ならR = 36 0-9a-z
となる。
- "radix-
-
が空文字ならZ NaN
を返す。 -
を radix-\mathit{mathInt} 表記のR の整数値とする。Z -
(However, if R = 10 and Z contains more than 20 significant digits, every significant digit after the 20th may be replaced by a 0 digit, at the option of the implementation; and if R is not one of 2, 4, 8, 10, 16, or 32, then mathInt may be an implementation-approximated integer representing the integer value denoted by Z in radix-R notation.)
-
-
を返す。\mathit{sign} \times \mathit{mathInt}
まとめ
-
parseFloat
と同様、入力文字列の空白を除く先頭部分のみを数値として解釈する(" 123.45foo"
→123
) - 正負は先頭の
"-"
の有無だけで決まる。 - 0x, 0b など
-
parseInt
は"0x"
("0X"
)prefix を考慮する。 -
parseInt("0x1")
は1
に評価される。 -
parseInt("0x1", 16)
は1
に評価される。 -
parseInt("0x1", 36)
は 1189 に評価される。('x'
= 33 という文字として解釈される) -
parseInt("0z", 36)
は35
に評価される。 "0b111"
は2進法表記の7
としては解釈されず、 radix が 11 以下の場合 0、 radix が 12 以上の場合'b'
= 12 という文字として解釈される。
-
-
parseFloat
は0x
始まりの文字列を解釈せず0
に評価するが、parseInt
は0x
始まりの文字列を解釈できるので、parseInt
はparseFloat
の小数部無視バージョンではない。 - 細かい仕様
- radix に
36.9
などを入れても36
と解釈されて動く。1.9
などは 2 未満で不正な入力扱いとなりNaN
が返される。
- radix に
Number
Number(x)
の(やや雑な)動作概略:
-
を\mathit{prim} とする。\mathrm{ToNumeric}(x)
加筆予定
valueAsNumber
type
に
- number
- range
- date
- month
- week
- time
- datetime-local
のいずれかを付与している input
要素で使用可能( type="text"
などの場合は数値を入力していても NaN
になる)
import * as React from 'react';
const NumericInputExample = () => {
const [num, setNum] = React.useState(0);
const onChange = React.useCallback(
(ev: React.ChangeEvent<HTMLInputElement>) => {
setNum(ev.target.valueAsNumber);
},
[],
);
return (
<div>
<input type='number' value={num} onChange={onChange} />
<div>{num}</div>
</div>
);
};
(type="date"
などのケースは本記事の調査対象外のため)type="number"
の input 要素のイベントの場合を調べる。
parse アルゴリズムの仕様: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-floating-point-number-values
valueAsNumber
を計算する parse 処理の(やや雑な)動作概略:
\mathit{value} \gets 1 \mathit{divisor} \gets 1 \mathit{exponent} \gets 1 - 先頭の空白文字を無視する
-
'-'
文字始まりなら を\mathit{divisor} に-1 - 先頭の
"-"
や"+"
文字を除いた部分文字列を とする。S - 以降加筆予定です 🙇♂️
使い分け方の考察
- 以下は
no-implicit-coercion
や@typescript-eslint/restrict-plus-operands
で禁止して良さそう。-
+x
(これは特に大体Number(x)
と似た結果っぽいので、文字列連結構文との曖昧性回避のためにそちらを使う方が安全そう) ~~x
x >>> 0
-
-
parseInt(x)
: 基数を設定したいときに使用できる。"11.9999999"
とかは に評価されてしまうことなどに注意が必要そう。11 - 以下の三つはどれを使っても普通の浮動小数点数のパースには大体同じように使用できそうだが、若干
Number
か(使えるときは)valueAsNumber
を使うのが無難そう?-
parseFloat(x)
-
実用上は、
"123.foo"
などの全体としては数値ではない文字列も prefix だけパースできてしまうことがある点に一番注意が必要かも。 うっかり渡すものを間違えてそのまま動いてしまうので気づかないという状況に注意したい。 -
"0x"
始まりの文字列を解釈できない点にも注意が必要だが、そういう入力を受け付けることは実用上そんなに無い気もする。
-
実用上は、
-
Number(x)
- 文字列は大体よしなに数値に変換してくれそうだが
true
,false
,null
なども や0 に評価して動いてしまう点に逆に注意が必要かも。ただし1 undefined
を渡すとNaN
が返るところがややこしい。文字列入力想定ならこの辺は踏む心配は無い。 -
parseFloat
と違い"123foo"
とかはNaN
になってくれるので fail fast の観点で安心。
- 文字列は大体よしなに数値に変換してくれそうだが
-
valueAsNumber
- 想定している数値文字列に対しては
Number(x)
と同じ挙動をしそう?(仕様要確認) - 数値入力欄のフォームに入力できる文字列を変換するロジックなので特に変な挙動は想定せず使えそう。
-
Number(event.target.value)
とかわざわざするよりはevent.target.valueAsNumber
を取り出した方が綺麗な気がする(パフォーマンスの細かい違いとかは調べ切れていない)。 - ただ
type="text"
のときは常にNaN
に評価されるので<input type="text">
を数値入力欄として使っているときは使用できない。
- 想定している数値文字列に対しては
-
TypeScript で unknown
型の変数を数値に変換したいときは、 まず typeof
で場合分けして string
以外のケースは用途に応じて適宜定義し、 string
型のケースに Number
を使ってパースするのが一番安全そうかなと思いました。
Discussion