5. 新しい数値とMathの機能
目次
この書籍へのご支援をお願いします: 購入 (PDF, EPUB, MOBI) または 寄付
(広告です。ブロックしないでください。)

5. 新しい数値とMathの機能



5.1 概要

5.1.1 新しい整数リテラル

2進数と8進数の表記で整数を指定できるようになりました

> 0xFF // ES5: hexadecimal
255
> 0b11 // ES6: binary
3
> 0o10 // ES6: octal
8

5.1.2 新しいNumberプロパティ

グローバルオブジェクトNumberには、いくつかの新しいプロパティが追加されました

5.1.3 新しいMathメソッド

グローバルオブジェクトMathには、数値、三角関数、およびビット単位演算のための新しいメソッドがあります。4つの例を見てみましょう。

Math.sign()は数値の符号を返します

> Math.sign(-8)
-1
> Math.sign(0)
0
> Math.sign(3)
1

Math.trunc()は数値の小数部分を削除します

> Math.trunc(3.1)
3
> Math.trunc(3.9)
3
> Math.trunc(-3.1)
-3
> Math.trunc(-3.9)
-3

Math.log10()は底が10の対数を計算します

> Math.log10(100)
2

Math.hypot()は、引数の二乗の和の平方根を計算します(ピタゴラスの定理)

> Math.hypot(3, 4)
5    

5.2 新しい整数リテラル

ECMAScript 5には、16進数整数のリテラルがすでにあります

> 0x9
9
> 0xA
10
> 0x10
16
> 0xFF
255

ECMAScript 6では、2つの新しい種類の整数リテラルが導入されています

NumberメソッドtoString(radix)を使用すると、10以外の基数で数値を確認できることを覚えておいてください

> 255..toString(16)
'ff'
> 4..toString(2)
'100'
> 8..toString(8)
'10'

(プロパティアクセスのドットが小数点と混同されないように、二重ドットが必要です。)

5.2.1 8進数リテラルのユースケース:Unixスタイルのファイル権限

Node.jsのファイルシステムモジュールでは、いくつかの関数にパラメータmodeがあります。その値は、Unixからの持ち越しであるエンコードを介してファイル権限を指定するために使用されます。

つまり、権限は9ビットで表すことができます(3つのカテゴリとそれぞれ3つの権限)

  ユーザー グループ 全員
権限 r、w、x r、w、x r、w、x
ビット 8, 7, 6 5, 4, 3 2, 1, 0

1つのカテゴリのユーザーの権限は、3ビットで格納されます

ビット 権限 8進数
000 ––– 0
001 ––x 1
010 –w– 2
011 –wx 3
100 r–– 4
101 r–x 5
110 rw– 6
111 rwx 7

つまり、8進数はすべての権限をコンパクトに表現したものであり、ユーザーのカテゴリごとに1桁、3桁のみが必要です。2つの例

5.2.2 Number.parseInt()と新しい整数リテラル

Number.parseInt()(グローバル関数parseInt()と同じ処理を行います)には、次のシグネチャがあります

Number.parseInt(string, radix?)
5.2.2.1 Number.parseInt(): 16進数リテラル

Number.parseInt()は16進数リテラル表記を特別にサポートしています。stringのプレフィックス0x(または0X)は、以下の場合に削除されます。

> Number.parseInt('0xFF')
255
> Number.parseInt('0xFF', 0)
255
> Number.parseInt('0xFF', 16)
255

他のすべての場合、数字は最初の数字以外までのみ解析されます

> Number.parseInt('0xFF', 10)
0
> Number.parseInt('0xFF', 17)
0
5.2.2.2 Number.parseInt(): 2進数および8進数リテラル

ただし、Number.parseInt()は2進数または8進数リテラルを特別にサポートしていません!

> Number.parseInt('0b111')
0
> Number.parseInt('0b111', 2)
0
> Number.parseInt('111', 2)
7

> Number.parseInt('0o10')
0
> Number.parseInt('0o10', 8)
0
> Number.parseInt('10', 8)
8

これらの種類のリテラルを解析する場合は、Number()を使用する必要があります

> Number('0b111')
7
> Number('0o10')
8

Number.parseInt()は、特別なプレフィックスがなく、パラメータradixが指定されている限り、異なる基数を持つ数値で正常に機能します

