LoginSignup
0
0

More than 1 year has passed since last update.

JavaScriptのNumberについて

Last updated at Posted at 2022-08-31

初めに

ソートの仕組みを書いて以来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_INTEGER9007199254740991(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-InfinityNaNではない数字。整数として表せる浮動小数点も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)
0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0