JavaScriptの数値型完全理解

数値というのはプログラミングにおいて極めて基本的な対象です。ほとんどのプログラミング言語は何らかの形で数値の操作を行うことができ、もちろんJavaScriptにおいても例外ではありません。

プログラミングにおける数値の特徴的な点は、往々にしてその性質に応じた複数の**型**[^type]が与えられている点です。まず、数値は**整数**か**小数**かによって分類されます。さらに、値を表すのに使われる**ビット数**、また整数に関しては**符号あり**か**符号なし**かという分類ができます。例えば、**Rust**という言語ではこれらの分類が分かりやすく表れています[^usize]。Rustにおける数値の型は`i32`, `i64`, `u32`, `u64`, `f32`, `f64`などがあり、見ただけでどのような特徴を持つ数値なのかが分かりやすくなっています。`i`というのは符号あり整数、`u`というのは符号なし整数、`f`は小数で、その後の数字がビット数ですね。

[^type]: これは静的型にも動的型にも言えることですね。
[^usize]: `usize`とか`isize`がちょっと厄介なのですが、ここではあまり関係がないので触れるのを避けることにします。

では、**JavaScript**においては数値はどのように扱われているのでしょうか。この記事では、JavaScriptの数値はどのように表されるのか、計算はどのように行われるのかなど、JavaScriptの数値に関するトピックを網羅します。

## JavaScriptの数値は（今のところ）1種類

実は、**JavaScriptの数値は型が1種類しかありません**。熱心な方は`BigInt`の存在についてご存知かと思いますが、まだこれはぎりぎりJavaScriptに入っていないのでここでは1種類とカウントします。（BigIntについてはこの記事の後半で触れます。）

1種類しかないということは、上で紹介した「整数か小数か」「ビット数」などによる区別を持たないということです。結論から言ってしまえば、JavaScriptの数値は**64ビットの浮動小数点数**です。つまり、JavaScriptの数値は全てが小数なのです。

以下のようなプログラムではJavaScriptで整数を扱っているように見えますが、実はこれも`1.0`や`2.0`などのデータを扱っていることになります（整数と小数を区別する言語では`1.0`のように小数点を明示することでそれが小数データであることを明示することがあり、この記事でもそれに倣っています）。

```js
const x = 1;
const y = 2;
console.log(x + y); // 3
```

`3`などと表示されるのも処理系が気を利かせているのであり、内部的には`3.0`というデータになっています[^js_internal]。

[^js_internal]: 本当の処理系の内部では最適化して整数として扱われている可能性もありますが、仕様上は全て浮動小数点数として扱われています。

### IEEE 754 倍精度浮動小数点数 (double)

より詳細に言えば、JavaScriptの数値型は**IEEE 754 倍精度浮動小数点数**です（長いので以降は**double**と呼ぶことにします）。これは何かというと、**64ビットの範囲内でどのように小数を表現するか**を定めた規格のひとつです。doubleによる小数（浮動小数点数）の表現は極めて広く使用されており、コンピュータにおける小数の表現のスタンダードとなっています。

まず前提として、**64ビットという限られたデータ量で任意の小数を表すのは不可能です**。それゆえ、プログラムで表せる数値の精度には限りがあります。このことは、以下のよく知られた例に表れています。

```js
// 0.30000000000000004 と表示される
console.log(0.1 + 0.2);
// false と表示される
console.log(0.3 === 0.1 + 0.2);
```

これは、`0.1`とか`0.2`という数が（2進数で数値を扱うコンピュータにとっては）きりの悪い数なので正確に表すことができないことが原因です。なので、`0.1`と書いた時点で、コンピュータはそれをぴったり`0.1`という数ではなく`0.1000000000000000055511151231257827021181583404541015625`という数として認識します。64ビットという限られたデータ量ではこれが精一杯で、これ以上正確に`0.1`を表すことはできないのです（doubleという規格を用いるならばの話ですが）。`0.2`についても同様で、コンピュータはこれを`0.200000000000000011102230246251565404236316680908203125`と認識します。この時点で、`0.1`も`0.2`もともに、実際よりもわずかに大きいほうに誤差が発生しています。

`0.1 + 0.2`という計算の結果は`0.3000000000000000444089209850062616169452667236328125`です[^decimal]。ポイントは、`0.1`や`0.2`と書いた時点で発生していた誤差、そして加算で発生した丸め誤差がこの計算結果に蓄積しているということです。

[^decimal]: 筆算してみたけど結果が全然違うじゃないかと思われる方がいるかもしれませんが、`0.1`と`0.2`では指数部（後述）が異なるのでそれによる丸め誤差が発生しているからです。なので、本当はこうやって割り切れるまで10進展開した値を書くことにはそこまで意味がないのですが、ここでは何となく誤差があるんだよということを認識してもらうのが目的なので大目に見てください。