> Number.parseInt('111', 2)
7
> Number.parseInt('10', 8)
8

5.3 新しい静的Numberプロパティ

このセクションでは、ECMAScript 6でコンストラクターNumberが取得した新しいプロパティについて説明します。

5.3.1 以前のグローバル関数

4つの数値関連関数はすでにグローバル関数として利用可能であり、メソッドとしてNumberに追加されました:isFiniteisNaNparseFloatparseInt。それらはすべてグローバル関数とほぼ同じように機能しますが、isFiniteisNaNは、特にisNaNにとって重要なことですが、引数を数値に強制変換しなくなりました。次のサブセクションでは、すべての詳細について説明します。

5.3.1.1 Number.isFinite(number)

Number.isFinite(number)は、numberが実際の数値であるかどうかを判断します(Infinity-Infinity、またはNaNのいずれでもない)。

> Number.isFinite(Infinity)
false
> Number.isFinite(-Infinity)
false
> Number.isFinite(NaN)
false
> Number.isFinite(123)
true

このメソッドの利点は、パラメータを数値に強制変換しないことです(グローバル関数はそうしますが)

> Number.isFinite('123')
false
> isFinite('123')
true
5.3.1.2 Number.isNaN(number)

Number.isNaN(number)は、numberが値NaNであるかどうかを確認します。

このチェックを行うES5の1つの方法は、!==を使用することです。

> const x = NaN;
> x !== x
true

より記述的な方法は、グローバル関数isNaN()を使用することです。

> const x = NaN;
> isNaN(x)
true

ただし、この関数は数値以外を数値に強制変換し、結果がNaNの場合にtrueを返します(通常は望ましくありません)

> isNaN('???')
true

新しいメソッドNumber.isNaN()は、引数を数値に強制変換しないため、この問題を抱えていません

> Number.isNaN('???')
false
5.3.1.3 Number.parseFloatNumber.parseInt

次の2つのメソッドは、同じ名前のグローバル関数とまったく同じように機能します。それらは完全性のためにNumberに追加されました。これで、数値関連のすべての関数がそこで利用可能になりました。

5.3.2 Number.EPSILON

特に小数部では、JavaScriptで丸め誤差が問題になる可能性があります3。たとえば、0.1と0.2は正確に表現できず、それらを加算して0.3と比較すると(これも正確に表現できません)、そのことに気付きます。

> 0.1 + 0.2 === 0.3
false

Number.EPSILONは、浮動小数点数を比較する際の妥当な誤差範囲を指定します。次の関数で示されているように、浮動小数点値を比較するためのより良い方法を提供します。

function epsEqu(x, y) {
    return Math.abs(x - y) < Number.EPSILON;
}
console.log(epsEqu(0.1+0.2, 0.3)); // true

5.3.3 Number.isInteger(number)

JavaScriptには浮動小数点数(倍精度)しかありません。したがって、整数は単に小数部のない浮動小数点数です。

Number.isInteger(number)は、numberが数値であり、小数部がない場合にtrueを返します。

> Number.isInteger(-17)
true
> Number.isInteger(33)
true
> Number.isInteger(33.1)
false
> Number.isInteger('33')
false
> Number.isInteger(NaN)
false
> Number.isInteger(Infinity)
false

5.3.4 安全な整数

JavaScriptの数値には、53ビットの符号付き整数を表すのに十分なストレージスペースしかありません。つまり、範囲−253 < i < 253の整数i安全です。それが正確に何を意味するのかは、すぐに説明します。次のプロパティは、JavaScriptの整数が安全かどうかを判断するのに役立ちます

安全な整数の概念は、JavaScript における数学的な整数の表現方法に焦点を当てています。範囲 (−253, 253) (下限と上限は含まない)では、JavaScript の整数は安全です。つまり、JavaScript の整数とそれが表す数学的な整数との間に一対一のマッピングが存在します。

この範囲を超えると、JavaScript の整数は安全でなくなります。つまり、2つ以上の数学的な整数が同じ JavaScript の整数として表現されます。例えば、253 から始まると、JavaScript は数学的な整数のうち、2つおきにしか表現できません。

> Math.pow(2, 53)
9007199254740992

