JavaScriptのNumberについて

# 初めに
ソートの仕組みを書いて以来Numberのことがあまり触れてない気がします。今回はNumberの基本知識、一部の関連メソッドをまとめてみました。

参考文章は以下です。

https://javascript.info/number

https://es6.ruanyifeng.com/#docs/number

# Memo
- 数字の間にあるアンダースコア`_`はJavaScriptエンジンに無視されるので、区切りしてより読みやすくなる。
- `e`は10の指数の表しです。`e6`⇒`* 1000000`。`-6e`は` * 0.000001`。
- 2進数（Binary）prefix⇒`0b`(0B)、digits⇒`0`, `1`。
- 8進数（Octal）prefix⇒`0o`(0O)、digits⇒`0...7`。
- 16進数（Hex）prefix⇒`0x`(0X)、digits⇒`0...9`, `A...F`。
- `Number.MAX_SAFE_INTEGER`⇒`9007199254740991`(2^53 - 1)。
- `Number.MIN_SAFE_INTEGER`⇒`-9007199254740991`(-(2^53 - 1))。
- `.toString(36)`(base=36 digits)、digits⇒`0...9`, `A...Z`。（URL、長い数値の識別子（numeric identifier）の処理など）
- `Number()`、`parseInt()`、`parseFloat()`はアンダースコアのある文字列は正確に処理できません。

```jsx
console.log(Number('123_456')); // NaN
console.log(parseInt('123_456')); // 123
console.log(parseFloat('1.00_123')); // 1

console.log(Number(123_456)); // 123456
console.log(parseInt(123_456)); // 123456
console.log(parseFloat(1.00_123)); // 1.00123
```

# Number methods
# Number.prototype.toString(radix)
数値型の値を、指定した基数の表現に転換させる。
```jsx
let num = 255;
console.log(num.toString(16)); // 'ff'
console.log(num.toString(8)); // '377'
console.log(num.toString(2)); // '11111111'
console.log(num.toString()); // '255'

num = 123456;
console.log(num.toString(36)); // '2n9c'
```

## Number.prototype.toFixed(precision)
小数点以下指定の桁まで丸めて文字列で返します。
```jsx
let num = 12.34;
console.log(num.toFixed(1)); // '12.3'
num = 0.1234;
console.log(num.toFixed(1)); // '0.1'
```
```jsx
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 - 0.3); // 5.551115123125783e-17
console.log(5.551115123125783e-17.toFixed(20)); // '0.00000000000000005551'
```

## Number.EPSILON
`Number.EPSILON`はプロパティ、`2^-52`の値を持っています。
`0 < Number.EPSILON < 1`
```jsx
console.log(Number.EPSILON); // 2.220446049250313e-16
console.log(Number.EPSILON === Math.pow(2, -52)); // true
console.log(Number.EPSILON.toFixed(20)); // '0.00000000000000022204'
console.log(0 < Number.EPSILON && Number.EPSILON < 1); // true

console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON); // true
```
`Number.EPSILON`で浮動小数点の差の範囲を抑えることによって、誤差が範囲内にあるときにはイコールとして認める。
```jsx
// expect the difference of floating point is less than 2^-50
const maxFloat = Number.EPSILON * Math.pow(2, 2)
console.log(maxFloat); // 8.881784197001252e-16
console.log((0.1 + 0.2 - 0.3) < maxFloat); // true
```
```jsx
function withinErrorMargin(left, right) {
  return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}

console.log(0.1 + 0.2 === 0.3); // false
console.log(withinErrorMargin(0.1 + 0.2, 0.3)); // true
console.log(1.1 + 1.3 === 2.4); // false
console.log(withinErrorMargin(1.1 + 1.3, 2.4)); // true
```

## Number.isFinite() & Number.isNaN()
`Number.isFinite()`は有限数であるかを判断してブーリアンを返す。
まずはタイプ検査（`Number()`で型変換ではない）をして、
`Number`であって`Infinity`/`-Infinity`の場合は`true`、残りは`false`。
`Number`ではない場合は`false`。
```jsx
console.log(Number.isFinite(1 / 0)); // false // Infinity
console.log(Number.isFinite(0 / 1)); // true // 0
console.log(Number.isFinite(0 / 0)); // false // NaN
console.log(Number.isFinite(-Infinity)); // false
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isFinite(NaN)); // false
console.log(Number.isFinite('foo')); // false // not number
console.log(Number.isFinite('0')); // false // not number

console.log(Number.isFinite(2 ** 54)); // true
// note: not a safe integer, but a finite number
```
`Number.isNaN()`は`NaN`であるかを判断してブーリアンを返す。
タイプ検査で`Number`である場合は、`NaN`であればtrue、残りは`false`。
`Number`ではない場合は`false`。
```jsx
console.log(Number.isNaN(0 / 0)); // true // NaN
console.log(Number.isNaN('0')); // false // not number
console.log(Number.isNaN('foo')); // false // not number

console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(Number.NaN)); // true

console.log(Number.isNaN({})); // false
console.log(Number.isNaN('')); // false
console.log(Number.isNaN(null)); // false
console.log(Number.isNaN(undefined)); // false
```
この二つの方法は[　`isNaN()`　](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/isNaN)と[　`isFinite()`　](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/isFinite)と違います。
一番の違いは、グローバル関数の`isNaN()`と`isFinite()`は引数を`Number()`で型変換してから処理する。それで予想外の結果につながります。