一方で、プログラムに`0.3`と書いた場合もやはりすでに誤差が発生しており、コンピュータは実際の`0.3`に最も近いdoubleで表現可能な数である`0.299999999999999988897769753748434595763683319091796875`を採用します。

ここで運悪く、`0.3`をdoubleで表現しようとすると負の方向に誤差が発生しています。`0.1`と`0.2`はともに正の誤差を持っていたこともあり、これらが蓄積した結果`0.1 + 0.2`は`0.3`から離れすぎてしまいます。その結果、`0.1 + 0.2`の結果は`0.3`（に最も近いdoubleで表現可能な数）からずれてしまうのです。このずれはビット表現でいうとわずか1ビットです。実際、`0.3`にちょうど1ビットぶんの値である$2^{-54}$を足すと`0.1 + 0.2`になります。

```js
// true と表示される
console.log(0.3 + 2 ** (-54) === 0.1 + 0.2);
```

結局ここで何が言いたいかというと、このような挙動はJavaScriptとは関係のない**浮動小数点数の仕様**であり、コンピュータで（固定長のデータで）小数を表そうとする限り避けようのないものであるということです。

ゆえに、JavaScriptの数値型を理解するには、**IEEE 754由来の挙動はどれか**ということも知らなければなりません。というわけで、もう少しだけIEEE 754（double）について見てみましょう。

---

doubleによる小数の表現では、64ビットのうち最初の1ビットを**符号ビット**（0なら正の数、1なら負の数を表す）、次の11ビットを**指数部**、残りの52ビットを**仮数部**として用います。本質的には、指数部と仮数部はそれぞれ2進数で表された整数であると解釈されます。仮数部が$x$という整数で指数部が$y$という整数であるとき、その小数表現が表すのは$x \times 2^y$となります（実際には$x$はいわゆるけち表現を加味して決められることや、$y$は正負をバランスよく表せるように調整されることに注意が必要です）。ここから分かることは、指数部のビット数というのは小数で表せるスケール（どれくらい大きな/小さな範囲の数を表せるか）、そして仮数部のビット数というのは小数で表せる精度（小数を表すのにどれくらいの桁数を使えるか）に関わるということです。

特に、doubleにおける仮数部の精度は**53ビット**であることは覚えておくに値するでしょう（52ビットからなぜ1つ増えているのかは調べてみてください）。

そろそろ話をJavaScriptに戻しますが、JavaScriptの数値がdoubleによって表されているという話と上記のdoubleの仕様を組み合わせて言えることは、**JavaScriptの数値の精度は53ビット分**であるということです。

### JavaScriptの数値の精度は53ビット

JavaScriptは整数と小数という区別が無いということは冒頭でお話ししましたね。これは、繰り返しになりますが、**整数だろうと小数だろうと全部doubleで表している**ということです。

ということは**JavaScriptの整数の精度も53ビット**ということです。整数と小数を区別するプログラミング言語では整数の精度は32ビットだったり64ビットだったりしますから、それに比べると随分中途半端に思えます。ただ、それらの言語と比べると、JavaScriptでの整数はdoubleであることに由来する面白い挙動をします。それは**だんだん下の桁から大ざっぱになっていく**という挙動です。まずこの辺りを見ていきましょう。

整数の精度が53ビットということは、$0$から$2^{53}-1$までの整数は正確に表せるということを意味します。ただし、ここで正確に表せるというのは**1つ違うとなりの整数と区別できる**ということを意味します。次の例は、$2^{53}-1$が両隣の数と区別できることを確かめています。

```js
// false と表示される（2^53 - 2 と 2^53 - 1は違うものと認識されている）
console.log(2 ** 53 - 2 === 2 ** 53 - 1);
// false と表示される（2^53 - 1 と 2^53は違うものと認識されている）
console.log(2 ** 53 - 1 === 2 ** 53);
```

この範囲を超えると、となりの整数と区別することができなくなります。例えば、$2^{53}$と$2^{53}+1$は区別することができません。両者をdoubleで表すと同一のビット列となるためです。

```js
// true と表示される（2^53 と 2^53 + 1は同じと認識されている）
console.log(2 ** 53 === 2 ** 53 + 1);
```

$2^{53}$を2進数で表すと`100000000000000000000000000000000000000000000000000000`（`1`の後に`0`が53個）である一方、$2^{53}+1$は`100000000000000000000000000000000000000000000000000001`です。どちらも整数としての表現に54ビット必要としていることが分かります。doubleは数の精度を53ビットしか確保できませんから、53ビットになるように丸められます（このときの丸めモードは最近接（偶数）丸めです）。これにより、両者はどちらも$2^{53}$に丸められて等しいと扱われるのです。

一方、$2^{53}+2$という数を考えてみます。これは`100000000000000000000000000000000000000000000000000010`ですから、53ビットに情報が減らされた後も依然として$2^{53}$とは異なります。