> 9007199254740992
9007199254740992
> 9007199254740993
9007199254740992
> 9007199254740994
9007199254740994
> 9007199254740995
9007199254740996
> 9007199254740996
9007199254740996
> 9007199254740997
9007199254740996

したがって、安全な JavaScript の整数とは、単一の数学的な整数を一意に表す整数のことです。

安全な整数の下限と上限を指定する2つの静的なNumberプロパティは、次のように定義できます。

Number.MAX_SAFE_INTEGER = Math.pow(2, 53)-1;
Number.MIN_SAFE_INTEGER = -Number.MAX_SAFE_INTEGER;

Number.isSafeInteger() は、JavaScript の数値が安全な整数であるかどうかを判定し、次のように定義できます。

Number.isSafeInteger = function (n) {
    return (typeof n === 'number' &&
        Math.round(n) === n &&
        Number.MIN_SAFE_INTEGER <= n &&
        n <= Number.MAX_SAFE_INTEGER);
}

指定された値nに対して、この関数は最初にnが数値であり、かつ整数であるかどうかをチェックします。両方のチェックが成功した場合、nMIN_SAFE_INTEGER 以上であり、かつ MAX_SAFE_INTEGER 以下であれば、n は安全です。

5.3.4.2 整数を使用した計算はいつ正しいのか?

整数を使用した計算の結果が正しいことを確認するにはどうすればよいでしょうか?例えば、次の結果は明らかに正しくありません。

> 9007199254740990 + 3
9007199254740992

2つの安全なオペランドがありますが、結果は安全ではありません。

> Number.isSafeInteger(9007199254740990)
true
> Number.isSafeInteger(3)
true
> Number.isSafeInteger(9007199254740992)
false

次の結果も正しくありません。

> 9007199254740995 - 10
9007199254740986

今回は、結果は安全ですが、オペランドの1つが安全ではありません。

> Number.isSafeInteger(9007199254740995)
false
> Number.isSafeInteger(10)
true
> Number.isSafeInteger(9007199254740986)
true

したがって、整数演算子opを適用した結果が正しいと保証されるのは、すべてのオペランドと結果が安全な場合のみです。より形式的に言えば、

isSafeInteger(a) && isSafeInteger(b) && isSafeInteger(a op b)

は、a op bが正しい結果であることを意味します。

5.4 新しいMath機能

グローバルオブジェクトMathには、ECMAScript 6 でいくつかの新しいメソッドがあります。

5.4.1 さまざまな数値機能

5.4.1.1 Math.sign(x)

Math.sign(x) は、以下を返します。

> Math.sign(-8)
-1
> Math.sign(3)
1

> Math.sign(0)
0
> Math.sign(NaN)
NaN

> Math.sign(-Infinity)
-1
> Math.sign(Infinity)
1
5.4.1.2 Math.trunc(x)

Math.trunc(x) は、x の小数部分を削除します。他の丸めメソッド Math.floor()Math.ceil()Math.round() を補完します。

> Math.trunc(3.1)
3
> Math.trunc(3.9)
3
> Math.trunc(-3.1)
-3
> Math.trunc(-3.9)
-3

Math.trunc() は、次のように実装できます。

function trunc(x) {
    return Math.sign(x) * Math.floor(Math.abs(x));
}
5.4.1.3 Math.cbrt(x)

Math.cbrt(x) は、x の立方根(∛x)を返します。

> Math.cbrt(8)
2

5.4.2 指数関数と対数関数で 1 の代わりに 0 を使用する

小さな分数は、ゼロの後に続く場合、より正確に表現できます。これを小数で示します(JavaScript の数値は内部的に基数 2 で格納されますが、同じ推論が適用されます)。

基数 10 の浮動小数点数は、内部的に 仮数 × 10指数 として表されます。仮数には、小数点記号の前に単一の数字があり、指数は必要に応じて小数点記号を「移動」します。つまり、小さな分数を内部表現に変換する場合、小数点記号の前のゼロは、小数点記号の前の 1 よりも小さい仮数につながります。例えば

精度に関しては、ここで重要な量は、有効数字で測定された仮数の容量です。そのため、(A)は(B)よりも高い精度が得られます。

さらに、JavaScript はゼロに近い数値(例えば、小さな分数)をより高い精度で表します。

5.4.2.1 Math.expm1(x)

Math.expm1(x)Math.exp(x)-1 を返します。Math.log1p() の逆関数です。

