JavaScript 入門 | 標準オブジェクト | Number オブジェクト

JavaScript における数の表現

JavaScript では整数と実数を別々に区別せず、すべての数を 1 種類の実数として表現する。
JavaScript のすべての数値は、IEEE 754 国際標準で定義された 64 ビット浮動小数点数として保存される。

64 ビット浮動小数点数 (double precision floating point numbers) は、メモリに次のような形で保存される。

0 ~ 51 ビット 52 ~ 62 ビット 63 ビット
合計 52 ビットの仮数部 合計 11 ビットの指数部 合計 1 ビットの符号部

このような 64 ビット浮動小数点数の精度は、整数部は 15 桁まで、小数部は 17 桁までだけ有効である。

次の例は、64 ビット浮動小数点数の精度を調べる例である。

var x = 999999999999999;  // 15 桁の整数部
var y = 9999999999999999; // 16 桁の整数部
var z = 0.1 + 0.2
x; // 999999999999999
y; // 10000000000000000
z; // 0.30000000000000004

上の例で変数 z の値を見ると、誤差が発生していることが分かる。
このように浮動小数点数を使って行う算術演算の結果値は、常に誤差が発生する可能性を持っている。
これは JavaScript だけの問題ではなく、浮動小数点数で実数を表現するすべてのプログラミング言語における問題である。

JavaScript では、このような誤差をなくすために、まず整数の形へ変換して計算を行い、再び実数の形へ再変換する方法も使用できる。

var z = (0.2 * 10 + 0.1 * 10) / 10; // 0.3

基数表現

JavaScript では基本的に 10 進法を使用して数を表現する。
しかし、0x 接頭辞を使用して 16 進法で数を表現することもできる。

var x = 0xAB; // 16 進法で表現された 10 進数 171
var y = 29;   // 10 進法で表現された 10 進数 29
x + y;        // どちらの数も 10 進法へ自動変換されて計算される。 -> 200

上の例のように、JavaScript では算術演算時にすべての数が 10 進数へ自動変換されて計算される。

また、数値に toString() メソッドを使用して、その数値を複数の基数の形へ変換できる。

var num = 256;
num.toString(2);       // 2 進法へ変換: 100000000
num.toString(8);       // 8 進法へ変換: 400
num.toString(10);      // 10 進法へ変換: 256
num.toString(16);      // 16 進法へ変換: 100
// 2 進数へ変換した結果値を文字列として返す。
num.toString(2);       // 100000000
// 文字列を数値で割ったため、自動的に 10 進数へ変換されて算術演算された結果値
(num.toString(2) / 2); // 50000000

toString() メソッドは、その数値の基数を実際に変えるのではなく、渡された基数へ変換された形の文字列を返す。

Infinity

JavaScript では、正の無限大を意味する Infinity 値と、負の無限大を意味する -Infinity 値を使用できる。
Infinity 値はユーザーが任意に変更できない読み取り専用値であり、JavaScript のどの数よりも大きい数として扱われる。

var x = 10 / 0;         // 数値を 0 で割ると Infinity を返す。
var y = Infinity * 1000 // Infinity にどのような数を算術演算しても Infinity を返す。
var z = 1 / Infinity    // Infinity の逆数は 0 を返す。
x;                      // Infinity
y;                      // Infinity
z;                      // 0

NaN

NaN (Not A Number) は数値ではないという意味で、定義されていない値や表現できない値を指す。
0 を 0 で割った場合や、数値へ変換できないオペランドで算術演算を試みた場合に返される読み取り専用値である。

var x = 100 - "10";     // "10" は自動的に数値へ変換されて計算される。
var y = 100 - "文字列"; // "文字列" は数値へ変換できないため NaN を返す。
var z = 0 / 0;          // 0 を 0 で割ることはできないため NaN を返す。
x;                      // 90
y;                      // NaN
z;                      // NaN

JavaScript のグローバル関数の 1 つである isNaN() 関数を使用すると、受け取った値が数値かどうかを判定できる。

var x = 100 * "文字列";
if(isNaN(x)) { // 渡された値が数値かどうかを検査する。
    document.write("変数 x の値は数値ではない。");
} else {
    document.write("変数 x の値は数値である。");
}

null、undefined、NaN、Infinity の比較

JavaScript では、少し似ているようでまったく異なる 4 つの値を提供している。

  • null は object 型であり、まだ「値」が決まっていないことを意味する値である。
  • undefined は null とは異なり 1 つの型であり、「型」が決まっていないことを意味する値でもある。
  • NaN は number 型であり、「数値ではない」ことを意味する数値である。
  • Infinity は number 型であり、「無限大」を意味する数値である。

JavaScript は型チェックが非常に柔軟な言語である。
したがって、上の値も文脈に応じて次のように自動的に型変換される。

Boolean 文脈 Number 文脈 String 文脈
null false 0 “null”
undefined false NaN “undefined”
NaN false NaN “NaN”
Infinity true Infinity “Infinity”
typeof null;        // object
typeof undefined;   // undefined
typeof NaN;         // number
typeof Infinity;    // number

Boolean(null);      // false
Boolean(undefined); // false
Boolean(NaN);       // false
Boolean(Infinity);  // true

Number(null);       // 0
Number(undefined);  // NaN
Number(NaN);        // NaN
Number(Infinity);   // Infinity

String(null);       // null
String(undefined);  // undefined
String(NaN);        // NaN
String(Infinity);   // Infinity

Number オブジェクト

JavaScript で数値は通常、数値リテラルを使用して表現する。

しかし数を表すとき、new 演算子を使用して明示的に Number オブジェクトを作成することもできる。
この Number オブジェクトは、数値を包んでいるラッパー (wrapper) オブジェクトである。

var x = 100;             // 数値リテラル
var y = new Number(100); // Number オブジェクト
x;                       // 100
y;                       // 100
typeof x;                // number 型
typeof y;                // object 型

等価演算子 (==) は、リテラル値とオブジェクトの値が同じであれば true を返す。
しかし厳密等価演算子 (===) は、数値リテラルと Number オブジェクトの型が異なるため、常に false を返す。

var x = 100;             // 数値リテラル 100
var y = new Number(100); // Number オブジェクト 100
x == y;                  // 値が同じなので true
x === y;                 // 互いに異なるオブジェクトなので false

new 演算子を使用してオブジェクトを作成するときは、非常に多くの追加演算が必要になる。
したがって、できるだけ数値リテラルを使用して数を表現し、Number オブジェクトはラッパーオブジェクトとしてだけ活用するのがよい。