```js
console.log(2 ** 53 === 2 ** 53 + 2); // false
```

この例では、整数として表すのに54ビット必要になったことで整数の精度が1ビット分大ざっぱになりました。さらに数を大きくしていくことで、下のほうからどんどんビットが削られて大ざっぱになっていくわけです。

このような事情から、JavaScriptには**safe integer**（安全な整数？）という概念があります。これは絶対値が$2^{53}-1$以下の整数を指し、計算結果がsafe integerならばずれが発生していない（上述の現象により正しい答えを表す整数からずれていない）ことが分かります[^safe_integer_interim]。

[^safe_integer_interim]: 複雑な計算の場合は途中の計算もsafe integerになっていないといけませんが。

JavaScriptには与えられた数がsafe integerかどうか判定する`Number.isSafeInteger`関数が用意されています。

```js
console.log(Number.isSafeInteger(2 ** 53 - 1)); // true
console.log(Number.isSafeInteger(2 ** 53));     // false
```

こう真面目に解説するとJavaScriptはアホな言語なのではないかと思われるかもしれませんが（そして整数もdoubleで表さないといけないのが実際アホなことは否定しませんが）、整数を浮動小数点数で表そうとしたときにこのように情報が落ちていくことはdoubleの仕様から来る話でJavaScriptに特有の話ではないということは理解しておくべきでしょう。

### `NaN`と`Infinity`, `+0`と`-0`

JavaScriptには`NaN`と`Infinity`という特別な数値が存在します。`NaN`はNot a Number（数ではない）の略であることは知られていますが、JavaScriptでは数値の一種なので型を調べると数値型です。

```js
console.log(typeof NaN); // "number"
```

言うまでもなく、これも**IEEE 754由来の概念**です。ですから、「JavaScriptにはNaNとかいう意味不明な値がある」などとは思わないでくださいね。

ただ、doubleはNaNを表すビットパターンを大量に（$2^{53}-2$個くらい）持ちますが、JavaScriptにおいては`NaN`はただ一種類であり区別はありません。

`NaN`は数値が必要だけどどうしても無理な場合に表れます。例えば、`parseInt`（後述）で文字列を数値に変換しようとしたけど無理だった場合は`NaN`が結果となります。

```js
console.log(parseInt("foobar!")); // NaN
```

また、`0 / 0`という計算をした場合も`NaN`となります。

`NaN`の面白い（もちろんIEEE 754由来の）点は、`NaN === NaN`という比較が`false`となることです。`NaN < NaN`など、`NaN`を含む比較演算はすべて`false`となります。ある数値が`NaN`かどうか判定した場合は`isNaN`（後述）を使うのがよいでしょう。

`Infinity`も同様です。これは「無限大」を表す特別な数値であり、IEEE 754由来です。無限大には正の無限大と負の無限大があります。

さらに、doubleは`+0`と`-0`という2種類の0が存在し、JavaScriptにおいても2種類の0が確認できます（普通の0は`+0`です）。`+0`と`-0`は`===`で比較しても等しいのが特徴です。

