JavaScript

数値

 多くのプログラミング言語と違って、JavaScript では、整数と浮動小数点数を区別しません。JavaScript では、すべての数値を浮動小数点数で表します。具体的には、IEEE 754 標準で規定された 64 ビット浮動小数点形式です。最大値は ± 1.7976931348623157 × 10308 で、最小値は ± 5 × 10-324 です。

 JavaScript の数値形式では、 - 9007199254740992 (-253) ~ 9007199254740992 (253) までの整数は正確に表せます。この範囲外の整数を使った場合には、下位数値の精度が損なわれます。ただし、配列のインデックス指定や、別途で紹介するビット演算子のように、32 ビット整数として処理されるものもあります。

 JavaScript プログラム内に直接記述されている数字を数値リテラルと言います。これから、JavaScript がサポートする数値リテラルの種類について説明します。なお、すべての数値リテラルに対して、その前にマイナス記号( - )を記述することで符号を反転できます。ただし、このマイナス記号は、JavaScript の仕様上は単項演算子と見なされ、数値リテラルの一部ではありません。

整数リテラル

 JavaScript プログラム中では、10進整数は一連の数字を続けて記述します。例をいくつか示しましょう。


0
3
10000000

 整数を 16進数で指定することもできます。16進リテラルは、先頭が 0x または 0X 、その後に一連の 16進数値が続きます。16進数値は 0 から 9 までの数字と a (または A )から f (または F )までの英字です。この英字は 10進数の 10 から 15 に対応します。16進リテラルの例をいくつか示しましょう。


0xff
// 15*16 + 15 = 255 (10進)

oxCAFE911

 ECMAScript 標準では 8進リテラルはサポートされていません。しかしながら、JavaScript の処理系によってはサポートされている場合があるので紹介しておきます。8進リテラルは、先頭がゼロで、その後に一連の数値( 0 から 7 まで)が続きます。例を示しましょう。


0377
// 3*64 + 7*8 + 7 = 255 (10進)

 8進リテラルは、JavaScript の処理系によってサポートされていたり、サポートされていなかったりします。つまり、0 から始まる整数を記述すると、処理系によって 8進数として解釈されたり、10進数として解釈されたりするので、0 から始まる整数は使わないようにしてください。ECMAScript 5 の strictモードでは、8進数を使えません。

浮動小数点リテラル

 JavaScript 中では、浮動小数点リテラルを使って実数を記述します。浮動小数点リテラルでは小数部や指数も使用できます。浮動小数点リテラルは、先頭が整数部、その次に小数点、その後ろに小数部という形式で記述します。

 指数は、先頭が英字の e または E 、その次にプラス( + )またはマイナス( - )記号(これはあってもなくてもかまいません)、その次に整数指数という形式で、小数部の後ろに記述します。浮動小数点リテラルの値は、先行する整数部と小数部の数値に 10 を指数部で指定された回数だけ掛けた値になります。

 書式は次のとおりです。


[ 数値 ][ .数値 ][ (E|e) [ (+|-) ] 数値 ]

 実例を以下にいくつか挙げます。


3.14
2345.789
.333333333333333333

6.02e23
// 6.02 × 1023

1.4738223E-32
// 1.4738223 × 10-32

JavaScript での算術演算

 JavaScript の基本的な算術演算子には、加算( + )、減算( - )、乗算( * )、除算( / )、剰余( % )があります。

 これらの基本的な算術演算子の他に、もっと複雑な数学演算を行う関数がたくさんあります。これらの関数は、Math という名前のオブジェクトのプロパティとしてコア言語に組み込まれています。以下に例を示します。


Math.pow(2,53)
// => 9007199254740992: 2 の 53 乗。

Math.round(.6)
// => 1.0: 最近傍の整数に変換。

Math.ceil(.6)
// => 1.0: 整数に切り上げ。

Math.floor(.6)
// => 0.0: 整数に切り捨て

Math.abs(-5)
// => 5: 絶対値。

Math.max(x,y,z)
// 引数の中から最大値を返す。

