-
JavaScript の parseInt で小数を整数に変換しようとして、はまったことがありました。
以下のように、小数第6位までが 0 で、第7位以降に 0 以外の値があると、
変換結果の整数が 0 ではなくなってしまいます。
小さい値を入力した方が、変換結果は逆に大きな値になるため、
一見すると理由が分かりにくい現象でした。parseInt(0.1, 10) → 0 parseInt(0.000001, 10) → 0 parseInt(0.0000001, 10) → 1 parseInt(0.0000009, 10) → 9
- 以下の3つの仕様の組み合わせにより、現象が発生しました。
- parseInt の第1引数は、文字列に変換してから解釈される
- JavaScript の 数値→文字列 の変換は、ある値から指数表記に切り換わる
- parseInt では、指数表記の e を見つけると、それ以後の文字列を無視して変換する
0.1 → "0.1" → 0 0.000001 → "0.000001" → 0 0.0000001 → "1e-7" → 1 0.0000009 → "9e-7" → 9
-
対策としては、整数化の処理を変更することが考えられます。
例えば、以下が挙げられます。
ただし、どれも微妙に仕様が異なるので、状況に応じて使い分けが必要です。- x | 0 で整数にするように変更する
- Math.floor(x) で整数にするように変更する
- Math.trunc(x) で整数にするように変更する(ES6 で追加された機能)
-
x | 0 で整数にする場合の注意点
-
符号付き 32bit 整数に変換されます。
-2147483648 から 2147483647 の範囲を超える数値は、32bit 分だけが取り出されます。-2147483649 → 2147483647 2147483648 → -2147483648 4294967295 → -1 4294967296 → 0
-
小数部は切り捨てられます。
1.1 → 1 -1.1 → -1
-
文字列を渡した場合には、数値に変換してから処理されます。
数値に変換できない文字列は、0 に変換されます(エラーチェックができなくなるので要注意)。
空白文字列 " " や空文字列 "" も、0 に変換されます。
指数表記の文字列は、正しく変換されます。 -
NaN, Infinity, -Infinity, -0 は、0 に変換されます。
-
true は 1 に、false は 0 に変換されます。
-
null は 0 に変換されます。
-
undefined は 0 に変換されます。
-
-
Math.floor(x) で整数にする場合の注意点
-
負の小数は、小さい側の整数に変換されます(parseInt と動作が異なるので要注意)。
1.1 → 1 -1.1 → -2
-
文字列を渡した場合には、数値に変換してから処理されます。
数値に変換できない文字列は、NaN に変換されます。
ただし空白文字列 " " や空文字列 "" は、0 に変換されます。
指数表記の文字列は、正しく変換されます。 -
NaN, Infinity, -Infinity, -0 は、変換されずそのままになります。
-
true は 1 に、false は 0 に変換されます。
-
null は 0 に変換されます。
-
undefined は NaN に変換されます。
-
-
Math.trunc(x) で整数にする場合の注意点
-
ES6 で追加された機能であるため、未対応のブラウザでも動作させるためには、
以下のように互換性の定義を記述する必要があります。if (!Math.trunc) { Math.trunc = function (x) { return (x < 0) ? Math.ceil(x) : Math.floor(x); }; }
-
小数部は切り捨てられます。
1.1 → 1 -1.1 → -1
-
文字列を渡した場合には、数値に変換してから処理されます。
数値に変換できない文字列は、NaN に変換されます。
ただし空白文字列 " " や空文字列 "" は、0 に変換されます。
指数表記の文字列は、正しく変換されます。 -
NaN, Infinity, -Infinity, -0 は、変換されずそのままになります。
-
true は 1 に、false は 0 に変換されます。
-
null は 0 に変換されます。
-
undefined は NaN に変換されます。
-
-
参考 : parseInt(x, 10) で整数にする場合の注意点
-
第1引数は、文字列に変換してから解釈されます。
数値以外の文字を見つけると、それ以後の文字列を無視して変換します。
(このため、指数表記の文字列は、指数部分を無視して変換するので要注意)1e-7 → 1 9e-7 → 9
-
第2引数は、変換の基数を指定します。
(省略すると、環境によっては "010" 等が8進数と解釈されるため要注意) -
小数部は切り捨てられます。
1.1 → 1 -1.1 → -1
-
数値にできない文字列は、NaN に変換されます。
空白文字列 " " や空文字列 "" も、NaN に変換されます。
指数表記の文字列は、指数部分を無視して変換されます(要注意)。 -
NaN はそのまま NaN になります。
-
Infinity, -Infinity は NaN に変換されます。
-
-0 は 0 に変換されます。ただし文字列の "-0" は -0 に変換されます。
-
true, false は NaN に変換されます。
-
null は NaN に変換されます。
-
undefined は NaN に変換されます。
-
- 整数化ではありませんが、parseFloat(x) の方は指数表記を正しく変換できるようです。
また、数値に変換するだけなら (+x) も使えます。
- http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.2
( 15.1.2.2 parseInt (string , radix) ) - http://www.ecma-international.org/ecma-262/5.1/#sec-9.5
( 9.5 ToInt32: (Signed 32 Bit Integer) ) - http://www.ecma-international.org/ecma-262/5.1/#sec-9.8.1
( 9.8.1 ToString Applied to the Number Type ) - http://www.ecma-international.org/ecma-262/5.1/#sec-9.3
( 9.3 ToNumber ) - http://www.ecma-international.org/ecma-262/5.1/#sec-15.8.2.9
( 15.8.2.9 floor (x) ) - http://www.ecma-international.org/ecma-262/6.0/#sec-20.2.2.35
( 20.2.2.35 Math.trunc ( x ) )
(2015-6-10)(2016-7-13)(2016-9-22)(2016-9-26)(2016-9-28)
(2016-11-21)