このように、JavaScriptにおいて広く使われる等値比較演算子である`===`もIEEE 754の影響を受けています。ES2015以降では、IEEE 754の影響を排除した等値比較の手段として`Object.is`という関数が用意されています（cf. [JavaScriptの等値比較を全部理解する](https://qiita.com/uhyo/items/d99f536332e03442c432)）。

```js
console.log(0 === -0); // true
console.log(Object.is(0, -0)); // false
```

以上でJavaScriptの数値型が裏ではどのようになっているか分かりました。これを踏まえてJavaScriptにおける数値に関連する演算を見ていきたいのですが、その前に**数値リテラル**の話を挟みます。

## JavaScriptの数値リテラル

数値リテラルとは、プログラム中で数値を表現する方法です。プログラム中に`123`とか`0.45`と書いたら当然`123`とか`0.45`という数値になりますが、これらが数値リテラルです。

これはJavaScriptに限った話ではありませんが、`0.123`のように`0.`で始まる小数を書きたい場合は最初の`0`を省略して`.123`のように書けます。見かけても驚かないようにしましょう。

```js
console.log(.123); // 0.123
```

また、整数を書きたい場合は`123.0`の最後の`0`を省略して`123.`とできます。JavaScriptではただ`123`とすればいいので特に意味はありませんが、整数と浮動小数点数を区別する言語では`123`と`123.0`が別の意味になるために後者を省略したい場合の記法として需要があるようです。

なお、数値のメソッドを呼びたい場合には注意してください。`123`の`toFixed`メソッド（後述）を呼びたい場合には、`123.toFixed()`とするとエラーとなります。なぜなら、`123.`という数値の直後に`toFixed`が並んでいる（`123 toFixed()`と書いたのと同じ）と解釈されるからです。これを回避するひとつの方法は`(123).toFixed()`ですが、文字数の少なさと見た目の面白さからこれを`123..toFixed()`とするのをよく見ます。こうすることで、`123.`が数値、`.toFixed()`でメソッド呼び出しと解釈されます。もちろん、`123.0.toFixed()`なども動作します。

さらに、数値リテラルは指数表記をサポートしています。これは`1.23e5`のように整数または小数のうしろに`e`と整数を付けるリテラルです。`e`の後ろは10の指数と解釈されます。よって、`1.23e5`は$1.23 \times 10^5$、すなわち`123000`と解釈されます。`1e-4`（`0.0001`と同じ）のように`e`の後ろは負の整数も可能です。

また、ここまでは10進数表記でしたが、他に16進数・8進数・2進数のリテラルがサポートされています。それぞれ`0xabcdef`、`0o755`、`0b1010111`のようなリテラルです。これらの10進数以外のリテラルは小数点や`e`による指数表記はサポートされていません。

また、桁数の多い数値リテラルを見やすく`1_234_567`のように書ける提案がもう少しで完成しそうです（cf. [JavaScriptで数値の区切り文字を使いたい物語](https://qiita.com/uhyo/items/4586dbc347d6985b44ef)）。

以上が数値リテラルの話でした。まあ、特におかしな所はありませんでしたね。では、いよいよ数値演算の話に入っていきます。

## JavaScriptにおける数値演算

とはいえ、JavaScriptの数値演算は、それほど特筆すべき点があるわけではありません。まず、普通の四則演算（`+`, `-`, `*`, `/`及び余り`%`）と累乗（`**`）が備えられています。

ただし、`+`は文字列の連結にも使われるので`"5" + 1`が`"51"`になったりする点はやや注意が必要でしょうか。`-`とかは両辺を数値に変換するので`"5" - 1`は`4`です。これらの演算子にオブジェクトを渡してしまったときの挙動はちょっと面白かったりするのですが（cf. [JavaScriptのプリミティブへの変換を完全に理解する](https://qiita.com/uhyo/items/44c2f79873de13186743)）、いまは関係のない話です。

やや注意が必要なのは**ビット演算**です。JavaScriptは一般的なビットごと演算（`&`, `|`, `~`）やビットシフト（`<<`, `>>`, `>>>`）を備えていますが、これらを使う場合は突然**数値が32ビット整数に変換されます**。このとき小数は0に近いほうへ丸められます。この結果として、32ビットを超える範囲の整数は下位32ビットのみが残されて他は捨てられます。また、負数は普通の2の補数表現によって表されます。

そして、32ビット整数に対してビット演算が適用され、結果が32ビット符号あり整数として再解釈されます（ただし、`>>>`のみ32ビット符号なし整数として解釈します）。

以上の説明で以下の結果が理解できることでしょう。

```js
console.log((2 ** 32) | 0);   // 0
console.log(2 ** 31);         // 2147483648
console.log((2 ** 31) | 0);   // -2147483648
console.log((2 ** 32) >> 0);  // 0
console.log((2 ** 31) >> 0);  // -2147483648
console.log((2 ** 31) >>> 0); // 2147483648
```

また、`<<`などのビットシフト演算の右オペランド（シフト量を指定する数）についても右側は32ビット整数として扱われます。また、対象が32ビット整数ということで32個以上シフトするのは意味がないと考えられることから、32以上の数については32で割った余りが取られます。よって、次のような結果となります。

```js
console.log(1 << 33);   // 2
console.log(1 << (-1)); // -2147483648
```

## 数値から文字列への変換

数値の計算に関する話は終わりにして、数値を文字列に変換したいときの話をしましょう。実は、JavaScriptは数値から文字列への変換メソッドを5種類提供しています。いずれも数値が持つメソッドとして利用可能です。

### `toExponential`

このメソッドは数値を指数表現で文字列に変換します。指数表現というのは`1.23e+5`（$1.23 \times 10^5$を表す）のように`e`を用いた表現です。`toExponential`はどのような数値でもこの表現に変換します（`NaN`とか`Infinity`は除く）。引数で、小数点以下の桁数を指定できます。省略すると数値を表現するのに最低限必要な分を適切に選んでくれます。

```js
console.log(150..toExponential(3)); // "1.500e+2"
console.log(0.1234.toExponential(1)); // "1.2e-1"
```

### `toFixed`

`toFixed`は、逆に指数表現を用いない文字列表現を得たいときに使います。引数は小数点以下の桁数で、省略の場合は`0`（整数部分のみ）扱いです。また、絶対値が$10^{21}$以上の数は諦めてしまいます（後述の`toString`と同じになります）。

```js
console.log(100..toFixed(3));     // "100.000"
console.log(0.000999.toFixed(5)); // "0.00100"
```

特筆すべき点として、safe integerの範囲を超える（しかし$10^{20}$を超えない）範囲の整数に対しては`toString`（後述）よりも`toFixed`のほうがより正確な[^accuracy]10進表現を求めてくれます。小数についても同様の場合があります。

[^accuracy]: 正確なというのは、53ビットで表せない範囲の部分をちゃんと最後まで10進展開してくれるという意味です（詳しくは`toString`のところで説明します）。

```js
console.log((2 ** 60).toString());   // "1152921504606847000"
console.log((2 ** 60).toFixed());    // "1152921504606846976"
```

### `toPrecision`

`toPrecision`は、引数で渡した数を有効数字の桁数とする文字列表現に直してくれます。必要に応じて指数表現が使われます。

```js
console.log(1.234.toPrecision(3));        // "1.23"
console.log(1234..toPrecision(3));        // "1.23e+3"
console.log(0.00123.toPrecision(2));      // "0.0012"
console.log(0.0000000123.toPrecision(2)); // "1.2e-8"
```

### `toString`

`toString`は数値から文字列への暗黙の変換の場合に使われる標準的な方法です。結果は数値を普通に（必要最小限の桁数で）表現したものですが、`toPrecision`の場合と同様に必要に応じて指数表現も使われます。

`toString`には大きな特徴が2つあります。1つ目は、10進表示への変換を**適度にサボる**ことです。`toFixed`の例を再掲します。`2 ** 60`（$2^{60}$）に対する結果が`toString`と`toFixed`で違っているという例でしたね。

```js
console.log((2 ** 60).toString());   // "1152921504606847000"
console.log((2 ** 60).toFixed());    // "1152921504606846976"
```

$2^{60}$は正確に`1152921504606846976`ですが、`(2 ** 60).toString()`は最後の4桁が`7000`と大ざっぱになっています。

しかし、JavaScriptの整数の精度が53ビットしかないことを考えれば、実は`1152921504606847000`も`2 ** 60`になることが分かります。

```js
console.log(2 ** 60 === 1152921504606846976); // true
console.log(2 ** 60 === 1152921504606847000); // true
```

$2^{60}$という61ビットの数が53ビットに情報を減らされる場合、8ビット分情報が落ちます。こうなると、丸めのことを考えてもその半分（7ビット）程度の違いは意味を成さなくなります。$2^7$は128ですから、`6976`と`7000`の間のたった24の差は無視できるのです。

このように、`toString`は無視できる範囲で情報を落としてもよいことになっています。ちなみに、小数の場合でもこれは同じです。次の例を見れば分かるように、これはわりと人間に優しい仕様であるといえますね。

```js
console.log(0.3.toString());  // "0.3"
console.log(0.3.toFixed(20)); // "0.29999999999999998890"
```

この場合、わざわざ`0.2999999999999999889`と書いても`0.3`と書いても結果は同じであるため、より少ない桁数で表されるほうが選ばれています。

```js
console.log(0.2999999999999999889 === 0.3); // true
```

`toString`にはもうひとつ特徴があります。それは、**引数で基数を指定できる**点です。これにより、10進数以外の表現を得ることができます。

```js
console.log(12345.67.toString(16)); // "3039.ab851eb852" (Google Chrome 74の場合）
```

ちなみに、10進数以外は文字列を生成する具体的なアルゴリズムは実装依存です。

### `toLocaleString`

これは`Intl` APIの一部で、言語や地域等の慣習に合わせて数値を文字列に変換してくれるメソッドです。詳しく説明するのは別の機会に譲るとして、ひとつだけ例を出しておきます。

```js
console.log(1234567..toLocaleString("ja-JP", { style: "currency", currency: "JPY"}); // "￥1,234,567"
```

## 文字列から数値への変換

逆に、文字列をいい感じに解釈して数値に変換するという機能にも需要がありますよね。JavaScriptにはそのための方法がいくつかあります。

### `parseInt`

その一つは`parseInt`です。Intというのはinteger（整数）のことですから、これは整数を表す文字列を数値に変換してくれます。

`parseInt`は大きな特徴が2つあり、1つは数値の後ろの余計な文字列を無視してくれることです。数値として解釈できない文字列は`NaN`となります。

```js
console.log(parseInt("55000"));   // 55000
console.log(parseInt("123px"));   // pxが無視されて結果は 123
console.log(parseInt("123.45"));  // 小数はパースしないので.45が無視されて 123
console.log(parseInt("foobar"));  // NaN
```

もうひとつは、第2引数で**基数**を指定できる点です。2から36まで可能です。

```js
console.log(parseInt("ff", 16));        // abcdef を16進数で解釈すると 255
console.log(parseInt("100zzzzzz", 16)); // 256
```

ちなみに、16進数の場合は`parseInt`は特殊な挙動をします。それは、`0x`で始まる文字列をいい感じに解釈してくれるというものです。とても<del>いい迷惑</del>ありがたい機能ですね。

```js
console.log(parseInt("0xff", 16)); // 255
```

では、`parseInt`の第2引数を省略した場合はどうなるのでしょうか。この場合は先の例からも分かるように10進数として扱われますが、省略した場合はひとつ特殊な挙動があります。それは`0x`で始まる文字列を渡された場合で、この場合だけ気を利かせて16進数として解釈してくれるのです。

```js
console.log(parseInt("0xff"));     // 255
console.log(parseInt("0xff", 10)); // 0 （第2引数を指定するとこの挙動はオフになるため）
```

最後に、`parseInt`は文字列前後の空白文字を無視してくれます。

```js
console.log(parseInt("      123   ")); // 123
console.log(parseInt("   1  2  3  ")); // 1 （1の後の空白以降は全部余計な文字と見なされるので）
```

ちなみに、グローバル変数の`parseInt`ではなく`Number.parseInt`もあります（ES2015で追加）。こちらも同じ挙動となります。

### `parseFloat`

`parseFloat`は、整数だけでなく小数も解釈してくれる関数です。まず、文字列前後の空白文字を無視してくれたり、後ろに余計なものが付いていても無視してくれるという点は`parseInt`と同じです。

`parseFloat`については、10進の数値リテラルを文字列として表したものを解釈できます。

```js
console.log(parseFloat("    123.45px")); // 123.45
console.log(parseFloat("1.23e4"));       // 12300
console.log(parseFloat("  .456"));       // 0.456
```

16進など、他の基数のリテラルは無理です。

```js
console.log(parseFloat("0x123")); // 0
```

加えて、`parseFloat`は`Infinity`に対する特別なサポートがあります。

```js
console.log(parseFloat("Infinity")); // Infinity
```

`Infinity`と書くと`Infinity`という数が得られますがこれは`Infinity`というグローバル変数が存在するからこそなので、`parseFloat`が`"Infinity"`に対応しているのはなかなか面白いですね。

実に簡単ですね。なお、`Number.parseFloat`をグローバルの`parseFloat`と同様に使えるのも`parseInt`と同じです。

### `Number`

`Number`は渡したものをなんでも数値に変換してくれる関数です。また、`+"123"`のように数値への暗黙の変換が要求される場合もこの変換が使われます。

`Number`は、parse系メソッドと同様に前後に空白文字があっても許容します。ただし、それ以外の余計な文字が後ろにくっついているのは`NaN`となります。

```js
console.log(Number("1234"));     // 1234
console.log(Number(" 1234  "));  // 1234
console.log(Number("1234px"));   // NaN
```

この点を除いて、`Number`は全ての数値リテラルを受け付けます[^all_literals]。また、`parseFloat`と同様に`Infinity`も受け付けます。

[^all_literals]: ただし、前述のnumeric separators（`1_234_567`みたいなやつ）が導入された場合は`Number`はこれを解釈してくれないという仕様になる予定のようです。

すなわち、`parseFloat`が受け付けてくれた全てのリテラルに加えて`"0xff"`や`0b10101`なども受け付けられるということです。`parseInt`と挙動が揃っていないのはご愛嬌です。

```js
console.log(Number("0xff"));  // 255
console.log(Number("0b101")); // 5
```

## その他の数値関係メソッド

他にもいくつか数値関係のメソッドがあるので紹介します。

### `isNaN`・`isFinite`

`isNaN`は引数で与えられた数が`NaN`かどうか判定するメソッドです。

```js
console.log(isNaN(123));      // false
console.log(isNaN(Infinity)); // false
console.log(isNaN(NaN));      // true
```

`NaN`は`NaN === NaN`が`false`になってしまうため、`isNaN`が`NaN`かどうか判定する簡単な方法です。

また、`isFinite`は与えられた数が`NaN`か`Infinity`または`-Infifity`だったら`false`で他は`true`を返すメソッドです。これは意外と使いどころがある関数です。

```js
console.log(isFinite(123.45));   // true
console.log(isFinite(NaN));      // false
console.log(isFinite(Infinity)); // false
```

`parseInt`などと同様にこれらにも`Number`の下にあるバージョン、すなわち`Number.isNaN`と`Number.isFinite`がありますが、何とこれらは`isNaN`や`isFinite`とは**微妙に挙動が違います**。

`Number`バージョン、すなわち`Number.isNaN`や`Number.isFinite`は、与えられたものが数値でない場合は即座に`false`を返します。一方、グローバルの`isNaN`や`isFinite`はまず与えられたものを（`Number`で）数値に変換しようとします。その結果、以下のような違いが現れることになります。

```js
console.log(isNaN("foobar"));        // true （"foobar"を数値に変換したらNaNなので）
console.log(Number.isNaN("foobar")); // false （"foobar"は数値ではないので）
console.log(isFinite("1234"));       // true
console.log(Number.isFinite("1234"));// false
```

### `Number.isInteger`, `Number.isSafeInteger`

`Number.isInteger`は単純ですね。与えられた数値が整数かどうかを判定します。`Number.isSafeInteger`は少し前に話題にのぼりました。絶対値が$2^{53}-1$以下の整数に対して`true`を返します。これらは`Number.isFinite`などと同様に、数値以外には即`false`を返します。

```js
console.log(Number.isInteger(1234));     // true
console.log(Number.isInteger(123.4));    // false
console.log(Number.isInteger(Infinity)); // false
```

### `Number`の定数

数値に関連するメソッドは以上ですが、`Number`にはいくつか付随する定数があります。

`Number.MAX_SAFE_INTEGER`は$2^{53}-1$です。つまり最大のsafe integerですね。同様に、`Number.MIN_SAFE_INTEGER`は$-(2^{53}-1)$です。

`Number.MAX_VALUE`はJavaScriptの数値型で（すなわちdoubleで）表現可能な最大の数です（`Infinity`は除く）。

```js
console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
```

これは約$1.7976931348623157 \times 10^{308}$のようですね。

同様に、`Number.MIN_VALUE`はdoubleで表現可能な最小の正の数です。

```js
console.log(Number.MIN_VALUE); // 5e-324
```

`Number.MAX_VALUE`に比べると有効数字が少ないような気がしますが、それはdoubleの0に近い部分では非正規化数（精度を落とすことで通常の小数（正規化数）よりも絶対値が0に近い数を表現する仕組み）が現れるからです。実際、この`5e-324`という数は精度を1ビットまで落とすことでぎりぎりまで0に近づけた数です。0より大きく`5e-324`より小さい数はJavaScript（あるいはdouble）には存在しません。例えば、`5e-324 / 2`は`0`となります。

他には`Number.POSITIVE_INFINITY`（`Infinity`が入っている）と`Number.NEGATIVE_INFINITY`（`-Infinity`が入っている）、そして`Number.NaN`があります（`NaN`が入っている）があります。グローバル変数の`NaN`や`Infinity`が信用ならないときに使いましょう。

最後に、`Number.EPSILON`という定数もあります。これは、「1」と「（doubleで表せる）1よりも大きい最小の数」の差です。doubleの仕組みを理解していれば、これが$2^{-52}$であることはすぐに分かるでしょう。

```js
console.log(Number.EPSILON === 2 ** (-52)); // true
```

以上で、数値に関する話はだいたい語りつくしました。他には`Math`オブジェクト以下で提供されるさまざまな数学関数もあります（ES2015でいろいろ追加されたのでまだ調べていない人は調べてみてください）。ここでは全ては紹介しませんが、少しだけ触れておきます。

面白いところでは、`Math.clz32`という関数があります。これは、与えられた数を32ビット（符号なし）整数に変換してleading zeroesを数える（2進表現における一番左の`1`よりも左にある`0`の数を数える）関数です。

```js
console.log(Math.clz32(1)); // 31
console.log(Math.clz32(2 ** 31)); // 0
console.log(Math.clz32(-(2 ** 31))); // 0
```

また、`Math.imul`は2つの引数を符号なし32ビット整数に変換したあと乗算を行い、結果の下部32ビット（を符号あり32ビット整数として解釈したもの）を返します。

```js
console.log(Math.imul(2 ** 15, 2 ** 16)); // -2147483648
```

他は普通の数学関数なので調べてみてください。

## `BigInt`の話

さて、以上で数値の話は終わりました。しかし、この記事はこれだけでは終わりません。JavaScriptには`BigInt`があります。これはまだJavaScriptに正式採用されていないものの現在Stage 3のプロポーザルです。これは要するにJavaScriptにもうそろそろ正式に追加されそうということで、恐らく2020年にリリースされるES2020で正式にJavaScriptの仕様に追加されると思います。

`BigInt`はこれまで説明してきた普通の数値（number型）とは別の型であり、**任意精度の整数**を表す型です。ざっくり言えば、ビット数という制限に囚われずに好きなだけ大きい整数を表すことができるという、従来の数値とはまったく異なる特徴を持つ値です。ただし、小数は範疇外です。あくまで整数のみが対象となります。

BigInt型の値を作るには主に2つの方法があります。一つは**BigIntリテラル**を使う方法です。これは`123n`のように整数の後に`n`を付けるリテラルを用います。

もう一つは`BigInt()`関数を使う方法です。これは`Number()`のように、与えられた値をBigInt型の値に変換してくれます。整数を表す数値や文字列を`BigInt()`に与えることでBigInt型の値を作ることができるのです。

BigInt型の値は`typeof`演算子で調べると`"bigint"`という結果になります。

```js
console.log(123n);                   // 123n
console.log(typeof 123n);            // "bigint"
console.log(BigInt(123) === 123n);   // true
console.log(BigInt("123") === 123n); // true
```

`BigInt`型の値は、普通の数値と同様に四則演算が可能です。ただし、これは`BigInt`同士の計算に制限されています。`BigInt`と普通の数値を混ぜるとエラーになります。また、`BigInt`は小数を表しませんので、割り算が割り切れない場合は切り捨てられます。

```js
console.log(2n + 3n); // 5n
console.log(2n * 3n); // 6n
console.log(5n / 2n); // 2n
```

`BigInt`が普通の数値とは異なり精度に制限がないことを確かめてみましょう。

```js
console.log(2 ** 60 + 10 === 2 ** 60);      // true （普通の数値は精度が53ビットしかないので）
console.log(2n ** 60n + 10n === 2n ** 60n); // false （BigIntはどんな大きさの整数も正確に表現可能）
```

このような挙動はたいへんうれしいですね。とりわけ、これまでJavaScriptで扱うのが難しかった64ビット整数が`BigInt`を使えば自然に表せるのがとても偉いです。`BigInt`の導入以降は、53ビット以上に大きな整数が必要な場面で`BigInt`が活用されていくことになります（cf. [JavaScriptの日時処理はこう変わる！　Temporal入門](https://qiita.com/uhyo/items/5f34f5d6f33aa091a104)）。

また、`BigInt`は任意精度のビット演算ができるのも嬉しい点です。従来の数値型では32ビットに制限されていたビット演算が、BigIntでは任意の精度で可能です。

```js
console.log((2n ** 60n | 2n ** 55n === 2n ** 60n + 2n ** 55n); // true
```

### `BigInt`関連のメソッド

以上が`BigInt`の基本機能です。また、以下の2つのメソッドが`BigInt`に関連して提供される予定です。

#### `BigInt.asUintN`, `BigInt.asIntN`

`BigInt`の値を指定したビット数に制限して得られる新しい`BigInt`値を返します。例えば`BigInt.asUintN(64, n)`は`n`の下位`64`ビットの値を表す`BigInt`です。`asUintN`と`asIntN`の違いは、得られた下位`n`ビット分の値を符号なし整数として解釈するか符号あり整数として解釈するかの違いです。

```js
console.log(BigInt.asUintN(64, 7n * (2n ** 62n)) === 2n ** 63n + 2n ** 62n); // true
```

### `BigInt`の上限

ちなみに、`BigInt`はどれくらい大きな整数を表せるのでしょうか。実は、仕様では`BigInt`の上限は定められていません。理想的な処理系では、どんなに大きな`BigInt`でも表せることになります。

ところが、現実にはそうもいきません。最悪のケースでも、コンピュータのメモリを全部食いつぶしてしまえばそれ以上の大きさの`BigInt`は作れないわけです。

もちろん、現実の処理系はそういう事態になるより前に何らかのエラーを発生させるでしょう。

これについては、[BigIntの上限を調べてみた方がいるようです](https://qiita.com/po_tau_feu/items/225f783784f68159c37d)。それによれば、Google ChromeではBigIntの上限$M$は$2^{2^{30}-1} \leq M \leq 2^{2^{30}}$を満たすようです。恐らくビット数で制限されているであろうことを考えると、Chromeでは`BigInt`の最大精度は$2^{30}$ビットと考えてよさそうです。$2^{30}$ビットというのは128MiBですから、Chromeはひとつの`BigInt`に対して128MiBまでの使用を許してくれるようです（複数の`BigInt`を同時に作る実験は行われていないので、複数の`BigInt`の合計が128MiBとかそういう話かもしれません。未検証です）。

もっとも、`BigInt`の演算というのは定数時間ではないので、実際の128MiBのメモリを使って表された`BigInt`に対して計算を行うのは非常に時間がかかることでしょう（前述の記事でも実際にそのような結果が報告されています）。

## まとめ

この記事ではJavaScriptの数値型について概観しました。

ポイントは、（`BigInt`ではない従来の）数値型は整数と小数を区別せず、**IEEE 754倍精度浮動小数点数**で表されているという点です。その結果、整数が53ビットという一見中途半端な精度を持っていたり、浮動小数点数に特有の（`0.1 + 0.2 !== 0.3`のような）挙動が表れます。特に、どの挙動がJavaScriptに特有の話でどの挙動がIEEE 754由来の話なのかはよく考えておくべきでしょう。万一にもIEEE 754をバカにするつもりでJavaScriptをバカにしてしまったら末代までの恥です。

とはいえ、JavaScriptも歴史ある言語ですから、数値周りの挙動も中々面白いものが出来上がっています。そのあたりをこの記事で楽しんでいただけたなら嬉しいです。