したがって、このメソッドは、Math.exp() の結果が 1 に近い場合に、より高い精度を提供します。次のインタラクションで、2つの違いを確認できます。

> Math.expm1(1e-10)
1.00000000005e-10
> Math.exp(1e-10)-1
1.000000082740371e-10

前者がより良い結果です。これは、任意の精度の浮動小数点数(「ビッグフロート」)用のライブラリ(例えば、decimal.js)を使用することで検証できます。

> var Decimal = require('decimal.js').config({precision:50});
> new Decimal(1e-10).exp().minus(1).toString()
'1.000000000050000000001666666666708333333e-10'
5.4.2.2 Math.log1p(x)

Math.log1p(x)Math.log(1 + x) を返します。Math.expm1() の逆関数です。

したがって、このメソッドを使用すると、1に近いパラメータをより高い精度で指定できます。次の例は、その理由を示しています。

次の 2 つの log() の呼び出しは同じ結果になります。

> Math.log(1 + 1e-16)
0
> Math.log(1 + 0)
0

対照的に、log1p() は異なる結果を生成します。

> Math.log1p(1e-16)
1e-16
> Math.log1p(0)
0

Math.log1p() の精度が高い理由は、1 + 1e-16 の正しい結果が 1e-16 よりも多くの有効数字を持つためです。

> 1 + 1e-16 === 1
true
> 1e-16 === 0
false

5.4.3 底が 2 と 10 の対数

5.4.3.1 Math.log2(x)

Math.log2(x) は、底が 2 の対数を計算します。

> Math.log2(8)
3
5.4.3.2 Math.log10(x)

Math.log10(x) は、底が 10 の対数を計算します。

> Math.log10(100)
2

5.4.4 JavaScript へのコンパイルのサポート

Emscripten は、後に asm.js によって採用されたコーディングスタイルを開拓しました。仮想マシン(バイトコードを考えてください)の操作は、JavaScript の静的なサブセットで表現されます。そのサブセットは、JavaScript エンジンによって効率的に実行できます。C++ からのコンパイルの結果である場合、ネイティブ速度の約 70% で実行されます。

次のMathメソッドは、主に asm.js および同様のコンパイル戦略をサポートするために追加されたものであり、他のアプリケーションにはそれほど役に立ちません。

5.4.4.1 Math.fround(x)

Math.fround(x) は、x を 32 ビット浮動小数点値(float)に丸めます。asm.js が、エンジンに内部的に float 値を使用するように指示するために使用されます。

5.4.4.2 Math.imul(x, y)

Math.imul(x, y) は、2 つの 32 ビット整数 xy を乗算し、結果の下位 32 ビットを返します。これは、JavaScript 演算子を使用して結果を 32 ビットに戻すことでシミュレートできない、唯一の 32 ビット基本数学演算です。例えば、idiv は次のように実装できます。

function idiv(x, y) {
    return (x / y) | 0;
}

対照的に、2つの大きな 32 ビット整数を乗算すると、下位ビットが失われるほど大きな倍精度値が生成される可能性があります。

5.4.5 ビット演算

なぜこれが興味深いのでしょうか?Miro Samek による「高速、決定的、移植可能な先頭ゼロのカウント」からの引用です。

整数数値の先頭のゼロをカウントすることは、サウンドやビデオ処理におけるサンプルの正規化など、多くの DSP アルゴリズムにおいて重要な操作であり、リアルタイムスケジューラで実行準備完了の最優先タスクをすばやく見つけるためにも重要です。

5.4.6 三角関数メソッド

5.5 FAQ:数値

5.5.1 JavaScript の 53 ビット範囲を超える整数を使用するにはどうすればよいですか?

JavaScript の整数は 53 ビットの範囲を持っています。64 ビット整数が必要な場合は常に問題になります。例えば、Twitter は JSON API で、ツイート ID が大きくなりすぎたときに、整数から文字列に切り替える必要がありました。

現時点では、その制限を回避する唯一の方法は、高精度の数値(ビッグイントまたはビッグフロート)用のライブラリを使用することです。このようなライブラリの 1 つが decimal.js です。

JavaScript でより大きな整数をサポートする計画は存在しますが、実現するまでには時間がかかる可能性があります。

次:6. 新しい文字列機能