Math.min(x,y,z)
// 引数の中から最小値を返す。

Math.random()
// 0 以上 1.0 未満の擬似乱数を返す。

Math.PI
// π : 円周率。

Math.E
// e: 自然対数の底。

Math.sqrt(3)
// 3 の平方根。

Math.pow(3,1/3)
// 3 の立方根。

Math.sin(0)
// 三角関数。Math.cos や Math.atan なども。

Math.log(10)
// 10 の自然対数。

Math.log(100)/Math.LN10
// 底が 10 の 100 の対数(100 の常用対数)。

Math.log(512)/Math.LN2
// 底が 2 の 512 の対数。

Math.exp(3)
// Math.E の 3 乗。

 JavaScript では、算術演算中にオーバーフローやアンダーフローが発生した場合や、0除算を行った場合もエラーが発生しません。算術演算の結果が表現可能な最大値より大きくなった場合(オーバーフロー)、算術演算の結果は無限大という特殊な値になります。JavaScript がこの値を出力する場合は、「 Infinity 」と出力します。同様に、ある負数の絶対値が表現可能な最大値を超えた場合、演算結果は特殊な無限大負値になります。JavaScript がこの値を出力する場合は、「 -Infinity 」と出力します。無限大値にどんな値を加算しても、減算しても、乗算しても、除算しても、無限大値のままです(符号が変わる場合はあります)。

 算術演算の結果の絶対値が表現可能な最小値よりも 0 に近づいた場合に、アンダーフローが発生します。この場合、JavaScript は 0 を返します。負の値に対してアンダーフローが発生した場合は、JavaScript は「負の 0 」という特殊な値を返します。この「負の 0 」は通常の 0 とほとんど区別がつかないので、JavaScript プログラマは気にする必要はありません。

 0 除算も JavaScript ではエラーにはなりません。無限大や、負の無限大が返されるだけです。しかし、1つだけ例外があります。それは 0 を 0 で割った時です。この計算結果は不定となり、JavaScript がこの値を出力する場合「 NaN 」(not-a-number)と出力します。無限大を無限大で割った場合や、負数の平方根を求めた場合、数値に変換できないオペランドに対して算術演算を行った場合にも NaN という結果になります。

 JavaScript には、InfinityNaN というグローバル変数があらかじめ定義されています。それぞれ、正の無限大と不定値(not-a-number)が格納されています。ECMAScript 3 では、この 2つのグローバル変数は読み書きできます。つまり、値を変更できます。ECMAScript 5 では、この問題は修正されました。値の読み出しのみとなり、変更できなくなっています。Number オブジェクトにも同様のプロパティが存在します。こちらは、ECMAScript 3 でも読み出しのみで変更はできません。以下に例をいくつか紹介します。


Infinity
// 読み書き可能な変数。初期値は無限大。

Number.POSITIVE_INFINITY
// 同じく無限大。読み出しのみ。

1/0
// この式も無限大を返す。

Number.MAX_VALUE + 1
// この計算も無限大になる。

Number.NEGATIVE_INFINITY
// 以下の式は、負の無限大になる。
 	-Infinity
 	-1/0
 	-Number.MAX_VALUE - 1

NaN
// 読み書き可能な変数。初期値は NaN。

Number.NaN
// 同じく NaN。読み出しのみ。

0/0
// この式は NaN と評価される。

Number.MIN_VALUE/2
// アンダーフローが発生し 0 になる。

-Number.MIN_VALUE/2
// 負の 0。
 	-1/Infinity
 	// これも負の 0。
 	-0

 JavaScript の NaN 値には、どの値と比較しても等しいと判定されない。という特徴があります。NaN 値同士を比べても等しいと判定されません。つまり、x == NaN と記述しても、ある変数 x の値が、NaN 値かどうかを判定できないということです。代わりに、x != x と記述してください。この式は、xNaN 値の時だけ、true になります。isNaN() 関数でも調べられます。この関数は、引数が NaN か、もしくは文字列やオブジェクトなどの数値以外の値の場合に、true を返します。同じように、isFinite() 関数は、引数が NaNInfinite-Infinite 以外の数値の場合に、true を返します。

 負の 0 にも少し特徴があります。負の 0 は、通常の 0 と等しいと判定されます。JavaScript の同値演算子でも等しいと判定されるので、事実上区別することはできません。ただし、除算で使った時にだけ違いが生じます。


