syuichi-tsuji
6/10/2015 - 5:11 AM

JavaScript の parseInt で小数を整数に変換しようとしてはまる

JavaScript の parseInt で小数を整数に変換しようとしてはまる

JavaScript の parseInt で小数を整数に変換しようとしてはまる

現象

  • 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) も使えます。

参考情報

(2015-6-10)(2016-7-13)(2016-9-22)(2016-9-26)(2016-9-28)
(2016-11-21)