Kilimanjaro Warehouse

WEBとかゲーム開発のことについて書きます。

JavaScript: parseInt(0.000001)が0を返し、parseInt(0.000001)が1を返す理由

JavaScriptのparseIntというメソッドで、
0.000001(10の-6乗)を引数にすると、返り値は0になるのに、
0.0000001(10の-7乗)は1が返ってくるという、
意味不明な挙動がTwitterで話題になっていました。

parseInt(0.000001) // 0

parseInt(0.0000001) // 1

この挙動について調べてみました。


TL;DR 要約

  • parseIntは有効でない文字があると、それ以降の文字を無視する
  • 小数点以下7桁以下の数値は、指数表記に変換される

上の仕様によって、この不可解な挙動が説明できます。


解説

parseIntは第一引数に変換するstring型の文字列、第二引数に基数を取ります(省略可)。
string型ではないものが第一引数に渡されると、内部でstring型に変換されます。
そして、数字と認識されない文字があった場合、それ以降の文字は無視されます。

parseInt(0.000001)

小数点が含まれる数字を第一引数に取る場合、小数点は数字として認識されません。
よって、"0.000001"の".000001"は無視され、先頭の0のみが返されます。


そして、0.0000001が1を返す方については、
小数点以下7桁以下の数値は指数表記に変換されることが原因のようです。

parseInt(0.0000001)

0.0000001は内部的にstringに変換される際、"1e-7"のような指数表記になります。
"e-7"は有効な数字ではないので無視され、先頭の1のみが返されます。


このような理由により、
parseInt(0.000001)は0を返し、parseInt(0.000001)は1を返すようでした。


どのような経緯で指数表記の閾値が小数点以下7桁になったのかはわかりませんが、
この閾値ECMAの仕様で決められているようです。
ECMAScript Language Specification - ECMA-262 Edition 5.1


また、ChromeJavaScript実行エンジンであるV8のテストケースにも、
上記の挙動が正しい動作としてテストが通るように書かれています。

PASS parseInt(Math.pow(10, -6)) is 0
PASS parseInt(Math.pow(10, -7)) is 1

v8/parseInt-expected.txt at a28c760ef01d2b9749c26d96aa5278a736ad4591 · v8/v8 · GitHub


結論

一見意味不明な挙動に見えるが、
しっかり調べると動きの理由があるようでした!