var zero = 0;
// 通常の 0 。

var negz = -0;
// 負の 0 。

zero === negz
// => true: 通常の 0 と負の 0 は等しい。

1/zero === 1/negz
// => false: 無限大と負の無限大は等しくない。

2進浮動小数点と丸め誤差

 実数は無限に存在します。しかし、JavaScript の浮動小数点形式では、有限個(正確には、18437736874454810627個)しか表現出来ません。つまり、JavaScript で実数を処理する場合、実数を近似表現することになる場合がほとんどです。

 JavaScript をはじめとして、最近のプログラミング言語の大半で使われている IEEE-754 浮動小数点表現形式は、2進数表記ですので、1/2 や 1/8 、1/1024 などの小数は正確に表現出来ます。しかし、金融計算などで普段使っているのは 1/10 、1/100 などの 10進数です。2進浮動小数点表現形式では、0.1 のような値も正確には表現できません。

 JavaScript の数値の精度は十分高いので、0.1 をほぼ近似表現できます。しかし、数値を正確に表現できないために、問題が生じる場合もあります。次のコードを見てください。


var x = .3 - .2;
// 30 セントから 20 セントを減算する。

var y = .2 - .1;
// 20 セントから 10 セントを減算する。

x == y
// => false: 2つの値は同じではない。

x == .1
// => false: .3 - .2 は .1 と等しくない。

y == .1
// => true: .2 - .1 は .1 と等しい。

 丸め誤差のために、.3 と .2 の差分の近似表現と、.2 と .1 の差分の近似表現は正確には同じ値になりません。なお、この問題は、JavaScript 特有の問題ではないことをよく覚えておいてください。2進浮動小数点数値を使うプログラミング言語なら同じような問題が生じます。また、先ほど紹介したコードの x と y の値はほぼ同じで、両方とも正しい値に非常に近い値になっています。計算結果が問題となる場合はほとんどありません。等しいかどうかを比較しようとした時にだけ問題が生じます。

 将来的には、JavaScript が 10進数型をサポートするようになり、前述したような丸め誤差問題も解決するかもしれません。ただ、それまでは、重要な金融計算では、スケールを調整して整数を使うようにしてください。例えば、先程の例であれば、ドル単位で計算する代わりに、セント単位で計算するとよいでしょう。

日付と時刻

 コア JavaScript 言語には、日付と時刻を表現するオブジェクトを生成するために、Date() コンストラクタが用意されています。Date オブジェクトには、日付計算を行うための API がメソッド形式で提供されています。Date オブジェクトは、数値型などのような基本型ではありません。この項では、日付処理の概要についてだけ説明します。


var then = new Date(2013, 6, 27);
// 2013年7月27日。

var later = new Date(2013, 6, 27, 17, 10, 30);
// 2013年7月27日現地時間午後5:10:30。

var now = new Date();
// 現在の日付と時刻。

var elapsed = now - then;
// Date 型の減算。ミリ秒単位の間隔。

later.getFullYear()
// => 2013

later.getMonth()
// => 6: 1月を 0 とする月番号。

later.getDate()
// => 27: 1日を 1 とする日にち。

later.getDay()
// => 6: 曜日。0 が日曜日、6 が土曜日。

later.getHours()
// => 17: 現地時間午後 5時。

later.getUTCHours()
// UTC での時間。タイムゾーンに依存する。

later.toString()
// => "Sat Jul 27 2013 17:10:30 GMT-0800 (PST)"

later.toUTCString()
// => "Sun, 28 Jul 2013 01:10:30 GMT"

later.toLocaleDateString()
// => "27/07/2013"

later.toLocaleTimeString()
// => "05:10:30 PM"

later.toISOString()
// => "2013-07-28T01:10:30.000Z" : ES5 のみ。