nmi.jp Twitter → @tkihira
JavaScript で学ぶビット演算の基礎 都会の星の撮り方本を出版しました。WebAssembly を使っています

JavaScript で parseInt / parseFloat を使わない方が良い理由


2022-02-03
Takuo Kihira

先日、Twitter のタイムライン上で JavaScript における parseInt 関数の不可解な挙動に関するネタがバズっていました。

console.log(parseInt(0.000005)); // → 0
console.log(parseInt(0.0000005)); // → 5 !!!!!

この記事では、JavaScript における文字列から数値への変換について簡単に説明します。

parseInt(0.0000005) === 5 になる理由

結論から書くと、

console.log(String(0.000005)); // → "0.000005"
console.log(String(0.0000005)); // → "5e-7"

parseInt int 0.0000005  "5e-7"  5 parseInt(0.0000005) === 5 

 String(0.000005) === "0.000005" String(0.0000005) === "5e-7" 

 Math.trunc 使


parseInt 使Math.trunc 使
const positive = 3.141592;
console.log(Math.trunc(positive)); // → 3
const negative = -2.718281;
console.log(Math.trunc(negative)); // → -2
const large = 12345678901.234;
console.log(Math.trunc(large)); // → 12345678901

Math.floor も使えそうに見えますが、floor は整数化ではなく、その数値を超えない一番大きな整数を返す関数です。マイナスの値における挙動が違うので、整数化したい場合には仮にプラスの値のみであることが保障されていても Math.trunc を使う方が望ましいです。

const positive = 3.141592;
console.log(Math.floor(positive)); // → 3
const negative = -2.718281;
console.log(Math.floor(negative)); // → -3 !!!!!

Math.trunc が使えなかった時代は、同じ挙動のためにビット演算(v >> 0v | 0~~v など)を利用して整数化するのが一般的でした。しかし、これらの方法は可読性が悪い上に 32bit の範囲を超える整数値に関しては予期しない挙動となるので、速度が相当にクリティカルでない限り使わないようにしましょう。

const positive = 3.141592;
console.log(positive | 0); // → 3
const negative = -2.718281;
console.log(negative | 0); // → -2
const large = 12345678901.234;
console.log(large | 0); // → -539222987 !!!!!

parseInt も parseFloat も使うべきではない


 parseInt / parseFloat 使

使

parseInt 


parseInt 1e3 === 1000 
console.log(parseInt("1e3")); // → 1 !!!!!

と意図した表現になりません。次のように Number を使って数値に変換して書くようにしましょう。

console.log(Math.trunc(Number("1e3"))); // → 1000

なお、例外として 16 進数文字列(もしくは 2 進数文字列など)を数値に変換する場合は積極的に使って良いです。0x のプリフィクスを付けると自動的に 16 進数でパースしてくれます。

console.log(parseInt("0x1234")); // → 4660
console.log(parseInt("7fff", 16)); // → 32767
console.log(parseInt("01101101", 2)); // → 109

parseFloat の場合


parseFloat parseFloat  Number 
function convert(str) {
    return {parseFloat: parseFloat(str), Number: Number(str)};
}
console.log(convert("   1234.567    ")); // {parseFloat: 1234.567, Number: 1234.567}
console.log(convert("1e3")); // {parseFloat: 1000, Number: 1000}
console.log(convert("abcde")); // {parseFloat: NaN, Number: NaN}
console.log(convert("n111111")); // {parseFloat: NaN, Number: NaN}
console.log(convert("-Infinity")); // {parseFloat: -Infinity, Number: -Infinity}

次に一致しない表現です。

function convert(str) {
    return {parseFloat: parseFloat(str), Number: Number(str)};
}
console.log(convert("   1234.567x   ")); // {parseFloat: 1234.567, Number: NaN}
console.log(convert("1e3x")); // {parseFloat: 1000, Number: NaN}
console.log(convert("11111n")); // {parseFloat: 11111, Number: NaN}
console.log(convert("123a45")); // {parseFloat: 123, Number: NaN}
console.log(convert("0x1234")); // {parseFloat: 0, Number: 4660}

parseFloat 1682 Number  NaN 1682

parseFloat




 StrDecimalLiteral  NaN 

 StrDecimalLiteral  numberString 

numberString MV


 Number 


 StringNumericLiteral  NaN 

 MV


NumberparseFloatStringNumericLiteral Number StrDecimalLiteral parseFloatparseFloat  StrDecimalLiteral 16(0x)8(0o)2(0b) prefix 

 NaN  Number 使 parseFloat 使petamoriken 
const widthString = "120px"; // document.getElementById('element').style.width のような形で取得
const width = parseFloat(widthString); // 120;

余談: 数値変換のイディオム

なお、JavaScript で文字列を数値に変換するイディオムとして、Number 以外にも +str もしくは str - 0 のような書き方があります。可読性に劣るので使わない方が良いですが、使っているプロジェクトは結構多いので読めるようにはなっておきましょう。内部動作は全て同じです。

const str = "123";
console.log(+str, str - 0, Number(str)); // 123, 123, 123

 new Number  Primitive Object 
const str = "123";
const num = new Number(str); // よろしくない!!!!!!!
console.log(num + num); // 246 一見正しく動いているように見えるが…
console.log(typeof num); // object
console.log(typeof Number(str)); // number 本来はこうあるべき

まとめ


JavaScript  parseInt / parseFloat 使JavaScript 使 parseFloat, parseInt, isNaN, isFinite, eval 


parseFloat(str) 使 Number(str) 

parseInt(num) 使 Math.trunc(num) 

isNaN(num) 使 Number.isNaN(num) 

isFinite(num) 使 Number.isFinite(num) 

eval 使


使

:  String(0.0000005) === "5e-7" 


parseInt  ToString  toString 

 n k s xss k1n x = s * 10^(n - k) ns 0 10


 1234.56789 s 123456789k 9123456789 91234.56789 === 123456789 * 10^(-5) n 4

0.01234 s 1234k 40.01234 === 1234 * (10^-5) n -1 

12345000 s 12345k 512345000 === 12345 * 10^3 n 8


k <= n nn 0  Number::toString 8n -5  0 5
String(0.000005) === '0.000005'

 0 69100.0000005  k === 1 9s '5'0x65 'e' n === -6  '-'  abs(n - 1)  '7' 
String(0.0000005) === '5e-7'

が出力されるのです。