初めに
ソートの仕組みを書いて以来Numberのことがあまり触れてない気がします。今回は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()
はアンダースコアのある文字列は正確に処理できません。
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)
数値型の値を、指定した基数の表現に転換させる。
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)
小数点以下指定の桁まで丸めて文字列で返します。
let num = 12.34;
console.log(num.toFixed(1)); // '12.3'
num = 0.1234;
console.log(num.toFixed(1)); // '0.1'
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
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
で浮動小数点の差の範囲を抑えることによって、誤差が範囲内にあるときにはイコールとして認める。
// 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
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
。
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
。
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()
と isFinite()
と違います。
一番の違いは、グローバル関数のisNaN()
とisFinite()
は引数をNumber()
で型変換してから処理する。それで予想外の結果につながります。
Number.parseInt(stringm radix) & Number.parseFloat(string)
グローバル関数のparseInt()
とparseFloat()
と変わらない。
(Numberオブジェクトへ移行したのはモジュール化のためだそうです。)
Number.parseInt()
は引数の文字列を解釈して、指定された基数(radix)の整数値を返す。
2 <= radix <= 36
、範囲外の基数を設置、またはスペース以外の文字が数値に変換できない場合はNaN
を返す。
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
を返す。
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
になるかもしれません。
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
になるかもしれません。
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
内部の実装は、このようなものかもしれません。
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
になってしまいました。
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()
に判定してもらってから、結果と同じである場合は信頼できる計算になる。
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
Math methods
Rounding - Math.floor()
floor(床)。引数の最小の整数を返す。
(負数は小数がある場合は一つ左へ移動)
let a = 3.95;
let b = -1.1;
console.log(Math.floor(a)); // 3
console.log(Math.floor(b)); // -2
Rounding - Math.ceil()
ceil(天井)。引数の最大の整数を返す。
let a = 3.05;
let b = -1.95;
console.log(Math.ceil(a)); // 4
console.log(Math.ceil(b)); // -1
Rounding - Math.round()
round(丸め)。四捨五入して最も近い整数を返す。
(負数の動きに気を付けましょう...)
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(胴体だけ)。小数点以下を取り除く。
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
。範囲内の疑似乱数を返す。
// random for 1 ~ 10
function getRandomOneToTen() {
return Math.floor(Math.random() * 10) + 1;
}
console.log(getRandomOneToTen());
Maximum - Math.max()
引数の最大の数を返す。
console.log(Math.max(1, 2, 3)); // 3
let arr = [4, 5, 6];
console.log(Math.max(...arr)); // 6
Minimum - Math.min()
引数の最小の数を返す。
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)を返す。
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)の結果を返す。
指数が小数の場合は平方根と表し、負数の場合はマイナス乗と表す。
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)