## Number.parseInt(stringm radix) & Number.parseFloat(string)
グローバル関数の`parseInt()`と`parseFloat()`と変わらない。
[（Numberオブジェクトへ移行したのはモジュール化のためだそうです。）](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat#number.parsefloat_%E3%81%A8_parsefloat)

`Number.parseInt()`は引数の文字列を解釈して、指定された基数（radix）の整数値を返す。
`2 <= radix <= 36`、範囲外の基数を設置、またはスペース以外の文字が数値に変換できない場合は`NaN`を返す。
```jsx
console.log(Number.parseInt('1', 2)); // 1
console.log(Number.parseInt('2', 2)); // NaN // 0 or 1
console.log(Number.parseInt('1111', 2)); // 15

console.log(Number.parseInt('1', 8)); // 1
console.log(Number.parseInt('2', 8)); // 2
console.log(Number.parseInt('8', 8)); // NaN // 0-7
console.log(Number.parseInt('9', 8)); // NaN // 0-7
console.log(Number.parseInt('10', 8)); // 8

console.log(Number.parseInt('1', 16)); // 1
console.log(Number.parseInt('2', 16)); // 2
console.log(Number.parseInt('8', 16)); // 8
console.log(Number.parseInt('16', 16)); // 22 // 0-9, A-F
console.log(Number.parseInt('G', 16)); // NaN // 0-9, A-F
console.log(Number.parseInt('0xff', 16)); // 255

console.log(Number.parseInt('1', 37)); // NaN
```

`Number.parseFloat()`は引数を解釈して浮動小数点値を返す。
数値で解釈できない場合は`NaN`を返す。
```jsx
console.log(Number.parseFloat('1.234abc')); // 1.234
console.log(Number.parseFloat('1.234efg')); // 1.234
console.log(Number.parseFloat('1.2a34')); // 1.2
console.log(Number.parseFloat('a1.234')); // NaN
console.log(Number.parseFloat('    1.234')); // 1.234 // whitespace is ignored
```
引数の文字列を走査して、数値である部分だけ返す。
数字でない要素に遭ったらすぐ数字の部分を返すが、最初から数字ではない場合は`NaN`を返す。
先頭にあるスペースを無視する。

## Number.isInteger()
引数が整数であるかを判定する。
整数：`Infinity`、`-Infinity`、`NaN`ではない数字。整数として表せる浮動小数点も`true`を返すが、精度を失った浮動小数点が切り捨てで`true`になるかもしれません。
```jsx
console.log(Number.isInteger(0.1)); // false
console.log(Number.isInteger(Math.PI)); // false

console.log(Number.isInteger(NaN)); // false
console.log(Number.isInteger(Infinity)); // false
console.log(Number.isInteger(-Infinity)); // false

console.log(Number.isInteger('10')); // false
console.log(Number.isInteger('')); // false
console.log(Number.isInteger(true)); // false
console.log(Number.isInteger([1])); // false

console.log(Number.isInteger(5.0)); // true

console.log(Number.isInteger(Number.MAX_SAFE_INTEGER + 1)); // true
// not safe, but it is an integer
console.log(Number.isInteger(Number.MIN_SAFE_INTEGER - 1)); // true
// not safe, but it is an integer
console.log(Number.isInteger(3.0000000000000002)); // true
// 2 is missing
```

## Number.isSafeInteger()
引数が安全な整数であるかを判定する。
しかし精度を失った浮動小数点が`true`になるかもしれません。
```jsx
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER)); // true
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1)); // false
console.log(Number.isSafeInteger(Number.MIN_SAFE_INTEGER)); // true
console.log(Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1)); // false

console.log(Number.isSafeInteger(NaN)); // false
console.log(Number.isSafeInteger(Infinity)); // false
console.log(Number.isSafeInteger(-Infinity)); // false

console.log(Number.isSafeInteger(1.2)); // false
console.log(Number.isSafeInteger(3.0000000000000002)); // true // the same problem
```
内部の実装は、このようなものかもしれません。
```jsx
function isSafe(num) {
  return (
    typeof num === 'number'
    && Math.round(num) === num
    && Number.MIN_SAFE_INTEGER <= num
    && num <= Number.MAX_SAFE_INTEGER
  );
}

console.log(isSafe(Number.MAX_SAFE_INTEGER)); // true
console.log(isSafe(Number.MAX_SAFE_INTEGER + 1)); // false
console.log(isSafe(Number.MIN_SAFE_INTEGER)); // true
console.log(isSafe(Number.MIN_SAFE_INTEGER - 1)); // false
console.log(isSafe(1.2)); // false
console.log(isSafe(3.0000000000000002)); // true
```
しかし下のように、実行するとき引数の計算がさきに行って、
`9007199254740993 - 990`の結果は`9007199254740993`、安全な整数に越えて精度を失ったため`9007199254740002`として`Number.isSafeInteger()`に受け入れられ、安全整数ではないのに`true`になってしまいました。
```jsx
console.log(Number.isSafeInteger(9007199254740993)); // false
console.log(Number.isSafeInteger(990)); // true
console.log(Number.isSafeInteger(9007199254740993 - 990)); // true
console.log((9007199254740993 - 990)); // 9007199254740002 // lost precision
// 9007199254740993 - 990 = 9007199254740003, over the Number.MAX_SAFE_INTEGER
```
なので計算を行う値がすべて、一度`Number.isSafeInteger()`に判定してもらってから、結果と同じである場合は信頼できる計算になる。
```jsx
function trusty(left, right, result) {
  if (
    Number.isSafeInteger(left)
    && Number.isSafeInteger(right)
    && Number.isSafeInteger(result)
  ) {
    return result;
  }
  throw new RangeError('Operation can not be trusted')
}

console.log(trusty(9007199254740993, 990, 9007199254740993 - 990));
// RangeError: Operation can not be trusted
console.log(trusty(1, 2, 3)); // 3
```

# about 0 & -0
https://qiita.com/hu-yu/items/50ca7faafa288dc13140#positive-zero0-vs-negative-zero-0

# Math methods
## Rounding - Math.floor()
floor（床）。引数の最小の整数を返す。
（負数は小数がある場合は一つ左へ移動）
```jsx
let a = 3.95;
let b = -1.1;
console.log(Math.floor(a)); // 3
console.log(Math.floor(b)); // -2
```

## Rounding - Math.ceil()
ceil（天井）。引数の最大の整数を返す。
```jsx
let a = 3.05;
let b = -1.95;
console.log(Math.ceil(a)); // 4
console.log(Math.ceil(b)); // -1
```

## Rounding - Math.round()
round（丸め）。四捨五入して最も近い整数を返す。
（負数の動きに気を付けましょう...）
```jsx
let a = 3.5;
let b = -1.45;
console.log(Math.round(a)); // 4
console.log(Math.round(b)); // -1

a = 3.49;
b = -1.5;
console.log(Math.round(a)); // 3
console.log(Math.round(b)); // -1

b = -1.51;
console.log(Math.round(b)); // -2
```

## Rounding - Math.trunc()
trunc（胴体だけ）。小数点以下を取り除く。
```jsx
let a = 3.5;
let b = -1.45;
console.log(Math.trunc(a)); // 3
console.log(Math.trunc(b)); // -1

a = 3.49;
b = -1.5;
console.log(Math.trunc(a)); // 3
console.log(Math.trunc(b)); // -1

b = -1.51;
console.log(Math.trunc(b)); // -1
```

## Math.random()
ランダム範囲は`0 <= n < 1`。範囲内の疑似乱数を返す。
```jsx
// random for 1 ~ 10
function getRandomOneToTen() {
  return Math.floor(Math.random() * 10) + 1;
}
console.log(getRandomOneToTen());
```

## Maximum - Math.max()
引数の最大の数を返す。
```jsx
console.log(Math.max(1, 2, 3)); // 3

let arr = [4, 5, 6];
console.log(Math.max(...arr)); // 6
```

## Minimum - Math.min()
引数の最小の数を返す。
```jsx
console.log(Math.min(1, 2, 3)); // 1

let arr = [4, 5, 6];
console.log(Math.min(...arr)); // 4
```

## Absolute value - Math.abs()
数値間の絶対値（absolute value）を返す。
```jsx
console.log(Math.abs(3 - 5)); // 2
console.log(Math.abs(5 - 3)); // 2
console.log(Math.abs(1)); // 1
```

## Exponentiation - Math.pow(base, exponent)
底（base）とべき指数（exponent）の結果を返す。
指数が小数の場合は平方根と表し、負数の場合はマイナス乗と表す。
```jsx
console.log(Math.pow(7, 3)); // 343
console.log(Math.pow(4, 0.5)); // 2 // sqrt(4) // 4^(1/2)
console.log(Math.pow(9, 0.5)); // 3 // sqrt(9) // 9^(1/2)
console.log(Math.pow(4, -2)); // 0.0625 // 1/(4^2)
```